GHSA-88F4-FRH5-GMCR

Vulnerability from github – Published: 2026-11-10 15:57 – Updated: 2026-11-10 15:57
VLAI
Summary
compose-lint: A `.compose-lint.yml` symlinked outside the project is read, and a parse error quotes part of the target
Details

Impact

compose-lint automatically reads a .compose-lint.yml policy file beside the Compose file. It is meant to read nothing outside the project directory, but it follows that file if it is a symlink, wherever it points.

A pull request can commit .compose-lint.yml as a symlink to a file on the CI runner. If the target is not valid YAML, the error compose-lint reports quotes part of the line the parser failed on. That quote reaches the job log (standard error), JSON errors[], and the SARIF toolExecutionNotifications, which the Action uploads to Code Scanning. The run exits 2.

What can be read is small. The quote is cut to about 30 characters around the error position, and the attacker cannot choose which line of the target the parser fails on. For an INI-style credentials file, this was the start of the second line (aws_access_key_id = followed by the first characters of the value). Targets in KEY=value form, and YAML files that parse, disclosed no values in testing. The pass/fail result is not affected. On a public repository, the job log is visible to the pull request's author.

The Compose file loader stopped quoting source lines in parse errors in 0.25.0 (GHSA-whr6-fgpq-fpv9). The policy loader did not.

Found during an internal pre-1.0 security review.

GitHub Action. Action tags v0.18.0 through v0.31.0 install their own compose-lint version and are affected. Earlier tags install the newest release unless version: is set, and so currently install an affected one.

Docker image. composelint/compose-lint tags 0.3.3 through 0.31.0, the matching minor tags, and latest are affected. Inside the container, a symlink resolves within the container, so what can be read is the container's own files and anything mounted into it.

Patches

Fixed in 0.32.0. A .compose-lint.yml discovered in the working directory that resolves outside it is refused with exit 2 rather than read, and a policy parse error no longer quotes the line it failed on, matching the Compose loader. --config is read as given. 0.32.0 also stops following a Compose file that is a symlink to a target outside the checkout.

Workarounds

  • Upgrade to 0.32.0 or later.
  • Run with an explicit --config pointing at a policy file inside the project; the automatic lookup is then skipped.
  • Don't run compose-lint on untrusted pull requests on a machine whose files outside the checkout hold credentials.

Reproduction

project/.compose-lint.yml -> /path/outside/project/credentials   (symlink)
project/compose.yml          services: {web: {image: nginx:1.27}}

compose-lint --format sarif compose.yml exits 2, and the notification text quotes the start of the target's second line.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.31.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "compose-lint"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.2.0"
            },
            {
              "fixed": "0.32.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.31.0"
      },
      "package": {
        "ecosystem": "GitHub Actions",
        "name": "tmatens/compose-lint"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.2.0"
            },
            {
              "fixed": "0.32.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-123371"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-209",
      "CWE-59"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-11-10T15:57:07Z",
    "nvd_published_at": "2026-11-09T18:17:26Z",
    "severity": "LOW"
  },
  "details": "### Impact\n\ncompose-lint automatically reads a `.compose-lint.yml` policy file beside the Compose file. It is meant to read nothing outside the project directory, but it follows that file if it is a symlink, wherever it points.\n\nA pull request can commit `.compose-lint.yml` as a symlink to a file on the CI runner. If the target is not valid YAML, the error compose-lint reports quotes part of the line the parser failed on. That quote reaches the job log (standard error), JSON `errors[]`, and the SARIF `toolExecutionNotifications`, which the Action uploads to Code Scanning. The run exits 2.\n\nWhat can be read is small. The quote is cut to about 30 characters around the error position, and the attacker cannot choose which line of the target the parser fails on. For an INI-style credentials file, this was the start of the second line (`aws_access_key_id = ` followed by the first characters of the value). Targets in `KEY=value` form, and YAML files that parse, disclosed no values in testing. The pass/fail result is not affected. On a public repository, the job log is visible to the pull request\u0027s author.\n\nThe Compose file loader stopped quoting source lines in parse errors in 0.25.0 (GHSA-whr6-fgpq-fpv9). The policy loader did not.\n\nFound during an internal pre-1.0 security review.\n\n**GitHub Action.** Action tags v0.18.0 through v0.31.0 install their own compose-lint version and are affected. Earlier tags install the newest release unless `version:` is set, and so currently install an affected one.\n\n**Docker image.** `composelint/compose-lint` tags 0.3.3 through 0.31.0, the matching minor tags, and `latest` are affected. Inside the container, a symlink resolves within the container, so what can be read is the container\u0027s own files and anything mounted into it.\n\n### Patches\n\nFixed in 0.32.0. A `.compose-lint.yml` discovered in the working directory that resolves outside it is refused with exit 2 rather than read, and a policy parse error no longer quotes the line it failed on, matching the Compose loader. `--config` is read as given. 0.32.0 also stops following a Compose file that is a symlink to a target outside the checkout.\n\n### Workarounds\n\n- Upgrade to 0.32.0 or later.\n- Run with an explicit `--config` pointing at a policy file inside the project; the automatic lookup is then skipped.\n- Don\u0027t run compose-lint on untrusted pull requests on a machine whose files outside the checkout hold credentials.\n\n### Reproduction\n\n```\nproject/.compose-lint.yml -\u003e /path/outside/project/credentials   (symlink)\nproject/compose.yml          services: {web: {image: nginx:1.27}}\n```\n\n`compose-lint --format sarif compose.yml` exits 2, and the notification text quotes the start of the target\u0027s second line.",
  "id": "GHSA-88f4-frh5-gmcr",
  "modified": "2026-11-10T15:57:07Z",
  "published": "2026-11-10T15:57:07Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/tmatens/compose-lint/security/advisories/GHSA-88f4-frh5-gmcr"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-123371"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tmatens/compose-lint/pull/943"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tmatens/compose-lint/commit/e80547d33b2114040c4bc0e34ef210aee51f5058"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/tmatens/compose-lint"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tmatens/compose-lint/releases/tag/v0.32.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "compose-lint: A `.compose-lint.yml` symlinked outside the project is read, and a parse error quotes part of the target"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
How durations are calculated

Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.

Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.

Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.