compose-lint
Version updated for https://github.com/tmatens/compose-lint to version v0.5.2.
- This action is used across all versions by 0 repositories.
Action Type
This is a Composite action.
Go to the GitHub Marketplace to find the latest changes.
Action Summary
compose-lint is a security-focused linter for Docker Compose files, designed to identify and prevent dangerous misconfigurations before deployment. It automates the process of validating Compose files against industry best practices, such as OWASP and CIS standards, offering a fast, zero-configuration, and opinionated solution. Key capabilities include automated detection of common security risks and compatibility with tools like Docker Scout, Trivy, and Grype for vulnerability scanning.
What’s Changed
Fixed
- CL-0009 now detects SELinux disabled via
security_opt: [label:disable]. The rule’s description and references promised SELinux coverage but the implementation only checked seccomp and AppArmor —label:disableturns off SELinux type enforcement for the container and was silently ignored. Description updated to reflect actual coverage; messages now read “SELinux” rather than “label profile”.label:user:...,label:type:...,label:role:...andlabel:level:...overrides remain unflagged since they reconfigure rather than disable confinement. - CL-0004 and CL-0019 now parse OCI image references via a
shared
split_image_refhelper that recognizesregistry:port/nameprefixes. The previous naiveimage.rsplit(":", 1)mistook the registry port for a tag separator, causing two related bugs: (a)localhost:5000/foowas treated as tag-pinned by CL-0004, so the “no tag, defaults to :latest” finding never fired; and (b) CL-0019 fired on the same input with a misleading message (“pinned to a tag but not a digest”) for an image that had no tag at all. Verified forlocalhost:5000/foo,localhost:5000/foo:latest,localhost:5000/foo:v1, and digest variants of each. - CL-0005 now detects IPv6 wildcard binds in short syntax
(
"[::]:8080:80") — the previous regex’s IP capture group rejected any colon-containing prefix, causing the rule to silently skip the port. Bracketed IPv6 prefixes are now stripped before the main pattern runs. - CL-0005 now detects explicit wildcard
host_ipvalues in long syntax (host_ip: "0.0.0.0",host_ip: "::"). The previous implementation treated any non-emptyhost_ipas a real bind, so operators who explicitly wildcarded their long-syntax bind got no warning. Loopback (127.0.0.1,::1) and specific addresses still suppress the finding. - CL-0005 also detects IPv4 wildcard short syntax (
"0.0.0.0:8080:80") — incidental fix; the previous_is_ip_addresshelper accepted0.0.0.0as a “real” IP and suppressed the finding. - CL-0013 now detects mounting the entire host root filesystem
(
"/:/host","/:/host:ro") at CRITICAL severity — previously the short-syntax regex required at least one non-colon character after/and silently skipped the most dangerous bind possible. - CL-0013 now detects long-syntax binds where
source:is an absolute path even whentype: bindis omitted. Compose infers bind mounts from absolute-path sources, but the rule previously gated ontypeand missed this realistic configuration. - CL-0013 sensitive-paths list extended with
/var/lib/docker,/var/run, and/home. The existing/rootentry already covered/root/.sshand/root/.awsvia subpath matching. - CL-0011 now flags
cap_add: [ALL](and lowercase[all]) at CRITICAL severity. Granting all Linux capabilities is functionally equivalent to--privilegedfor capability isolation, but the rule previously only knew the seven named caps and silently ignored the catch-all. Named caps (SYS_ADMIN,NET_ADMIN, etc.) continue to fire at HIGH; the rule now emits per-finding severity so--fail-onthresholds against the named caps are unchanged. - CL-0015 now flags
test: ["NONE"]and the string formtest: NONE, the idiomatic way to disable a healthcheck inherited from a base image. Lowercase["none"]deliberately does not fire — Docker’s runtime treats only uppercaseNONEas the disable sentinel; lowercase is executed as a command and is a different problem (a broken healthcheck, not a disabled one). Severity stays at LOW. - CL-0018 now detects the cross-spec root forms
root:0,0:root,root:1000, and0:1000by parsinguser:rather than matching a fixed allowlist. The previous{"root", "0", "root:root", "0:0"}set silently passed any value where a non-root group was paired with a root user, even though the container still runs as UID 0. The inverse (user: "1000:0"— non-root UID with root group) correctly does not fire. - OpenVEX product identifier in
.vex/compose-lint.openvex.jsonnow usesrepository_url=index.docker.io/composelint/compose-lint. The previousdocker.io/...form loaded successfully but matched zero scanned images: Trivy, Grype (per anchore/grype#2818), and Scout all canonicalise Docker Hub toindex.docker.iofor VEX product matching. Confirmed locally with Trivy 0.70.0 against the published image. - Every VEX statement now ships two
products[]entries —pkg:oci/compose-lint?repository_url=index.docker.io/composelint/compose-lintfor Trivy and Grype, plus a barepkg:docker/composelint/compose-lintfor Docker Scout, whose own “Create exceptions” docs example uses thepkg:docker/form. Trivy honoured the single-PURL form from PR #143 but Scout did not — verified empirically on commit5abd036’sscout-scan.ymldispatch whereLoaded 1 VEX documentwas followed by all three pip CVEs still flagged. OpenVEX explicitly invites multi-identifier products for exactly this scanner-disagreement case. - Every
docker/scout-actionstep that passesvex-locationnow passesvex-author: .*. Scout’s default--vex-authorallowlist is<.*@docker.com>and silently drops statements signed outside that pattern. PR #143’s first override (<.*@gmail\.com>) was also silently dropped — Scout appears to use full-string regex match on the author field rather than substring, so the bracket-anchored shape did not match the full author stringTodd Matens <tmatens@gmail.com>..*accepts any author and is safe because the document is also cosign-attested to the image manifest. Applied to bothscout-scan.ymlsteps and thedocker-smokeScout step inpublish.yml.
Added
- VEX statement covering CVE-2026-3219 (pip 25.1.1 — incorrect file
installation due to improper archive handling). Same
vulnerable_code_not_presentmitigation as the existing pip CVEs: pip’s runtime code is removed from the container image during build, only.dist-infometadata remains for SCA scanner identification.
Changed
- VEX document
versionbumped to 3 andtimestamprefreshed. See ADR-012 (docs/adr/012-vex-product-identifier.md) for the full rationale on the product-identifier and author-allowlist decisions, including the empirical evidence from PR #143’s first attempt.
Security
- CI
pip-auditstep ignoresCVE-2026-3219(pip 26.0.1) until pip 26.0.2+ ships on PyPI and the dev lockfile is regenerated. pip is a dev-only transitive ofpip-audithere — it is not inrequirements.lockand is stripped from the runtime container image (only.dist-infometadata is kept for SCA attribution). The same CVE is declarednot_affectedagainst the published image via the OpenVEX document on the samevulnerable_code_not_presentgrounds as the existing pip CVEs.