C static analysis Docker image — CppCheck, Clang-Tidy, Clang-Format, Lizard, REUSE and Doxygen.
10K+
A ready-to-use Docker image bundling several widely-used linters and documentation tools for C/C++ projects. Use it to run static analysis, format checks, complexity measurement, license compliance checks, and documentation generation — either as one-off local commands or as steps in a CI pipeline — without installing or version-managing each tool yourself.
Github: gilleshenrard/docker-c-linters
DockerHub: gilleshenrard/docker-c-linters/general
Maintainer note: Tool versions are hardcoded in this README. Remember to update this table whenever versions are bumped in the Dockerfile.
| Tool | Version | Purpose |
|---|---|---|
| CppCheck | 2.22.0 | Static analysis |
| Clang-Format | 22.1.8 | Code formatting |
| Clang-Tidy | 22.1.8 | Linting and static analysis |
| run-clang-tidy | 22.1.8 | Parallel Clang-Tidy runner |
| Lizard | 1.24.0 | Cyclomatic complexity measurement |
| REUSE | 6.2.0 | SPDX license compliance checker |
| Doxygen | 1.18.0 | Documentation generation |
The image is hosted on Docker Hub and can be pulled with:
docker pull gilleshenrard/c-linters:latest
# Static analysis
docker run --rm -v "$(pwd)":/src c-linters:latest cppcheck --enable=all /src
docker run --rm -v "$(pwd)":/src c-linters:latest run-clang-tidy -p build/
# Format check
docker run --rm -v "$(pwd)":/src c-linters:latest clang-format --dry-run --Werror /src/main.c
# Complexity report
docker run --rm -v "$(pwd)":/src c-linters:latest lizard /src
# License compliance
docker run --rm -v "$(pwd)":/src c-linters:latest reuse lint
docker run --rm -it -v "$(pwd)":/src c-linters:latest bash
jobs:
lint:
runs-on: ubuntu-latest
container:
image: gilleshenrard/c-linters:latest
steps:
- uses: actions/checkout@v7
- name: Static analysis (CppCheck)
run: cppcheck --enable=all --error-exitcode=1 src/
- name: Format check (Clang-Format)
run: |
find src/ -name '*.c' -o -name '*.h' | \
xargs clang-format --dry-run --Werror
- name: Linting (Clang-Tidy)
run: run-clang-tidy -p build/
- name: Complexity (Lizard)
run: lizard src/ --CCN 10 --length 50 --arguments 5
- name: License compliance (REUSE)
run: reuse lint
- name: Documentation (Doxygen)
run: doxygen Doxyfile
docker build -t c-linters:latest .
All versions are declared as ARG at the top of the Dockerfile and can be overridden
at build time:
docker build \
--build-arg CPPCHECKVERSION="2.21.0" \
--build-arg CLANGVERSION="22.2.0" \
--build-arg LIZARDVERSION="1.23.1" \
--build-arg REUSEVERSION="6.3.0" \
--build-arg DOXYGENVERSION="1.18.0" \
-t c-linters:latest .
Vulnerabilities can be checked using Docker Scout.
Note: Scout caches SBOMs locally, keyed by image digest. When re-scanning a freshly rebuilt image under the same tag, clear the cache first to avoid stale results.
# Clear Scout's local SBOM cache (non-interactive)
docker scout cache prune --sboms --force
# Generate a CVE report in Markdown format
docker scout cves c-linters:latest --format markdown --output <image_name>_report.md
Warning
**Residual vulnerabilities are expected and are not fixable from this image.** The overwhelming majority of CVEs flagged by Docker Scout come from OS-level packages that are part of the `debian:trixie-slim` base image itself, not from anything installed by this Dockerfile. These vulnerabilities remain until Debian's security team ships a patch upstream; no build-arg override, `apt` flag, or workaround in this repository can resolve them. Rebuilding the image regularly will pick up upstream fixes as they become available, but a completely clean Scout report should **not** be expected as a baseline for this image.In practice, the security impact of this is limited: this image is meant to be run as a one-off tool (
docker run --rm ...) rather than as a long-lived service, which significantly reduces the exposure window and attack surface these base-image CVEs would otherwise represent.
To find out which installed package pulled in a given vulnerable dependency (e.g. a package named in a Scout report), run the following against the built image, not a fresh base image (dependency resolution can otherwise differ from what was actually installed):
# 1. Scout's report may name a bare library (e.g. "tiff"); find the actual installed
# Debian package name, since it is often versioned or prefixed differently (e.g.
# "libtiff6")
docker run --rm c-linters:latest bash -c "dpkg -l | grep -i <package>"
# 2. Once you have the real package name, trace which installed package depends on it
docker run --rm c-linters:latest bash -c \
"apt-get update -qq && apt-get install -y aptitude >/dev/null 2>&1 && aptitude why <real_package_name>"
The image uses a two-stage build to keep the final image lean:
+-----------------------------------------------+
| Stage 1 : build (based on debian:trixie-slim) |
| |
| - Build CppCheck from source |
| - Extract Clang binaries from LLVM release |
| - Install Doxygen from its Github repo |
| - Install Lizard + REUSE in Python venv |
+------------------------|----------------------+
| COPY /opt
+------------------------|----------------------+
| Stage 2 : run (based on debian:trixie-slim) |
| |
| - Minimal packages needed to run the tools |
| - Additional tools: git, graphviz |
| - Append tool paths to PATH |
+-----------------------------------------------+
No build toolchain (cmake, ninja, wget, gcc…) is present in the final image.
| Path | Contents |
|---|---|
/opt/cppcheck/bin/ | cppcheck binary |
/opt/clang/bin/ | clang-format, clang-tidy, run-clang-tidy |
/opt/clang/lib/clang/22/ | Clang built-in headers |
/opt/pip-packages/bin/ | lizard, reuse |
/opt/doxygen/bin/ | doxygen |
All of the above are appended to PATH, so every tool is callable directly by name.
Image tags and GitHub releases follow Semantic Versioning
(vMAJOR.MINOR.PATCH):
MAJOR — a tool was added or removed, or the image's architecture changed in a way that can break existing usage.
MINOR — a pinned tool version was bumped (e.g. a newer Clang-Tidy). The tool is still invoked the same way, but its behavior may have evolved (for example, new warnings may fire that didn't before).
PATCH — a bug fix, or an automated rebuild that picked up an upstream security fix in the base image. Patch releases may occur with no visible change to this repository's source, since they can be triggered solely by Debian shipping a fix for a previously unfixable CVE.
This Dockerfile is distributed under the MIT License.
© 2026 Gilles Henrard [email protected]
Content type
Image
Digest
sha256:74e63bad7…
Size
248.1 MB
Last updated
3 days ago
docker pull gilleshenrard/c-linters