How to Integrate Static Code Analysis into CI / CD Workflows

Nick Miller
6 min readApr 3, 2023

--

All the cool kids are generating code with LLMs. They’re writing Bash scripts, server-side NodeJS, and API endpoints. It’s a wonderful boon to productivity. And a lot of it ends up in your production environments.

If that last sentence didn’t scare you, it should. These LLMs make statistical predictions about characters without regard for your unique environment or company security requirements. So what should you do?

You’re developers are not not going to use ChatGPT (and if they say they will, they’re lying to appease you). That means you need to start scanning their IaC templates and application code and ensuring the code snippets they’re generating remain compliant. One of the open-source tools you can use to maintain your security posture is Checkov.

What is Checkov?

Checkov is an open-source tool used for static code analysis and policy enforcement for cloud infrastructure. It is designed to detect potential misconfigurations in cloud infrastructure, including infrastructure as code (IaC) files. Checkov can scan infrastructure code written in various languages such as Terraform, CloudFormation, Kubernetes, ARM, and others. It supports multiple cloud providers such as AWS, GCP, and Azure.

Checkov uses a set of pre-defined policies that can be customized or extended to fit an organization’s specific needs. The policies are written in a simple declarative language, making it easy for developers and DevOps teams to write, understand, and maintain policies.

Checkov can be integrated into a CI/CD pipeline to detect potential issues early in the development process and prevent them from reaching production. It can also be used as part of a continuous compliance monitoring strategy to ensure that infrastructure remains compliant with the organization’s policies over time.

Testing Checkov

For our example today, we’re going to create a GitHub Action that performs a Checkov scan every time code is pushed to the repo. GitHub Actions is a powerful tool that automates software development workflows right from within GitHub. It allows users to define custom workflows, that automatically build, test, and deploy their code.

Pre-Requisites for this Project

  • Intermediate Understanding of GitHub
  • A GitHub Account
  • Basic Understanding of Terraform

Create the Github Repo

Navigate to your GitHub account:

Copy that URL under the Code button.

Go into your IDE of choice and clone the project into your local directory:

git clone <repo url>

Setting Up the GitHub Action

Our initial directory setup:

checkov-demo/
├── .git
│ └── ....
├── .github
│ └── workflows
│ └── checkov.yml
└── .gitignore

Importantly, you’ll notice a workflow folder containing a checkov.yml within the hidden .github folder. This is where we will define the Github Action.

Once the workflow file is in place, GitHub will automatically detect it and make it available as a workflow in your repository. When you push new changes to the repository, GitHub will automatically trigger the workflow according to the event and branch filters specified in the workflow file.

You can set up the directories with by copying these commands

mkdir .github
mkdir .github/workflows
touch .github/workflows/checkov.yml

Then within the checkov.yml, past in the code for the GitHub Actions workflow that uses the Checkov tool:

---
name: Checkov
on:
push:
branches:
- main
jobs:
build:

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v1
with:
python-version: 3.8
- name: Test with Checkov
id: checkov
uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: terraform

This workflow provides a convenient way to automate Terraform code testing for security and compliance issues, ensuring that any issues are caught early in the development process.

The workflow is triggered on every push to the main branch and runs on an ubuntu-latest runner. It comprises three steps:

  1. actions/checkout@v2, which checks out the latest version of the repository code onto the runner
  2. actions/setup-python@v1, which sets up Python 3.8 on the runner;
  3. bridgecrewio/checkov-action@master, which runs the Checkov tool.

The directory field in the Checkov step specifies that the tool should be run on the current directory. The framework field specifies that it should be run on Terraform files. The id: checkov line specifies an ID for the step, which can be used later in the workflow to reference the output of the Checkov tool.

Next, commit this to your repo:

#Stage changes in your local repo
git add .

#Check that your only adding the files you intend to commit
git status

#Commit and add your own commit message
git commit -m "Adding GitHub Action"

#Push to GitHub (you will likely need to authenticate after this command)
git push

Check in the GitHub directory to see that it’s been committed correctly:

Then navigate to the Actions tab to verify that that the Action has been created during the commit:

Testing the GitHub Action with Terraform

Now I’m going to commit a Terraform template that, if applied, would create an S3 bucket with a Policy that allows anyone on the Internet to access it. This will fail the Checkov compliance checks.

Create a main.tf in the main directory and paste in the IaC template for the S3 bucket:

# Set the AWS provider region to us-east-1
provider "aws" {
region = "us-east-1"
}

# Define an AWS S3 bucket resource named "example"
resource "aws_s3_bucket" "example" {
bucket = "checkov-example-bucket-29"

# Configure the S3 bucket as a static website
website {
index_document = "index.html"
error_document = "error.html"
}
}

# Define a bucket policy for the S3 bucket
resource "aws_s3_bucket_policy" "example" {
# Reference the S3 bucket created above by its ID
bucket = aws_s3_bucket.example.id

# Define the policy as a JSON string
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "${aws_s3_bucket.example.arn}/*"
}
]
}
POLICY
}

# Define an output named "bucket_name" with the value of the S3 bucket ID
output "bucket_name" {
value = aws_s3_bucket.example.id
}

Your local directory should now look like this:

checkov-article/
├── .git
├── .github
│ └── workflows
│ └── checkov.yml
├── .gitignore
└── main.tf

Now time to commit your code to the repo:

git add .
git status
git commit -m "Add Terraform Template"
git push

Wait a few minutes and then go into your GitHub directory. If the Action has finished running, you’ll notice the small red ‘x’ by the commit:

We’ll find more details in the Actions tab:

If you click into the workflow run, you’ll see the errors flagging potential misconfigurations in the Terraform template:

This output about the misconfigurations can be accessed in various formats such as plain text, JSON, or JUnit XML, depending on how Checkov is configured. Once Checkov flags an issue, users must review the output and determine if it’s a valid misconfiguration. The next step is prioritizing issues based on severity level and impact on infrastructure. Then, users must remediate the issue by updating infrastructure code or modifying configurations. After making changes, Checkov should be rerun to ensure successful remediation.

Users can integrate Checkov into their CI/CD pipeline to automatically detect potential misconfigurations. Using Checkov’s output, users can ensure that their cloud infrastructure remains secure and compliant with their organization’s policies.

--

--