{"id":48969315,"url":"https://github.com/tmatens/compose-lint","last_synced_at":"2026-04-26T05:04:46.930Z","repository":{"id":350567209,"uuid":"1202463834","full_name":"tmatens/compose-lint","owner":"tmatens","description":"Security-focused linter for Docker Compose files. Catches dangerous misconfigurations before they reach production. Grounded in OWASP and CIS Docker Benchmark.","archived":false,"fork":false,"pushed_at":"2026-04-18T05:47:38.000Z","size":312,"stargazers_count":1,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-18T06:27:47.811Z","etag":null,"topics":["cis-benchmark","code-quality","compose","container-security","devops","devsecops","docker","docker-compose","github-actions","hardening","iac-security","linter","owasp","pre-commit","python","security","security-scanner","security-tools","static-analysis","yaml"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tmatens.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-06T03:45:55.000Z","updated_at":"2026-04-18T05:39:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tmatens/compose-lint","commit_stats":null,"previous_names":["tmatens/compose-lint"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/tmatens/compose-lint","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tmatens%2Fcompose-lint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tmatens%2Fcompose-lint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tmatens%2Fcompose-lint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tmatens%2Fcompose-lint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tmatens","download_url":"https://codeload.github.com/tmatens/compose-lint/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tmatens%2Fcompose-lint/sbom","scorecard":{"id":1245895,"data":{"date":"2026-04-10T23:39:33Z","repo":{"name":"github.com/tmatens/compose-lint","commit":"7fe2d07545d1ecd947aae3547d7fea50f06f9a28"},"scorecard":{"version":"v5.3.0","commit":"c22063e786c11f9dd714d777a687ff7c4599b600"},"score":6.5,"checks":[{"name":"Maintained","score":0,"reason":"project was created within the last 90 days. Please review its contents carefully","details":["Warn: Repository was created within the last 90 days."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#maintained"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dependency-update-tool"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:54: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:57: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:68: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:71: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/publish.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/publish.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/publish.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:55: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/publish.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:59: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/publish.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:77: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/publish.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:81: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/publish.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/scorecard.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/scorecard.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/scorecard.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/scorecard.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/tmatens/compose-lint/scorecard.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:30","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:44","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:61","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:79","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:80","Warn: pipCommand not pinned by hash: .github/workflows/publish.yml:24","Warn: pipCommand not pinned by hash: .github/workflows/publish.yml:27","Info:   0 out of  19 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned","Info:   0 out of   7 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#pinned-dependencies"}},{"name":"Code-Review","score":0,"reason":"Found 0/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#code-review"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#security-policy"}},{"name":"Token-Permissions","score":10,"reason":"GitHub workflow tokens follow principle of least privilege","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:24","Info: topLevel 'contents' permission set to 'read': .github/workflows/ci.yml:16","Info: topLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:13","Info: topLevel 'contents' permission set to 'read': .github/workflows/publish.yml:10","Info: topLevel permissions set to 'read-all': .github/workflows/scorecard.yml:10","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#token-permissions"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#vulnerabilities"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#cii-best-practices"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: SAST configuration detected: CodeQL","Info: all commits (1) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#sast"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":3,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Warn: could not determine whether codeowners review is allowed","Info: 'up-to-date branches' is required to merge on branch 'main'","Info: status check found to merge onto on branch 'main'","Warn: PRs are not required to make changes on branch 'main'; or we don't have data to detect it.If you think it might be the latter, make sure to run Scorecard with a PAT or use Repo Rules (that are always public) instead of Branch Protection settings"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#signed-releases"}},{"name":"CI-Tests","score":10,"reason":"1 out of 1 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#ci-tests"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/publish.yml:64"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#packaging"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#license"}},{"name":"Contributors","score":3,"reason":"project has 1 contributing companies or organizations -- score normalized to 3","details":["Info: found contributions from: capital one"],"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#contributors"}}]},"last_synced_at":"2026-04-11T01:20:41.694Z","repository_id":350567209,"created_at":"2026-04-11T01:20:41.694Z","updated_at":"2026-04-11T01:20:41.694Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32165201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-23T02:19:40.750Z","status":"ssl_error","status_checked_at":"2026-04-23T02:17:55.737Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cis-benchmark","code-quality","compose","container-security","devops","devsecops","docker","docker-compose","github-actions","hardening","iac-security","linter","owasp","pre-commit","python","security","security-scanner","security-tools","static-analysis","yaml"],"created_at":"2026-04-18T06:17:28.219Z","updated_at":"2026-04-23T04:01:17.483Z","avatar_url":"https://github.com/tmatens.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# compose-lint\n\n[![CI](https://github.com/tmatens/compose-lint/actions/workflows/ci.yml/badge.svg)](https://github.com/tmatens/compose-lint/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/compose-lint)](https://pypi.org/project/compose-lint/)\n[![Docker](https://img.shields.io/badge/docker-composelint%2Fcompose--lint-2496ED?logo=docker\u0026logoColor=white)](https://hub.docker.com/r/composelint/compose-lint)\n[![Python](https://img.shields.io/pypi/pyversions/compose-lint)](https://pypi.org/project/compose-lint/)\n[![License](https://img.shields.io/github/license/tmatens/compose-lint)](https://github.com/tmatens/compose-lint/blob/main/LICENSE)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/tmatens/compose-lint/badge)](https://scorecard.dev/viewer/?uri=github.com/tmatens/compose-lint)\n[![OpenSSF Baseline 2](https://www.bestpractices.dev/projects/12472/baseline)](https://www.bestpractices.dev/projects/12472)\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/12472/badge)](https://www.bestpractices.dev/projects/12472)\n\nA security-focused linter for Docker Compose files. Catches dangerous misconfigurations before they reach production.\n\ncompose-lint targets the same niche [Hadolint](https://github.com/hadolint/hadolint) occupies for Dockerfiles: zero-config, opinionated, fast, and grounded in [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html) and [CIS](https://www.cisecurity.org/benchmark/docker) standards.\n\n## Installation\n\n**pip**\n\n```bash\npip install compose-lint\n```\n\n**Docker** — [composelint/compose-lint](https://hub.docker.com/r/composelint/compose-lint)\n\n```bash\ndocker run --rm -v \"$(pwd):/src\" composelint/compose-lint\n```\n\nThe image runs on [Google's distroless Python](https://github.com/GoogleContainerTools/distroless) (Debian 13, Python 3.13): no shell, no package manager, no `apt`, no `pip` at runtime. The entrypoint runs as nonroot (UID 65532). Only the Python interpreter, PyYAML, and `compose_lint` itself live in the final image — pip and the `dist-info` metadata are stripped from the venv after the build stage so Python-ecosystem CVEs don't surface on an unreachable binary. Multi-arch (linux/amd64 + linux/arm64), SHA-pinned base images bumped by Renovate, SLSA build provenance and Sigstore attestations published with every release. See [ADR-009](https://github.com/tmatens/compose-lint/blob/main/docs/adr/009-runtime-base-image.md).\n\n## Quick Start\n\nRun without arguments to auto-detect `compose.yml`, `compose.yaml`, `docker-compose.yml`, or `docker-compose.yaml` in the current directory:\n\n```bash\ncompose-lint\n```\n\nOr pass files explicitly:\n\n```bash\ncompose-lint docker-compose.yml docker-compose.prod.yml\n```\n\nDocker equivalent:\n\n```bash\ndocker run --rm -v \"$(pwd):/src\" composelint/compose-lint docker-compose.prod.yml\n```\n\n## Example Output\n\nGiven this `docker-compose.yml`:\n\n```yaml\nservices:\n  traefik:\n    image: traefik:v3.0@sha256:aaaabbbbccccddddeeeeffff00001111222233334444555566667777888899990\n    read_only: true\n    cap_drop: [ALL]\n    security_opt:\n      - no-new-privileges:true\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock\n    ports:\n      - \"8080:80\"\n```\n\nand this `.compose-lint.yml` (suppressing CL-0001 for `traefik` with a tracked reason):\n\n```yaml\nrules:\n  CL-0001:\n    exclude_services:\n      traefik: \"SEC-1234 approved — socket proxy planned for 2026-Q3\"\n```\n\nrunning `compose-lint docker-compose.yml` produces:\n\n```\ncompose-lint 0.3.7\nfiles: docker-compose.yml  ·  config: .compose-lint.yml  ·  fail-on: high\n\ndocker-compose.yml:8  SUPPRESSED  CL-0001  Docker socket mounted via '/var/run/docker.sock:/var/run/docker.sock'. This gives the container full control over the Docker daemon.\n  service: traefik\n  reason: SEC-1234 approved — socket proxy planned for 2026-Q3\n\ndocker-compose.yml:10  HIGH      CL-0005  Port '8080:80' is bound to all interfaces. Docker bypasses host firewalls (UFW/firewalld), potentially exposing this port to the public internet.\n  service: traefik\n  fix: Bind to localhost: 127.0.0.1:8080:80\n       If public access is needed, use a reverse proxy with TLS.\n  ref: https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-5a---be-careful-when-mapping-container-ports-to-the-host-with-firewalls-like-ufw\ndocker-compose.yml: 1 high  ·  1 suppressed (not counted)\n✗ FAIL  ·  1 finding at or above high\n```\n\nExit code is `1` (one finding at or above the default `--fail-on high` threshold). Suppressed findings are shown for auditability but do not count toward the threshold.\n\n## Rules\n\n| ID | Severity | Description | OWASP | CIS |\n|----|----------|-------------|-------|-----|\n| [CL-0001](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0001.md) | CRITICAL | Docker socket mounted | [Rule #1](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-1---do-not-expose-the-docker-daemon-socket-even-to-the-containers) | 5.31 |\n| [CL-0002](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0002.md) | CRITICAL | Privileged mode enabled | [Rule #3](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-3---do-not-run-containers-with-the---privileged-flag) | 5.4 |\n| [CL-0003](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0003.md) | MEDIUM | Privilege escalation not blocked | [Rule #4](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-4---add-no-new-privileges-flag) | 5.25 |\n| [CL-0004](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0004.md) | MEDIUM | Image not pinned to version | [Rule #13](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-13---enhance-supply-chain-security) | 5.27 |\n| [CL-0005](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0005.md) | HIGH | Ports bound to all interfaces | [Rule #5a](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-5a---be-careful-when-mapping-container-ports-to-the-host-with-firewalls-like-ufw) | 5.13 |\n| [CL-0006](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0006.md) | MEDIUM | No capability restrictions | [Rule #3](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-3---limit-capabilities-grant-only-specific-capabilities-needed-by-a-container) | 5.3 |\n| [CL-0007](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0007.md) | MEDIUM | Filesystem not read-only | [Rule #8](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-8---set-filesystem-and-volumes-to-read-only) | 5.12 |\n| [CL-0008](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0008.md) | HIGH | Host network mode | [Rule #5](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-5---be-mindful-of-inter-container-connectivity) | 5.9 |\n| [CL-0009](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0009.md) | HIGH | Security profile disabled | [Rule #6](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-6---use-linux-security-module-seccomp-apparmor-or-selinux) | 5.21 |\n| [CL-0010](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0010.md) | HIGH | Host namespace sharing | [Rule #3](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-3---limit-capabilities-grant-only-specific-capabilities-needed-by-a-container) | 5.8, 5.15, 5.16, 5.21 |\n| [CL-0011](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0011.md) | HIGH | Dangerous capabilities added | [Rule #3](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-3---limit-capabilities-grant-only-specific-capabilities-needed-by-a-container) | 5.5 |\n| [CL-0012](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0012.md) | MEDIUM | PIDs cgroup limit disabled | — | 5.29 |\n| [CL-0013](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0013.md) | HIGH | Sensitive host path mounted | [Rule #8](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-8---set-filesystem-and-volumes-to-read-only) | 5.5 |\n| [CL-0014](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0014.md) | MEDIUM | Logging driver disabled | — | 5.x |\n| [CL-0015](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0015.md) | LOW | Healthcheck disabled | — | 4.6, 5.27 |\n| [CL-0016](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0016.md) | HIGH | Dangerous host device exposed | — | 5.18 |\n| [CL-0017](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0017.md) | MEDIUM | Shared mount propagation | — | 5.20 |\n| [CL-0018](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0018.md) | MEDIUM | Explicit root user | [Rule #7](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-7---do-not-run-containers-with-a-root-user) | 5.x |\n| [CL-0019](https://github.com/tmatens/compose-lint/blob/main/docs/rules/CL-0019.md) | MEDIUM | Image tag without digest | [Rule #13](https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-13---enhance-supply-chain-security) | 5.27 |\n\n## Severity Levels\n\nFindings are rated **LOW**, **MEDIUM**, **HIGH**, or **CRITICAL** based on exploitability and impact scope. See [docs/severity.md](https://github.com/tmatens/compose-lint/blob/main/docs/severity.md) for the full scoring matrix.\n\n## Configuration\n\nCreate `.compose-lint.yml` to disable rules or adjust severity:\n\n```yaml\nrules:\n  CL-0001:\n    enabled: false\n  CL-0003:\n    enabled: false\n    reason: \"SEC-1234 — Approved by J. Smith, expires 2026-07-01\"\n  CL-0005:\n    severity: medium\n```\n\nDisabled rules still run — findings appear as **SUPPRESSED** without affecting the exit code. The `reason` field is surfaced in all output formats:\n\n- **Text**: shown after the `SUPPRESSED` label\n- **JSON**: `suppression_reason` field\n- **SARIF**: `suppressions[].justification` (recognized by GitHub Code Scanning)\n\nTo hide suppressed findings from output:\n\n```bash\ncompose-lint --skip-suppressed docker-compose.yml\n```\n\n### Per-service rule exclusions\n\nWhen a rule is valid for some services but architecturally incompatible with\nothers (e.g. CL-0003 `no-new-privileges` and an image whose entrypoint\nswitches users), use `exclude_services` to suppress it for just the\naffected services while keeping it active elsewhere:\n\n```yaml\nrules:\n  CL-0003:\n    exclude_services:\n      minecraft: \"entrypoint switches users via su-exec\"\n      backup: \"forks as different user\"\n  CL-0007:\n    exclude_services:\n      - legacy-worker   # list form when no reason is needed\n```\n\nExcluded services still produce findings marked **SUPPRESSED** with the\nper-service reason flowing to `suppression_reason` / SARIF `justification`,\nsame as a global disable. Service names are matched exactly; unknown names\nproduce a stderr warning but do not error (Compose files are edited\nindependently of config). Global `enabled: false` takes precedence over\nper-service exclusions.\n\n## CLI Reference\n\n```\ncompose-lint [OPTIONS] [FILE ...]\n\n  --format {text,json,sarif}  Output format (default: text)\n  --fail-on SEVERITY          Minimum severity to trigger exit 1 (default: high)\n  --skip-suppressed           Hide suppressed findings from output\n  --config PATH               Path to config file (default: .compose-lint.yml)\n  --explain CL-XXXX           Print the full documentation for a single rule\n  --version                   Show version and exit\n```\n\n## Exit Codes\n\n| Code | Meaning |\n|------|---------|\n| 0 | No findings at or above the `--fail-on` threshold |\n| 1 | One or more findings at or above the `--fail-on` threshold |\n| 2 | Usage error (invalid args, file not found, invalid Compose file) |\n\nThe default threshold is `high` — medium and low findings don't fail CI unless you opt in:\n\n```bash\ncompose-lint --fail-on low docker-compose.yml   # fail on everything\ncompose-lint --fail-on critical docker-compose.yml  # only critical\n```\n\n## CI Integration\n\n### GitHub Actions\n\nThe easiest path — runs compose-lint and uploads findings to GitHub Code Scanning:\n\n```yaml\n# .github/workflows/lint.yml\nname: Compose Lint\non: [push, pull_request]\n\njobs:\n  compose-lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: tmatens/compose-lint@v0.3.7\n        with:\n          sarif-file: results.sarif\n```\n\nOr install from PyPI directly:\n\n```yaml\n      - uses: actions/setup-python@v6\n        with:\n          python-version: \"3.13\"\n      - run: pip install compose-lint\n      - run: compose-lint docker-compose.yml\n```\n\n### SARIF output\n\n```bash\ncompose-lint --format sarif docker-compose.yml \u003e results.sarif\n```\n\n## Pre-commit\n\n```yaml\n# .pre-commit-config.yaml\nrepos:\n  - repo: https://github.com/tmatens/compose-lint\n    rev: v0.3.7\n    hooks:\n      - id: compose-lint\n```\n\n## How it compares\n\n| Tool | Compose security rules | Scope | Zero config |\n|------|----------------------|-------|-------------|\n| **compose-lint** | Yes | Docker Compose | Yes |\n| **KICS** | Yes | Broad IaC (Terraform, K8s, Compose, ...) | No |\n| **Hadolint** | No — Dockerfile only | Dockerfile | Yes |\n| **dclint** | Yes — schema/structure only | Docker Compose | Yes |\n| **Trivy** | No — Dockerfile + image scanning | Dockerfiles, images, repos | Yes |\n| **Checkov** | No — no Compose support | Broad IaC (Terraform, K8s, ...) | No |\n\nIf you need broad IaC coverage across Terraform, Kubernetes, and more, KICS covers Docker Compose and is worth evaluating. If you want a lightweight, focused tool with zero config and actionable fix guidance for Compose files specifically, this is it.\n\n## Contributing\n\nSee [CONTRIBUTING.md](https://github.com/tmatens/compose-lint/blob/main/CONTRIBUTING.md) for development setup and how to add rules.\n\n## License\n\n[MIT](https://github.com/tmatens/compose-lint/blob/main/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftmatens%2Fcompose-lint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftmatens%2Fcompose-lint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftmatens%2Fcompose-lint/lists"}