GHSA-r7j4-crjv-h467

Suggest an improvement
Source
https://github.com/advisories/GHSA-r7j4-crjv-h467
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-r7j4-crjv-h467/GHSA-r7j4-crjv-h467.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-r7j4-crjv-h467
Aliases
  • CVE-2026-125121
Published
2026-11-10T18:28:35Z
Modified
2026-11-10T18:45:03Z
Severity
  • 5.3 (Medium) CVSS_V4 - CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N CVSS Calculator
Summary
compose-lint: Text in a pull request can still issue CI workflow commands through compose-lint 0.31.0's output
Details

Impact

0.31.0 fixed GHSA-6f4g-xm8v-pgv6, where text from a Compose file became GitHub Actions workflow commands in compose-lint's output. The fix escapes the command openers in everything compose-lint prints, and the 0.31.0 changelog says the report "escapes both openers (also covering Azure Pipelines)". Five paths were left open:

  • Unicode whitespace before a line-leading ::. The runner strips all Unicode whitespace from the start of a line before it looks for a command. compose-lint only accounted for spaces and tabs, so a no-break space or another Unicode space ahead of :: is not escaped. It can come from a Compose value, or from a file name: the text report's file heading and per-file summary, and the fix summary on stderr, start with the path as given, so a directory named with a leading no-break space followed by :: puts a command at the start of a line. This reaches those lines, the text report's fix guidance, the fix dry-run diff, and continuation lines of stderr diagnostics.
  • JSON and SARIF printed to stdout. --format json and --format sarif output never passed through the sanitizer, so ##[ inside a service name or other value reaches the log intact when the output isn't redirected to a file.
  • Command-line errors. When a repository path reaches compose-lint's arguments without a -- before it, the argument parser prints it raw in its error message. That includes a carriage return, which the runner treats as a line break, so a command can also start a new line there.
  • Azure Pipelines. ##vso[ logging commands were not escaped on any path, despite the changelog's Azure claim.
  • An error message in the GitHub Action (0.30.0 and 0.31.0). When sarif-file: names a file in a subdirectory that can't be created, the Action prints the directory's resolved path in an error message, outside the window in which it turns command processing off. A pull request can commit a symlink on that path to a directory whose name contains a newline followed by a command. The documented setup, sarif-file: results.sarif at the repository root, is not affected.

A pull request can therefore create annotations, fold or hide parts of the job log, or issue other commands the runner accepts, when CI runs compose-lint over it, before anyone reviews it. The pass/fail result is not changed.

Affected setups are those that print compose-lint's output to a CI log without the GitHub Action: the compose-lint package run directly in a workflow, the pre-commit hook run in CI, and the Docker image. The pre-commit hook is affected in its default configuration: it passes each changed file's path to compose-lint, and a path whose directory name starts with Unicode whitespace and :: reaches the report's file heading. The JSON/SARIF and command-line paths also need output left on stdout, or args: without the documented trailing --, which is why they alone would score lower. The GitHub Action turns command processing off around each compose-lint run, so of these paths only its own error message affects it, and only when sarif-file: points into a subdirectory. That setup is outside the documented one, which is why that path alone would also score lower.

Found during an internal pre-1.0 security review.

Docker image. composelint/compose-lint tag 0.31.0, the matching minor tag, and latest are affected.

Releases before 0.31.0 are covered by GHSA-6f4g-xm8v-pgv6, except the Action's error message, which also affects Action 0.30.0.

Patches

Fixed in 0.32.0. The line-start escaping covers every character the runner strips before ::, so it holds for quoted values and file names alike. JSON and SARIF on stdout escape ##[ inside strings as \u005b, which decodes to the same data. argparse's own messages go through the same sanitizer. ##vso[ is escaped everywhere. In the Action, the SARIF-directory error names the sarif-file: input as written.

Workarounds

  • Upgrade to 0.32.0 or later.
  • On GitHub Actions, use the tmatens/compose-lint Action, or bracket the compose-lint step with ::stop-commands::<random token> and ::<token>::, as the Action does.
  • In the Action, keep sarif-file at the workspace root, for example sarif-file: results.sarif.
  • Write --format json and --format sarif output to a file rather than the log.
  • End the pre-commit hook's args: with --. This closes the command-line path only; it does not stop a file name at the start of a report line, so in CI also bracket the pre-commit step with ::stop-commands:: as above.
  • On Azure Pipelines, don't print compose-lint's output from runs over untrusted pull requests to the job log.

Reproduction

On compose-lint 0.31.0 on a GitHub-hosted runner (runner 2.337.0), each of the following creates an annotation:

  • a Compose file with image: "nginx\n\u00a0::notice::X", linted with the text report;
  • a directory named \u00a0::notice::X (a no-break space, then ::notice::X) holding a compose.yml, linted with compose-lint -- '<that directory>/compose.yml', or by the pre-commit hook in its default configuration;
  • a service named a##[warning]X, linted with --format json or --format sarif to stdout;
  • compose-lint '--##[warning]X/compose.yml' with no --;
  • the Action with sarif-file: out/reports/results.sarif, where out is a committed symlink to a directory named d\n::warning::X that contains a regular file reports.

The same inputs with a plain space in place of the no-break space, or with -- before the path, or with sarif-file at the repository root, create none. The same inputs on compose-lint 0.30.0 create annotations even with a plain space, which is GHSA-6f4g-xm8v-pgv6. The composelint/compose-lint:0.31.0 image produces byte-identical output to the package.

Database specific
{
    "cwe_ids": [
        "CWE-117",
        "CWE-150"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-11-10T18:28:35Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
}
References

Affected packages

PyPI / compose-lint

Package

Name
compose-lint
View open source insights on deps.dev
Purl
pkg:pypi/compose-lint

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0.31.0
Fixed
0.32.0

Affected versions

0.*
0.31.0

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-r7j4-crjv-h467/GHSA-r7j4-crjv-h467.json"