audit reports the known security advisories affecting the versions you actually have installed. It covers composer.lock and package-lock.json in a single pass, and for each vulnerable package it resolves the lowest version that fixes the problem.

whatsdiff audit
2 vulnerable packages, 4 security advisories
symfony/cache v8.0.5 → fixed in v8.0.12
○ CVE-2026-45073 SQL Injection in PdoAdapter::doClear() via Unsanitized $prefix
symfony/yaml v8.0.1 → fixed in v8.0.12
○ CVE-2026-45304 YAML Parser Exponential Memory Allocation via Recursive Collection-Alias Expansion ("Billion Laughs")
○ CVE-2026-45305 YAML Parser ReDoS via Catastrophic Backtracking in Parser::cleanup() Regex
○ CVE-2026-45133 YAML Parser Stack Exhaustion via Unbounded Recursion in Nested Blocks, Sequences, and Mappings
Legend: ○ rating pending (4)
In an interactive terminal the severity bullets are coloured (red for critical and high, yellow for medium, cyan for low, grey for unrated) and CVE IDs become clickable links wherever the terminal supports OSC 8.
Three modes
By default, audit reads the lock files in your working tree.
--at audits the lock file as it existed at a given commit, tag, or branch:
whatsdiff audit --at=v2.3.0
--from and --to switch to diff mode, which reports only the advisories a change introduces and leaves pre-existing ones alone. In a pull request pipeline this keeps the feedback on what the author actually touched:
whatsdiff audit --from=v2.2.0 --to=v2.3.0
Options
| Option | Description |
|---|---|
--from |
Commit, branch, or tag to compare from (enables diff mode) |
--to |
Commit, branch, or tag to compare to (defaults to HEAD) |
--at |
Audit the lock file at a given commit, tag, or branch |
-f, --format |
Output format: text (default), json, or markdown |
--no-fix |
Skip the suggested-fix lookup, saving the extra registry calls |
--include |
Only these package managers (comma-separated: composer, npmjs) |
--exclude |
Everything but these package managers |
--fail-on |
Severity threshold for a non-zero exit: low (default), medium, high, critical, or none |
--allow-unrated |
Do not trip --fail-on on advisories with no upstream severity rating |
--no-cache |
Bypass the cache and fetch fresh advisories |
--at cannot be combined with --from or --to, and --include cannot be combined with --exclude.
An advisory whose severity has not been rated upstream yet counts towards any --fail-on threshold, so a pending CVSS score never slips through. Pass --allow-unrated if you would rather not block on those.
Exit codes
| Code | Meaning |
|---|---|
0 |
No advisory meets the --fail-on threshold |
1 |
An advisory meets or exceeds the threshold, or the options were invalid |
2 |
Lock file missing, or a network, registry, or git error |
Pass --fail-on=none to always exit 0 when you only want the report.
Output formats
The text format above is the default: a coloured list with the installed version, a suggested fix, and a legend counting advisories by severity.
JSON
whatsdiff audit --format=json
Trimmed to a single advisory:
{
"mode": "current",
"from": null,
"to": null,
"summary": {
"vulnerable_packages": 2,
"total_advisories": 4,
"by_severity": {
"critical": 0,
"high": 0,
"medium": 0,
"low": 0,
"unknown": 4
},
"max_severity": "unknown"
},
"audits": [
{
"name": "symfony/cache",
"type": "composer",
"installed_version": "v8.0.5",
"suggested_fix_version": "v8.0.12",
"max_severity": "unknown",
"advisories": [
{
"advisory_id": "PKSA-z7t6-zt6p-wtng",
"cve": "CVE-2026-45073",
"title": "CVE-2026-45073: SQL Injection in PdoAdapter::doClear() via Unsanitized $prefix",
"link": "https://symfony.com/cve-2026-45073",
"affected_versions": ">=8.0.0,<8.0.12",
"severity": "unknown"
}
]
}
]
}
In diff mode, mode is "diff" and from and to hold the resolved commit hashes.
Markdown
whatsdiff audit --format=markdown > security-report.md
A summary table followed by one section per severity, ready to paste into a pull request or an issue:
# Security Audit
## Summary
| Severity | Count |
|----------|-------|
| Unknown | 4 |
## Unknown
### symfony/cache (`v8.0.5`)
**Fix available:** upgrade to `v8.0.12`
| ID | Severity | Title |
|----------------------------------------------------------|----------|----------------------------------------------------------------------------------|
| [CVE-2026-45073](https://symfony.com/cve-2026-45073) | Unknown | CVE-2026-45073: SQL Injection in PdoAdapter::doClear() via Unsanitized $prefix |
Failing a build on serious advisories
Block the deploy on high and critical findings while tolerating the rest:
name: Security Audit
on: [pull_request, push]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # diff-mode runs need the full history
- name: Install whatsdiff
run: composer global require whatsdiff/whatsdiff
- name: Audit dependencies
run: whatsdiff audit --fail-on=high
Tighten or loosen the gate by changing --fail-on.
Reporting new advisories on a pull request
Diff mode plus markdown output gives you a comment covering only what the branch introduced:
- name: Audit new advisories vs main
run: |
whatsdiff audit \
--from=origin/main \
--to=HEAD \
--format=markdown \
--fail-on=medium \
> new-advisories.md
- name: Comment on PR
uses: marocchino/sticky-pull-request-comment@v2
with:
path: new-advisories.md
Use analyse to see every dependency change rather than the vulnerable ones, or check to test a single package.