Reading the report¶
Every format (text, HTML, JSON, SARIF, CycloneDX, GitLab) carries the same content. This page walks through the text report section by section and defines the terms that appear in all of them.
Header and warnings¶
Composer Remediate — 2 findings in /srv/app
Advisories: advisories from packagist.org
Solver: in-process Composer 2.10.3 dry-run (fallback: `composer update --no-install` in a scratch directory)
Warning: ...
The header names the advisory source and the solver route, so a report can be reproduced. Warnings are conditions that make the result weaker than it looks and deserve a reader's attention before the findings do:
| Warning starts with | Meaning |
|---|---|
Advisory source "…" only knows advisories for the current lock |
The source was a composer audit dump; candidate locks could not be checked for other advisories. |
N advisory matches ignored per configuration |
--ignore, config.audit.ignore or config.policy suppressed findings. |
Ignore hygiene: … |
Ignore entries that match nothing in this lock; stale configuration. |
Coverage gap: … |
An advisory database record about a locked package (or one it replaces or provides) could not be interpreted when the database was built; the package is treated as unaffected by that record. A lock with gaps and no findings exits 4, not 0, unless --accept-coverage-gaps is given. See Advisory database. |
Could not confirm that the advisory database at … is current / Offline: using the advisory database at … |
The published database could not be asked (or --offline); the local copy is used and its build age is given. --database-max-age turns this into exit 4 past a chosen age. |
Advisory database unavailable; the configured repositories were asked instead |
No copy at the database path and nothing could be fetched; the run used the repository API as composer audit does, without exploit data or coverage gaps. |
… is not a readable advisory database …; it will be replaced |
The file at the database path was not a database (corrupt, foreign); it was replaced by a download. |
… was downloaded without a published sha256 … |
The database in use was never verified against a checksum. |
<package>: the recommended command moves … to a version that still carries another advisory |
Blocking risk, see below. |
A gate that wants to be strict about any of these can read the warnings array of the JSON report.
One finding¶
CVE-2024-45411
────────────────────────────────────────────────────────────
Affected
twig/twig v3.10.3
CVE-2024-45411 (PKSA-6319-ffpf-gx66): Twig has a possible sandbox bypass
https://github.com/advisories/GHSA-6j75-5wfj-gh66
affected versions: >=1.0.0,<1.44.7|>=2.0.0,<2.16.0|>=3.0.0,<3.11.0|>=3.12.0,<3.14.0
Introduced by
root
└── drupal/core-recommended 10.3.1
└── twig/twig v3.10.3 (requires ~v3.10.2)
Current state
Transitive dependency.
Recommended remediation
drupal/core-recommended 10.3.1 -> 10.3.4
twig/twig v3.10.3 -> v3.14.0
Composer validation
PASS 3 packages changed, 0 added, 0 removed, 0 root constraints changed
Expected changes
drupal/core 10.3.1 -> 10.3.4
drupal/core-recommended 10.3.1 -> 10.3.4
twig/twig v3.10.3 -> v3.14.0
Recommended command
composer update drupal/core-recommended:10.3.4 -W -m
Other candidates
rejected: composer update twig/twig
resolves, but twig/twig ends at v3.10.3 which is still affected by CVE-2024-45411
- Affected: the locked package and every advisory on it. Several advisories on one package are
always handled together; the command must escape all of them.
(replaces x/y)after the package means the advisory is about a package this one replaces or provides. With an advisory database carrying exploit data, an advisory line is followed byEPSS 0.93 (97th percentile)and, when CISA lists the CVE as exploited in the wild,listed in CISA KEV since 2026-02-01; findings are ordered by that urgency, so the first finding in the report is the one to fix today. - Introduced by: the dependency paths from the root package down to the vulnerable one, with the constraint each link places on the next. Up to five paths are printed; the JSON report has up to ten and the total count.
- Current state: direct or transitive, and whether it is a development requirement only. An Abandoned line follows when Packagist marks the vulnerable package or a package on its dependency path abandoned, with the replacement Packagist names: an abandoned parent will not ship the release that lifts its pin, and an abandoned vulnerable package will not ship a fix.
- Recommended remediation: what moves. For a transitive package this names the parent that is updated and the version the vulnerable package lands on.
- Composer validation:
PASSmeans Composer's own solver resolved the command in a dry run and the resulting lock no longer contains the advisory. This line also counts changed, added and removed packages and any root-constraint change, and flags a major version change. - Expected changes: the full lock diff the command produces (first thirty entries).
- Recommended command: the exact command to run. It is the request that was verified, including
any
--withconstraint,-m, and platform flags the analysis was run with. - Other candidates: what else was tried and why it lost or was rejected, with the solver's own explanation. Candidates listed as "not tried" were skipped because a better one already existed or the solve budget ran out.
Notes that may appear between remediation and validation:
- Constraint drag: the only fix requires widening a constraint in
composer.json; the note names the root requirement that blocks every fix within the current constraints and the constraint the recommendation widens it to. The command then starts withcomposer require --no-update …. - Blocking risk: the command moves a package to a version that still carries another, already
present, advisory. Composer 2.10 and newer refuse such updates by default (advisory blocking); the
command may need that advisory ignored in
config.policyor blocking disabled to run. - Note: installs pre-release versions: no stable release satisfies the constraints yet.
When there is no fix¶
Recommended remediation
No verified remediation found (none).
The word in parentheses is the outcome, and it matters for gating:
| Outcome | Meaning | Exit code |
|---|---|---|
none |
Every candidate was solved and none removes the advisory within the current metadata. Usually a platform or constraint bound; the rejected candidates say which. | 2 |
none found within the search budget |
--max-candidates or --solve-budget cut the search short. A fix may exist outside the bounded search; raise the limits to look further. |
2 |
unknown: solver error |
At least one solve failed inside Composer. The absence of a fix is not established. | 3 |
unknown: network failure while solving |
Package metadata could not be fetched. Retry, or warm the cache. | 5 |
A finding without a fix is never silently equal to a finding with no vulnerability: exit code 2 is only returned when the search completed.
Summary¶
Summary
Findings: 2 advisories on 2 packages, 2 with a verified fix
You can fix all 2 findings with:
composer update symfony/http-foundation symfony/process
(2 packages changed, verified by Composer)
Gate: --fail-on high, 1 of 2 packages count towards the exit code (1).
The summary merges the per-package winners into one command and verifies it with a further solve;
when that does not fix everything it tries other combinations and smaller ones (see
global planning). It reads "You can fix all N findings with …",
or "… fixes k of N findings" when some have no reachable fix, or lists per-package commands when no
combination resolves. When more than one combination was tried, a "Combined command search" block
lists each attempt with its verdict, so the choice is explained rather than asserted. [baselined] marks packages
accepted through --baseline; the Gate line shows how --fail-on and the baseline shaped the exit
code.
Exit codes¶
| Exit | Meaning |
|---|---|
0 |
No known vulnerabilities in the lock (after --fail-on and baseline), and no coverage gap about a locked package unless --accept-coverage-gaps was given |
1 |
Vulnerabilities found and every gated one has a verified fix |
2 |
At least one gated vulnerability has no verified fix, and every solve completed |
3 |
Tool error: no lock file, bad option, or a solver error left an outcome unknown |
4 |
Advisory data unavailable: the source could not be read or fetched, an incomplete source cannot verify candidates, or records about locked packages could not be read (coverage gaps) and were not accepted |
5 |
Package metadata could not be fetched while solving |
The same content in other formats¶
- JSON (
--format=json,--output=x.json): one object per finding withremediation.status(verifiedornone),remediation.outcome,remediation.command,remediation.blocking_risk,constraint_drag, the fullcandidateslist,solver_runsandsearch_exhausted; thesummaryblock carries the combined command and the gate counts;warningsis the list above. The schema is published atdocs/schema/report.schema.json. - HTML (
--output=x.html): a self-contained page with the same sections, a summary table and collapsible candidate lists; advisory links are anchors only forhttp(s)URLs. - SARIF (
--output=x.sarif): one rule per advisory, one result per vulnerable package located at itscomposer.lockline, the recommended command in the message. For GitHub Code Scanning. - CycloneDX (
--output=x.cdx.json): the lock as an SBOM with vulnerabilities and their recommendation attached. - GitLab (
--output=gl-dependency-scanning-report.json): the dependency-scanning report format for GitLab's security dashboard, solution field holding the command.
--format selects what goes to standard output; --output writes files and can be repeated. See
CI integration for how to gate on these.