Aller au contenu
login
arrow_backRetour aux issues
tmatens/compose-lint #671

A fragment compose.override.yml skips the whole project and reports PASS, dropping a CRITICAL

ecoDébutant bug good first issue

descriptionDescription

An overlay that Compose merges without complaint makes compose-lint skip the entire project and report `PASS` at exit 0, dropping every finding in the base file. Three innocuous override shapes trigger it. Filing publicly per SECURITY.md, which puts false negatives in security rules in scope for normal issues. ## Reproduction ```yaml # compose.yml services: web: image: nginx privileged: true ``` ```yaml # compose.override.yml volumes: data: {} ``` With the override present: ```console $ compose-lint check compose.yml compose.yml: Skipped: file appears to be a Compose fragment (no 'services:' key; only top-level structural keys present). Fragments are typically merged via `extends:` or `-f` overlays and have no services to lint on their own. ✓ PASS · threshold: high $ echo $? 0 ``` Delete the override and the same base file reports: ```console 4 CRITICAL CL-0002 Service runs in privileged mode... ✗ FAIL · 1 finding at or above high $ echo $? 1 ``` Docker Compose merges and deploys it either way (`docker compose config` exits 0, `privileged: true` intact). ## Scope Any overlay with no `services:` key does it. All three verified against Docker Compose 5.4.0, all three deploy fine, all three produce `PASS` / exit 0: | `compose.override.yml` | compose-lint | Compose | |---|---|---| | `volumes:\n data: {}` | PASS, exit 0 | deploys, `privileged: true` | | `networks:\n n: {}` | PASS, exit 0 | deploys, `privileged: true` | | `{}` | PASS, exit 0 | deploys, `privileged: true` | ## Why it matters The shipped deployment model is a CI merge gate. Adding a two-byte `compose.override.yml` containing `{}` turns a failing gate green while changing nothing about what deploys. That is a silent false negative of the kind ADR-023 ranks worst, and it is trivially reachable rather than an edge case: an overlay carrying only `volumes:` or `networks:` is an ordinary thing to write. The verdict is also actively wrong rather than merely absent. `✓ PASS` asserts the stack was graded and cleared. It was not graded at all. ## Cause `load_merged` calls `load_document` per path. A fragment overlay raises `ComposeNotApplicableError` (ADR-013), which propagates out of the merge. The `check` loop's `except ComposeNotApplicableError` clause at `cli.py:~707` is written for the single-file case, where "this file is a fragment" correctly means "skip it", so it skips the whole project and reports it under the primary path. Two defects fall out of that: 1. **The project is skipped when only the overlay is a fragment.** The base file has `services:` and is perfectly lintable. This is the security-relevant half. 2. **The message names the wrong file.** `compose.yml` is reported as the fragment when `compose.override.yml` is. Same class as #666, and deliberately not covered by #670, whose `except ComposeNotApplicableError: raise` is the correct ADR-013 behaviour. The fix belongs here, not there. ## Direction Compose treats a fragment overlay as an ordinary document that happens to contribute only `volumes:` or `networks:`, and merges it. ADR-025 says compose-lint grades the configuration Compose runs, so the merge should do the same and lint the result. ADR-013's "a fragment alone has no services to lint" reasoning is about a file linted *on its own*. It was never weighed against a fragment sitting in a merge set beside a base file that does have services, which is the case here. Whatever the resolution, the current outcome is the one thing it must not stay: reporting `PASS` at exit 0 on a stack that was never graded.
codeOuvre sur GitHub