Run gitleaks, hadolint, shellcheck, actionlint, typos, semgrep, and trivy from one image.
420
Run gitleaks, hadolint, shellcheck, actionlint, typos, semgrep, and trivy from one image.
This image has the static checks that many repositories run in CI:
| Tool | It checks | It exits with an error when |
|---|---|---|
| gitleaks | Secrets in files and in the git history | it finds a secret |
| hadolint | Dockerfiles | it finds a problem |
| shellcheck | Shell scripts | it finds a problem |
| actionlint | GitHub Actions workflows, with shellcheck for run scripts | it finds a problem |
| typos | Spelling in code and documents | it finds a spelling mistake |
| semgrep | Code patterns from your rules | it finds a match and you set --error |
| trivy | Known CVEs in a file tree or a container image | it finds a CRITICAL CVE that has a fix and you set --exit-code 1 |
Each build installs the newest stable release of each tool. The build downloads gitleaks, hadolint, shellcheck, actionlint, typos, and trivy from their GitHub releases. It checks each file with the SHA-256 digest in the release metadata. The build installs Semgrep with pip in its own environment on the Debian Python. The build also stores the trivy vulnerability database. This database is large, so the image is much larger than the tool files alone.
The base image is debian:stable-slim. The image also has git, so gitleaks can read the
commit history. The image trusts all git directories (safe.directory). Without this
setting, git does not accept a mounted repository with a different owner, and gitleaks
scans 0 commits and passes. The working directory is /workspace. The default command
starts Bash.
| Tag | Meaning | Platform |
|---|---|---|
latest | Latest successful build from main with the newest stable tools. | linux/amd64 |
View all tags on Docker Hub.
The latest tag changes when a new build passes its tests.
docker pull stden/code-quality:latest
Run the commands from the root of your project. The commands mount the project at
/workspace. The container runs as your user, so the files that a tool writes belong to
you. Set a short alias:
alias cq='docker run --rm --user "$(id -u):$(id -g)" -v "$PWD":/workspace stden/code-quality:latest'
Each tool exits with a non-zero status when it finds a problem. A script or a CI job then stops.
cq gitleaks git --redact --no-banner --verbose .
cq gitleaks dir --redact --no-banner .
gitleaks git scans each commit. gitleaks dir scans the files, also the files that you
did not commit. --redact replaces the secret value with REDACTED in the output. The log
shows the number of scanned commits. If it shows 0 commits, the scan did not read the
history.
A secret that you delete in a new commit stays in the history. Revoke the secret. Then make
a new secret. To accept a known finding, add its Fingerprint line to .gitleaksignore.
To accept all current findings of an old repository, save a baseline once. Then compare
each scan with it:
cq gitleaks git --redact --no-banner --report-path gitleaks-baseline.json .
cq gitleaks git --redact --no-banner --baseline-path gitleaks-baseline.json .
cq hadolint Dockerfile
To ignore a rule in the project, write .hadolint.yaml:
ignored:
- DL3008
To ignore a rule for one instruction, write # hadolint ignore=DL3008 on the line before it.
cq bash -c 'git ls-files -z "*.sh" | xargs -0 --no-run-if-empty shellcheck'
To ignore a rule for one command, write # shellcheck disable=SC2086 on the line before
it. Put project settings in .shellcheckrc.
cq actionlint
actionlint reads all files in .github/workflows. It finds incorrect keys, incorrect
expressions, and jobs that do not exist. It also runs shellcheck on each run script.
cq typos
cq typos --write-changes
The second command corrects the files. Review the change with git diff. To skip files or
accept project words, write _typos.toml:
[files]
extend-exclude = ["*.min.js", "testdata/"]
[default.extend-words]
# Add a project word here as word = "word".
Write a rule file semgrep.yml:
rules:
- id: no-eval
languages: [python]
severity: ERROR
message: Do not pass data to eval().
pattern: eval(...)
cq semgrep scan --config semgrep.yml --metrics=off --error
--error makes semgrep exit with status 1 when it finds a match. Without it, semgrep
exits with status 0. With --metrics=off, semgrep does not send usage data. A local rule
file does not use the network. A registry rule set, for example --config p/default,
downloads its rules from semgrep.dev.
To skip a file, add it to .semgrepignore. To accept one line, write # nosemgrep: no-eval
on it.
Do not use Mend for this check. Mend must have a license, MEND_EMAIL, and MEND_USER_KEY.
Mend uploads the scan. trivy runs inside this image. The image disables telemetry and the
version check.
Scan a file tree. --exit-code 1 makes trivy exit with status 1 when it finds a match.
--ignore-unfixed skips a CVE that has no fix yet. --skip-db-update uses the database
from this image and does not use the network.
cq trivy fs --scanners vuln --severity CRITICAL --ignore-unfixed --exit-code 1 \
--skip-db-update --offline-scan .
Scan a local image. Save it first. trivy reads the tar. The Docker socket is not necessary.
docker save my-app:test -o /tmp/my-app.tar
docker run --rm -v /tmp/my-app.tar:/image.tar:ro stden/code-quality \
trivy image --input /image.tar --scanners vuln --severity CRITICAL \
--ignore-unfixed --exit-code 1 --skip-db-update --offline-scan
To accept one CVE, add its id to .trivyignore, one id on each line. Review that file as
you review code.
The job runs in the image. The image has git, so actions/checkout makes a real clone.
fetch-depth: 0 gets the full history for gitleaks.
name: Code quality
on:
push:
pull_request:
permissions:
contents: read
jobs:
checks:
runs-on: ubuntu-latest
container: stden/code-quality:latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- run: gitleaks git --redact --no-banner .
- run: hadolint Dockerfile
- run: git ls-files -z '*.sh' | xargs -0 --no-run-if-empty shellcheck
- run: actionlint
- run: typos
- run: semgrep scan --config semgrep.yml --metrics=off --error
- run: trivy fs --scanners vuln --severity CRITICAL --ignore-unfixed --exit-code 1 --skip-db-update --offline-scan .
GIT_DEPTH: 0 gets the full history for gitleaks.
code-quality:
image: stden/code-quality:latest
variables:
GIT_DEPTH: 0
script:
- gitleaks git --redact --no-banner .
- hadolint Dockerfile
- git ls-files -z '*.sh' | xargs -0 --no-run-if-empty shellcheck
- typos
- semgrep scan --config semgrep.yml --metrics=off --error
- trivy fs --scanners vuln --severity CRITICAL --ignore-unfixed --exit-code 1 --skip-db-update --offline-scan .
|| true, continue-on-error, or
allow_failure. Always give semgrep --error. Always give trivy --exit-code 1..gitleaksignore or the gitleaks
baseline, .hadolint.yaml, .shellcheckrc, _typos.toml, .semgrepignore, and
.trivyignore.
Review each change to them as you review code. Write the reason next to each inline
ignore comment.gitleaks --redact. CI logs stay for
a long time, and many people can read them. Do not upload a report without --redact
as a CI artifact.gitleaks git --redact --log-opts="origin/main..HEAD" ..--metrics=off. Registry rule sets change without a change in your repository.latest tag gets new tool versions, and a new version can
find new problems. To choose when this happens, pin the image digest. Update the digest
with Dependabot or Renovate.The smoke test works offline. It makes small sample files. It checks that each tool finds a known problem and accepts the clean file. The known problems are:
ADD.run script with an unquoted variable.needs entry for a job that does not exist.The test also checks that gitleaks does not print the secret, that a gitleaks baseline accepts a known finding, and that typos corrects a file.
Content type
Image
Digest
sha256:aa9b99fb5…
Size
384.2 MB
Last updated
1 day ago
docker pull stden/code-quality