feat: add support for checkov to lint iac files (#4925)

- Add support to run Checkov against infrastructure as code descriptors
  that are in a given (configurable) directory. Defaults to lint the
  whole workspace.
- Establish a baseline for our own codebase so we don't have to fix
  issues right away with this change.
This commit is contained in:
Marco Ferrari 2023-12-22 13:22:15 +01:00 committed by GitHub
parent 61d0c6992b
commit 9d7268fb99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 214 additions and 32 deletions

View file

@ -0,0 +1,5 @@
---
# Options reference: https://www.checkov.io/2.Basics/CLI%20Command%20Reference.html
quiet: false
...

27
.github/linters/.checkov.baseline vendored Normal file
View file

@ -0,0 +1,27 @@
{
"failed_checks": [
{
"file": "/dev-dependencies/Dockerfile",
"findings": [
{
"resource": "/dev-dependencies/Dockerfile.",
"check_ids": [
"CKV_DOCKER_2"
]
}
]
},
{
"file": "/Dockerfile",
"findings": [
{
"resource": "/Dockerfile.",
"check_ids": [
"CKV_DOCKER_2",
"CKV_DOCKER_3"
]
}
]
}
]
}

29
.github/linters/.checkov.yaml vendored Normal file
View file

@ -0,0 +1,29 @@
---
# Options reference: https://www.checkov.io/2.Basics/CLI%20Command%20Reference.html
# Establish a baseline so we don't have to fix these issues at the same time
# as we ship Checkov with super-linter.
baseline: .github/linters/.checkov.baseline
# Report skipped baseline checks in the output
output-baseline-as-skipped: true
# Don't report passed checks in output
quiet: true
# The tests directory contains files that we need for test cases that are
# expected to fail. Checkov would catch those issues, so we exclude the tests
# directory.
skip-path:
- test/linters/ansible
- test/linters/arm
- test/linters/checkov/bad
- test/linters/dockerfile_hadolint
- test/linters/jscpd
- test/linters/json
- test/linters/kubernetes_kubeconform
- test/linters/openapi
- test/linters/terraform_fmt
- test/linters/terraform_tflint
- test/linters/terraform_terrascan
...

View file

@ -5,6 +5,9 @@ on:
branches:
- main
# Don't grant any access by default
permissions: {}
jobs:
test:
name: Build and Test

View file

@ -5,6 +5,9 @@ on:
merge_group:
workflow_dispatch:
# Don't grant any access by default
permissions: {}
jobs:
test:
name: Build and Test
@ -118,6 +121,8 @@ jobs:
preview-release-notes:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:

View file

@ -6,9 +6,14 @@ on:
pull_request:
merge_group:
# Don't grant any access by default
permissions: {}
jobs:
commitlint:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:

View file

@ -1,9 +1,4 @@
---
###############################
###############################
## StaleBot for Super-Linter ##
###############################
###############################
on:
schedule:
# every day at 0:00 UTC
@ -11,18 +6,12 @@ on:
issue_comment:
types: [created, deleted, edited]
###################
# Name of the Job #
###################
name: "Stale[bot]"
###############
# Run the job #
###############
# Don't grant any access by default
permissions: {}
jobs:
#######################
# Mark an Issue Stale #
#######################
markstale:
permissions:
issues: write # for actions/stale to close stale issues
@ -45,10 +34,10 @@ jobs:
stale-pr-label: "O: stale 🤖"
exempt-pr-labels: "O: backlog 🤖"
##################
# Mark not stale #
##################
marknotstale:
permissions:
issues: write # for actions/stale to close stale issues
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
# do not run on schedule
if: "${{ github.event_name == 'issue_comment' && contains(github.event.issue.labels.*.name, 'O: stale 🤖') && github.event.issue.user.type != 'Bot' }}"

View file

@ -31,7 +31,7 @@ FROM yoheimuta/protolint:0.47.2 as protolint
FROM python:3.12.1-alpine3.19 as base_image
LABEL com.github.actions.name="Super-Linter" \
com.github.actions.description="A collection of code linters and analyzers." \
com.github.actions.description="Super-linter is a ready-to-run collection of linters and code analyzers, to help validate your source code." \
com.github.actions.icon="code" \
com.github.actions.color="red" \
maintainer="@Hanse00, @ferrarimarco, @zkoppert" \
@ -39,7 +39,6 @@ LABEL com.github.actions.name="Super-Linter" \
org.opencontainers.image.url="https://github.com/super-linter/super-linter" \
org.opencontainers.image.source="https://github.com/super-linter/super-linter" \
org.opencontainers.image.documentation="https://github.com/super-linter/super-linter" \
org.opencontainers.image.vendor="GitHub" \
org.opencontainers.image.description="A collection of code linters and analyzers."
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
@ -309,6 +308,7 @@ ENV IMAGE="slim"
ENV PATH="${PATH}:/venvs/ansible-lint/bin"
ENV PATH="${PATH}:/venvs/black/bin"
ENV PATH="${PATH}:/venvs/checkov/bin"
ENV PATH="${PATH}:/venvs/cfn-lint/bin"
ENV PATH="${PATH}:/venvs/cpplint/bin"
ENV PATH="${PATH}:/venvs/flake8/bin"

View file

@ -157,6 +157,7 @@ test-linters: ## Run the linters test suite
docker run \
-e ACTIONS_RUNNER_DEBUG=true \
-e ANSIBLE_DIRECTORY=test/linters/ansible \
-e CHECKOV_FILE_NAME=".checkov-test-linters.yaml" \
-e DEFAULT_BRANCH=main \
-e ENABLE_GITHUB_ACTIONS_GROUP_TITLE=true \
-e ERROR_ON_MISSING_EXEC_BIT=true \
@ -170,6 +171,8 @@ test-linters: ## Run the linters test suite
.phony: build-dev-container-image
build-dev-container-image: ## Build commit linter container image
DOCKER_BUILDKIT=1 docker buildx build --load \
--build-arg GID=$(shell id -g) \
--build-arg UID=$(shell id -u) \
-t ${DEV_CONTAINER_URL} "${CURDIR}/dev-dependencies"
.phony: lint-commits

View file

@ -1,8 +1,7 @@
# Super-Linter
This repository is for the **GitHub Action** to run a **Super-Linter**, a
ready-to-run collection of linters and code analyzers, to help validate your
source code.
Super-linter is a ready-to-run collection of linters and code analyzers, to
help validate your source code.
The goal of super-linter is to help you establish best practices and consistent
formatting across multiple programming languages, and ensure developers are
@ -46,6 +45,7 @@ Super-linter supports the following tools:
| **JavaScript** | [ESLint](https://eslint.org/) / [standard js](https://standardjs.com/) |
| **JSON** | [eslint-plugin-json](https://www.npmjs.com/package/eslint-plugin-json) |
| **JSONC** | [eslint-plugin-jsonc](https://www.npmjs.com/package/eslint-plugin-jsonc) |
| Infrastructure as code | [Checkov](https://www.checkov.io/) |
| **Kubernetes** | [kubeconform](https://github.com/yannh/kubeconform) |
| **Kotlin** | [ktlint](https://github.com/pinterest/ktlint) |
| **LaTeX** | [ChkTex](https://www.nongnu.org/chktex/) |
@ -155,6 +155,7 @@ You can configure super-linter using the following environment variables:
| **ANSIBLE_CONFIG_FILE** | `.ansible-lint.yml` | Filename for [Ansible-lint configuration](https://ansible.readthedocs.io/projects/lint/configuring/) (ex: `.ansible-lint`, `.ansible-lint.yml`) |
| **ANSIBLE_DIRECTORY** | `/ansible` | Flag to set the root directory for Ansible file location(s), relative to `DEFAULT_WORKSPACE`. Set to `.` to use the top-level of the `DEFAULT_WORKSPACE`. |
| **BASH_SEVERITY** | `style` | Specify the minimum severity of errors to consider in shellcheck. Valid values in order of severity are error, warning, info and style. |
| **CHECKOV_FILE_NAME** | `.checkov.yaml` | Configuration filename for Checkov. |
| **CREATE_LOG_FILE** | `false` | If set to `true`, it creates the log file. You can set the log filename using the `LOG_FILE` environment variable. This overrides any existing log files. |
| **CSS_FILE_NAME** | `.stylelintrc.json` | Filename for [Stylelint configuration](https://github.com/stylelint/stylelint) (ex: `.stylelintrc.yml`, `.stylelintrc.yaml`) |
| **DEFAULT_BRANCH** | `master` | The name of the repository default branch. |
@ -195,7 +196,7 @@ You can configure super-linter using the following environment variables:
| **PYTHON_ISORT_CONFIG_FILE** | `.isort.cfg` | Filename for [isort configuration](https://pycqa.github.io/isort/docs/configuration/config_files.html) (ex: `.isort.cfg`, `pyproject.toml`) |
| **PYTHON_MYPY_CONFIG_FILE** | `.mypy.ini` | Filename for [mypy configuration](https://mypy.readthedocs.io/en/stable/config_file.html) (ex: `.mypy.ini`, `setup.config`) |
| **PYTHON_PYLINT_CONFIG_FILE** | `.python-lint` | Filename for [pylint configuration](https://pylint.pycqa.org/en/latest/user_guide/run.html?highlight=rcfile#command-line-options) (ex: `.python-lint`, `.pylintrc`) |
| **RENOVATE_SHAREABLE_CONFIG_PRESET_FILE_NAMES** | `` | Comma-separated filenames for [renovate shareable config preset](https://docs.renovatebot.com/config-presets/) (ex: `default.json`) |
| **RENOVATE_SHAREABLE_CONFIG_PRESET_FILE_NAMES** | not set | Comma-separated filenames for [renovate shareable config preset](https://docs.renovatebot.com/config-presets/) (ex: `default.json`) |
| **RUBY_CONFIG_FILE** | `.ruby-lint.yml` | Filename for [rubocop configuration](https://docs.rubocop.org/rubocop/configuration.html) (ex: `.ruby-lint.yml`, `.rubocop.yml`) |
| **SCALAFMT_CONFIG_FILE** | `.scalafmt.conf` | Filename for [scalafmt configuration](https://scalameta.org/scalafmt/docs/configuration.html) (ex: `.scalafmt.conf`) |
| **SNAKEMAKE_SNAKEFMT_CONFIG_FILE** | `.snakefmt.toml` | Filename for [Snakemake configuration](https://github.com/snakemake/snakefmt#configuration) (ex: `pyproject.toml`, `.snakefmt.toml`) |
@ -220,6 +221,7 @@ You can configure super-linter using the following environment variables:
| **VALIDATE_BASH** | `true` | Flag to enable or disable the linting process of the Bash language. |
| **VALIDATE_BASH_EXEC** | `true` | Flag to enable or disable the linting process of the Bash language to validate if file is stored as executable. |
| **VALIDATE_CPP** | `true` | Flag to enable or disable the linting process of the C++ language. |
| **VALIDATE_CHECKOV** | `true` | Flag to enable or disable the linting process with Checkov |
| **VALIDATE_CLANG_FORMAT** | `true` | Flag to enable or disable the linting process of the C++/C language with clang-format. |
| **VALIDATE_CLOJURE** | `true` | Flag to enable or disable the linting process of the Clojure language. |
| **VALIDATE_CLOUDFORMATION** | `true` | Flag to enable or disable the linting process of the AWS Cloud Formation language. |

View file

@ -1,7 +1,7 @@
---
name: 'Super-Linter'
author: 'GitHub'
description: 'It is a simple combination of various linters, written in bash, to help validate your source code.'
author: 'Super-linter contributors'
description: 'Super-linter is a ready-to-run collection of linters and code analyzers, to help validate your source code.'
runs:
using: 'docker'
image: 'docker://ghcr.io/super-linter/super-linter:v5.7.2' # x-release-please-version

1
dependencies/python/checkov.txt vendored Normal file
View file

@ -0,0 +1 @@
checkov==3.1.38

View file

@ -18,6 +18,15 @@ RUN jq '.dependencies | to_entries[] | select(.key | startswith("@commitlint/"))
&& xargs npm install -g < "${NPM_PACKAGES_FILE_PATH}" \
&& rm package.json "${NPM_PACKAGES_FILE_PATH}"
# Split this from the previous RUN instruction so we can cache the costly installation step
# hadolint ignore=DL3059
RUN commitlint --version \
&& release-please --version \
&& git config --global --add safe.directory /source-repository
ARG USERNAME=super-linter-dev
ARG UID=1000
ARG GID=1000
RUN groupadd -g ${GID} -o "${USERNAME}" \
&& useradd -m -u ${UID} -g ${GID} -o -s /bin/bash -l "${USERNAME}"
USER $UNAME

View file

@ -169,6 +169,20 @@ function BuildFileList() {
debug "ANSIBLE_DIRECTORY (${ANSIBLE_DIRECTORY}) does NOT exist."
fi
if CheckovConfigurationFileContainsDirectoryOption "${CHECKOV_LINTER_RULES}"; then
debug "No need to configure the directories to check for Checkov."
else
debug "Checking if we are in test mode before configuring the list of directories to lint with Checkov"
if [ "${TEST_CASE_RUN}" == "true" ]; then
debug "We are running in test mode. Adding test case directories to the list of directories to analyze with Checkov."
FILE_ARRAY_CHECKOV+=("${DEFAULT_CHECKOV_TEST_CASE_DIRECTORY}/bad")
FILE_ARRAY_CHECKOV+=("${DEFAULT_CHECKOV_TEST_CASE_DIRECTORY}/good")
else
debug "We are not running in test mode (${TEST_CASE_RUN}). Adding ${GITHUB_WORKSPACE} to the list of directories to analyze with Checkov."
FILE_ARRAY_CHECKOV+=("${GITHUB_WORKSPACE}")
fi
fi
################################################
# Iterate through the array of all files found #
################################################

View file

@ -261,3 +261,17 @@ function ValidateDefaultGitBranch() {
debug "The default branch (${DEFAULT_BRANCH}) exists in this repository"
fi
}
function CheckovConfigurationFileContainsDirectoryOption() {
local CHECKOV_LINTER_RULES_PATH="${1}"
local CONFIGURATION_OPTION_KEY="directory:"
debug "Checking if ${CHECKOV_LINTER_RULES_PATH} contains a '${CONFIGURATION_OPTION_KEY}' configuration option"
if grep -q "${CONFIGURATION_OPTION_KEY}" "${CHECKOV_LINTER_RULES_PATH}"; then
debug "${CHECKOV_LINTER_RULES_PATH} contains a '${CONFIGURATION_OPTION_KEY}' statement"
return 0
else
debug "${CHECKOV_LINTER_RULES_PATH} doesn't contain a '${CONFIGURATION_OPTION_KEY}' statement"
return 1
fi
}

View file

@ -137,6 +137,7 @@ debug "TFLINT_LOG: ${TFLINT_LOG}"
ANSIBLE_FILE_NAME="${ANSIBLE_CONFIG_FILE:-.ansible-lint.yml}"
# shellcheck disable=SC2034 # Variable is referenced indirectly
ARM_FILE_NAME=".arm-ttk.psd1"
CHECKOV_FILE_NAME="${CHECKOV_FILE_NAME:-".checkov.yaml"}"
# shellcheck disable=SC2034 # Variable is referenced indirectly
CLOJURE_FILE_NAME=".clj-kondo/config.edn"
# shellcheck disable=SC2034 # Variable is referenced indirectly
@ -293,7 +294,7 @@ fi
##################
# Language array #
##################
LANGUAGE_ARRAY=('ANSIBLE' 'ARM' 'BASH' 'BASH_EXEC' 'CLANG_FORMAT'
LANGUAGE_ARRAY=('ANSIBLE' 'ARM' 'BASH' 'BASH_EXEC' 'CHECKOV' 'CLANG_FORMAT'
'CLOUDFORMATION' 'CLOJURE' 'COFFEESCRIPT' 'CPP' 'CSHARP' 'CSS' 'DART'
'DOCKERFILE_HADOLINT' 'EDITORCONFIG' 'ENV' 'GITHUB_ACTIONS'
'GITLEAKS' 'GHERKIN' 'GO' 'GO_MODULES' 'GOOGLE_JAVA_FORMAT' 'GROOVY' 'HTML' 'JAVA'
@ -315,6 +316,7 @@ LINTER_NAMES_ARRAY['ANSIBLE']="ansible-lint"
LINTER_NAMES_ARRAY['ARM']="arm-ttk"
LINTER_NAMES_ARRAY['BASH']="shellcheck"
LINTER_NAMES_ARRAY['BASH_EXEC']="bash-exec"
LINTER_NAMES_ARRAY['CHECKOV']="checkov"
LINTER_NAMES_ARRAY['CLANG_FORMAT']="clang-format"
LINTER_NAMES_ARRAY['CLOJURE']="clj-kondo"
LINTER_NAMES_ARRAY['CLOUDFORMATION']="cfn-lint"
@ -871,10 +873,16 @@ ConfigureGitSafeDirectories
########################################################
# Initialize variables that depend on GitHub variables #
########################################################
DEFAULT_ANSIBLE_DIRECTORY="${GITHUB_WORKSPACE}/ansible" # Default Ansible Directory
export DEFAULT_ANSIBLE_DIRECTORY # Workaround SC2034
DEFAULT_TEST_CASE_ANSIBLE_DIRECTORY="${GITHUB_WORKSPACE}/${TEST_CASE_FOLDER}/ansible" # Default Ansible directory when running test cases
export DEFAULT_TEST_CASE_ANSIBLE_DIRECTORY # Workaround SC2034
# shellcheck disable=SC2034 # Variable is referenced indirectly
DEFAULT_ANSIBLE_DIRECTORY="${GITHUB_WORKSPACE}/ansible"
debug "DEFAULT_ANSIBLE_DIRECTORY: ${DEFAULT_ANSIBLE_DIRECTORY}"
# shellcheck disable=SC2034 # Variable is referenced indirectly
DEFAULT_TEST_CASE_ANSIBLE_DIRECTORY="${GITHUB_WORKSPACE}/${TEST_CASE_FOLDER}/ansible"
debug "DEFAULT_TEST_CASE_ANSIBLE_DIRECTORY: ${DEFAULT_TEST_CASE_ANSIBLE_DIRECTORY}"
# shellcheck disable=SC2034 # Variable is referenced indirectly
DEFAULT_CHECKOV_TEST_CASE_DIRECTORY="${GITHUB_WORKSPACE}/${TEST_CASE_FOLDER}/checkov"
debug "DEFAULT_CHECKOV_TEST_CASE_DIRECTORY: ${DEFAULT_CHECKOV_TEST_CASE_DIRECTORY}"
TYPESCRIPT_STANDARD_TSCONFIG_FILE="${GITHUB_WORKSPACE}/${TYPESCRIPT_STANDARD_TSCONFIG_FILE:-"tsconfig.json"}"
debug "TYPESCRIPT_STANDARD_TSCONFIG_FILE: ${TYPESCRIPT_STANDARD_TSCONFIG_FILE}"
@ -923,6 +931,15 @@ else
LINTER_COMMANDS_ARRAY['BASH']="shellcheck --color --external-sources --severity=${BASH_SEVERITY}"
fi
LINTER_COMMANDS_ARRAY['BASH_EXEC']="bash-exec"
LINTER_COMMANDS_ARRAY['CHECKOV']="checkov --config-file ${CHECKOV_LINTER_RULES}"
if CheckovConfigurationFileContainsDirectoryOption "${CHECKOV_LINTER_RULES}"; then
debug "No need to update the Checkov command."
else
debug "Adding the '--directory' option to the Checkov command."
LINTER_COMMANDS_ARRAY['CHECKOV']="${LINTER_COMMANDS_ARRAY['CHECKOV']} --directory"
fi
LINTER_COMMANDS_ARRAY['CLANG_FORMAT']="clang-format --Werror --dry-run"
LINTER_COMMANDS_ARRAY['CLOJURE']="clj-kondo --config ${CLOJURE_LINTER_RULES} --lint"
LINTER_COMMANDS_ARRAY['CLOUDFORMATION']="cfn-lint --config-file ${CLOUDFORMATION_LINTER_RULES}"

View file

@ -1,7 +1,7 @@
---
name: 'Super-Linter slim'
author: 'GitHub'
description: 'It is a simple combination of various linters, written in bash, to help validate your source code.'
author: 'Super-linter contributors'
description: 'Super-linter is a ready-to-run collection of linters and code analyzers, to help validate your source code.'
runs:
using: 'docker'
image: 'docker://ghcr.io/super-linter/super-linter:slim-v5.7.2' # x-release-please-version

View file

@ -119,6 +119,7 @@ control "super-linter-installed-commands" do
{ linter_name: "bash-exec", expected_exit_status: 1}, # expect a return code = 1 because this linter doesn't support a "get linter version" command
{ linter_name: "black"},
{ linter_name: "cfn-lint"},
{ linter_name: "checkov"},
{ linter_name: "checkstyle", version_command: "java -jar /usr/bin/checkstyle --version"},
{ linter_name: "chktex"},
{ linter_name: "clang-format"},
@ -374,6 +375,7 @@ control "super-linter-installed-pypi-packages" do
"ansible-lint",
"black",
"cfn-lint",
"checkov",
"cpplint",
"flake8",
"isort",

View file

@ -0,0 +1,25 @@
resource "aws_instance" "bad" {
ami = "ami-0ff8a91507f77f867"
instance_type = "t2.small"
associate_public_ip_address = false
vpc_security_group_ids = ["sg-12345678901234567"]
metadata_options {
http_endpoint = "disabled"
}
ebs_block_device {
device_name = "name"
encrypted = true
}
}
terraform {
required_version = ">=1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}

View file

@ -0,0 +1,32 @@
resource "aws_instance" "good" {
ami = "ami-0ff8a91507f77f867"
associate_public_ip_address = false
ebs_optimized = true
iam_instance_profile = "test"
instance_type = "t2.small"
monitoring = true
vpc_security_group_ids = ["sg-12345678901234567"]
metadata_options {
http_endpoint = "disabled"
}
ebs_block_device {
device_name = "name"
encrypted = true
}
root_block_device {
encrypted = true
}
}
terraform {
required_version = ">=1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}