dependency-audit¶
Scans the project's dependency tree for known CVEs and (optionally) license-compliance issues; severity-sorted report.
Scan the current project's dependency tree for known vulnerabilities (CVEs) and, when requested, license-compliance issues. Dispatches dependency-audit-scanner agent for the read-only scan step. Detects project kind from pyproject.toml / requirements*.txt / poetry.lock / uv.lock for Python and package.json / package-lock.json / pnpm-lock.yaml / yarn.lock for Node, runs the appropriate auditors, and produces a severity-sorted report with direct vs transitive attribution. Invoke when the user asks to "audit dependencies," "run a CVE scan," "check for vulnerable packages," "check license compliance," "run pip-audit," "run npm audit," or equivalent German-language requests. Also handles a pre-PR / pre-release dependency gate. Don't use for upgrading dependencies (that's an author's decision) or for writing Renovate configs (that's project-structure-apply). Supports resume on re-invocation per spec/claude/resumable-work/.
- Plugin:
nolte-engineering - Phase: 6 Quality (
quality) - Tags:
dependency - Source: skills/dependency-audit/SKILL.md
Use when¶
- you want to run a CVE scan on the current project
- you want a pre-PR or pre-release dependency-vulnerability gate
- you want to check license compliance across direct and transitive dependencies
Don't use when¶
- You want to upgrade dependencies (that is an author's decision) →
project-structure-apply - You want to write or update Renovate config →
project-structure-apply
See also¶
Referenced by¶
code-security-reviewerdependency-audit-scannerwebview-ui-expertgdpr-data-protection-reviewerquality-gate-enforcertech-stack-drift-reviewertech-stack-fitness-reviewerlicense-checkquality-gatewebview-ui-optimizeworkflow-health-triage
Dependency Audit¶
Run a CVE and optional license audit against every dependency manifest the current project ships, and produce a single severity-sorted report. This skill reports and recommends; it never upgrades, pins, or removes dependencies on its own.
Implements spec/project/dependency-audit/ — the spec defines the audit scope, severity mapping, and per-language tooling. This skill binds those rules to the on-disk procedure.
German trigger phrases¶
This skill also triggers on equivalent German-language requests, including:
- "Abhängigkeiten auditieren"
- "CVE-Scan durchführen"
- "Lizenz-Compliance prüfen"
User-language policy¶
Detect the user's language from their message and respond in it. The report itself uses English section headings (so downstream tooling can parse it reliably); prose around the report is localised.
Inputs¶
- Repo root: default is the current working directory.
- License audit toggle: defaults to on whenever a license allowlist is discoverable in the repo (at
.license-allowlist.txt,.licenses/allowed.txt, undertool.*in the manifest, or equivalent) — perspec/project/dependency-audit/, a discoverable allowlist means license auditing is "enabled" and the pass runs automatically. When no allowlist is discoverable, the toggle is off by default and the caller opts in explicitly ("also check licenses," "include license compliance"). The caller may always opt out of an automatic license pass for an ad-hoc invocation. - Severity floor: defaults to
low(report every finding). Caller may narrow tomediumorhighto de-noise pre-release gates.
Operations¶
0. Dispatch the read-only scan agent¶
Dispatch dependency-audit-scanner (Agent) for the read-only audit pass: it detects project type, runs the matching auditor (pip-audit / npm audit / etc.), and returns a structured drift report. Wait for its inventory before proceeding to severity-triage and follow-up actions.
1. Detect project kind¶
Look in the repo root and obvious subroots (backend/, frontend/, packages/*, apps/*):
| Indicator | Kind | Auditor |
|---|---|---|
pyproject.toml, requirements*.txt, poetry.lock, uv.lock, Pipfile.lock |
Python | pip-audit |
package.json, package-lock.json |
Node (npm) | npm audit --json |
pnpm-lock.yaml |
Node (pnpm) | pnpm audit --json |
yarn.lock |
Node (yarn) | yarn npm audit --json or yarn audit --json depending on version |
go.mod |
Go | govulncheck ./... (if available; otherwise report "no Go auditor installed") |
Cargo.toml |
Rust | cargo audit (if available) |
If the project has no detectable manifest, stop and report clearly. Don't guess.
Record every subroot where a manifest was found; audits run per subroot so the report can attribute findings.
2. Prefer Taskfile targets when they exist¶
If the repo has a Taskfile.yml (or Taskfile.yaml) at the root, check for existing audit-named targets before invoking auditors directly:
task --list-all 2>/dev/null | grep -E '^\* (audit|deps:audit|security:audit)'
When a target exists and it wraps the same auditor you'd otherwise run, invoke it via task <target> instead. Record in the report that the target was used. This keeps the skill consistent with the project's conventions and picks up any project-specific ignore-list the Taskfile applies.
If no matching target exists, call the auditor directly.
3. Run auditors¶
Run every detected auditor per subroot. Use --json / equivalent machine-readable output where available; fall back to text when necessary.
- Python (
pip-audit):pip-audit --format=json --progress-spinner=off [--require-hashes] [-r <file> | -e .]. When the project usesuv, preferuv pip compile-generated requirements as the input. Skippip-auditwhen the binary is missing and instead emit an install hint (pip install pip-auditoruv tool install pip-audit); don't silently skip. - Node (
npm audit):npm audit --jsonfrom the subroot that owns the lockfile. Strip out theauditReportVersionmetadata before parsing. - Node (
pnpm audit):pnpm audit --json— JSON shape differs from npm; normalise to the same internal structure. - Node (
yarn audit): v1:yarn audit --json(newline-delimited JSON); v2+:yarn npm audit --json.
Record per finding: package, installed_version, advisory_id (GHSA/CVE/PYSEC), severity (critical / high / medium / low / unknown), path (direct or transitive), fixed_in, summary_url.
Severity classification (CVSS scale). Per spec/project/dependency-audit/ §Severity classification, take the auditor's native classification as the source of truth and map it onto the shared scale by CVSS threshold:
| Severity | CVSS | Auditor tag | Response window |
|---|---|---|---|
critical |
≥ 9.0 | critical |
within 7 days |
high |
7.0–8.9 | high |
within 30 days |
medium |
4.0–6.9 | moderate / medium |
within the current calendar quarter |
low |
< 4.0 | low |
best effort, revisited next quarterly audit |
unknown |
unclassified | — | treat as high until classified otherwise |
When the auditor couldn't classify a finding, record it as unknown and treat it as high for triage and response-window purposes — never as a lower tier. Never downgrade a severity on local judgement alone (disagreement is a dated ignore entry with a rationale, not a reclassification).
Gate outcome: blocked, not pass. When a subroot can't be audited because its auditor isn't installed (or a lockfile is missing), the audit MUST report that subroot's verdict as blocked with the install/remediation hint — never pass. A blocked subroot is not a clean result: the gate fails closed. Only a subroot the auditor actually scanned can return pass.
4. Run a license audit (auto-on when an allowlist is discoverable)¶
First check for a license allowlist. Locations to check, in order:
.license-allowlist.txtor.licenses/allowed.txtat the repo root.- A
licenses:array undertool.pip-auditor an equivalent config block inpyproject.toml. - The project's README if it explicitly lists accepted licenses (uncommon).
Per spec/project/dependency-audit/, license auditing counts as "enabled" whenever an allowlist is discoverable. When one is found, run the license pass automatically (the caller may still opt out for an ad-hoc invocation). When no allowlist is discoverable, run the license pass only when the caller explicitly asked for it.
When the license pass runs:
- Python:
pip-licenses --format=json --with-urls --with-license-file=false(install hint:pip install pip-licenses). - Node:
npx --yes license-checker --json --production(orpnpm licenses list --long --jsonfor pnpm).
Compare the discovered licenses against the allowlist. If no allowlist exists (and the caller opted in anyway), flag every non-permissive license (GPL / AGPL / LGPL / SSPL / unknown) as review, not as failure. Don't invent a policy.
5. Render the report¶
```
Dependency Audit¶
Scope: )
Trigger:
Git revision:
Findings (sorted: critical → high → medium → low)¶
— ¶
@ (subroot:, path: direct|transitive via ) - Advisory:
— - Fixed in:
- Reference:
- Response:
) | accept as known>
(repeat per finding)
License review¶
Health¶
- Total findings:
(critical: , high: , medium: , low: ) - Python manifests audited:
- Node manifests audited:
- Auditors skipped (with reason):
- Taskfile targets used:
-
```
Sort findings by severity first, then package name alphabetically, so diffs of the rendered report stay stable across runs.
6. Triage and respond¶
Per spec/project/dependency-audit/ §Response to findings, every finding gets exactly one of three responses inside its severity's response window. Never silently downgrade a severity — disagreement with the auditor's classification is an ignore entry with an explicit rationale, not a reclassification.
fix: bump to the smallest version that crosses thefixed_inboundary. For acriticalorhighfinding this is the only response, unless an accepted, dated ignore entry exists.ignore with rationale: record the advisory in the auditor's native ignore location (for examplepyproject.tomlunder[tool.pip-audit], or.npm-audit-ignore.json) — never as free-form prose only the report sees. Every entry MUST carry the advisory ID, the affected package, avalid-untilISO-8601 date, and a one-line rationale; an entry missing any of these fails the audit. Revisit each entry at the latest on itsvalid-untildate and never renew it without a fresh rationale. Never silence a finding globally (--ignore-vuln <id>with no date) just to green the gate.accept as known: permitted only for themedium/lowtiers. Acriticalorhighfinding MUST NOT be markedaccept as known.
Don't execute a bump, write an ignore entry, or draft a .license-allowlist.txt without explicit confirmation. Keep the active ignore list small (guideline: fewer than ten per subroot); a growing list signals the dependency strategy itself needs review rather than another one-off ignore.
7. Persist the audit artifact¶
Per spec/project/dependency-audit/ §Audit artifact, persist every full audit as a git-tracked artifact (a commit or a security-audit-labelled issue are accepted alternatives). Default to the canonical path .audits/dependency-audit/dependencies-YYYY-Q<n>.md (the portfolio-wide .audits/<audit-type>/ standard). The artifact MUST record: date; trigger (quarterly / pre-release / manifest-change); scope (which subroots were audited, which were skipped and why); the tools used and their versions; the per-finding severity and response decision; and the Git revision audited. Link to the prior quarter's artifact so the progression stays traceable. The quarterly spec-drift-audit references this artifact rather than re-running the scan.
Gotchas¶
pip-auditandnpm auditexit codes don't agree on what counts as a finding.pip-auditexits non-zero on any vulnerability;npm auditexits non-zero only when the vulnerability is at or above its--audit-levelthreshold (defaultlow). When the skill aggregates per-ecosystem results, treat exit-code parsing as a fallback signal; the JSON output is the source of truth.pnpm auditandyarn audituse different JSON shapes thannpm audit. A naïve "parsenpm audit --json" pipeline misses both. Detect the package manager from the lockfile (pnpm-lock.yaml/yarn.lock/package-lock.json) before choosing the audit invocation; don't fall back silently tonpm auditagainst apnpm-managed project, because the result will be a clean report based on a missingnode_modules/instead of a real audit.uv.lockaudits don't have a first-class CLI yet. When the project usesuv, runpip-audit -r requirements.txtafter exporting (uv export --no-hashes); auditinguv.lockdirectly produces no output. Document the export step in the report so the operator knows the audit was a derivative pass.- License-compliance scanners pull from external metadata (PyPI / npm registry); a transient registry outage produces a false "no findings" report rather than a clear error. Re-run on transient HTTP 5xx; only report
cleanwhen the run reached the registry successfully. - Direct vs transitive attribution requires the lockfile. Without the lockfile, the audit can only flag the surface that the manifest declares; transitive vulnerabilities don't surface. The skill stops and reports when no lockfile is present rather than producing a misleading direct-only report.
Examples¶
- Read
examples/01-python-pip-audit.mdwhen running the first audit on a Python project. - Read
examples/02-node-pnpm-audit.mdwhen auditing a Node.js project managed with pnpm. - Read
examples/03-multi-ecosystem-monorepo.mdwhen the repository contains multiple language ecosystems.
Resumability¶
Per spec/claude/resumable-work/, this skill is resumable: true. State is persisted to .resume/dependency-audit/<run-id>.yml after every successful user-approval gate and after each named phase boundary. On re-invocation, scan that directory for files with status: in_progress whose inputs: snapshot matches the current invocation; if one matches, prompt the operator with Resume run <run_id> from phase <phase> (last checkpoint <last_checkpoint_at>)? [resume / start-new / discard]. The state-file envelope (schema_version, run_id, inputs, phase, decisions[], status, ...) and the fail-closed semantics on schema or YAML errors are load-bearing in the spec; don't duplicate those rules here.
Source triangulation¶
Per spec/claude/research-triangulate/, before this skill presents any repo-external assertion that goes beyond the auditor's own machine-readable output — a recommended fix version, an interpretation of an advisory's affected range, or a licence's compatibility — triangulate it instead of trusting a single source:
- Independent sources by blast radius. At least two independent sources; at least three (the Release/dispatch tier) when the assertion will direct a write outside the working copy (a version pin, a sister-repo path, a third-party API signature, an external tool default).
- Record provenance. For every source record the URL or path, the source class, and the retrieval date in the audit artifact's source list; at least one source SHOULD carry a verifiable date so a stale advisory or pin is detectable.
- Surface conflicts, never silent-vote. When sources disagree, name the most likely explanation and let the operator decide; never apply a majority vote or auto-pick by source class.
- Mark
unverifiedwhen under-triangulated. If the required source count is unreachable, mark the assertionunverifiedand hand back to the operator; in an autonomous run with no reachable operator, abort the write and persist the conflict as a findings report.
Hard rules¶
- Never modify dependency manifests, lockfiles, or ignore lists without explicit user confirmation. This skill reports; mutations are a follow-up step.
- Never upgrade dependencies autonomously, even when a fix version is obvious. That's an author decision with test-suite consequences.
- Never silently skip an auditor that isn't installed. Emit an install hint and record the skip in the
Healthsection, and report that subroot's verdict asblocked, neverpass— the gate fails closed on an un-auditable subroot. - Always treat an
unknown-severity finding ashighuntil it is classified otherwise; never let an unclassified finding fall into a lower tier. - Never invent a license policy when the project has no allowlist. Flag findings as
review, not as failure. - Never report findings below the requested severity floor. Keep the report signal-heavy.
- Always prefer a repository-declared Taskfile target over invoking auditors directly, when one exists and wraps the same auditor. This honours any project-specific ignore list the Taskfile applies.
- Always attribute every finding to the subroot whose manifest caused it, so consumers with monorepos can act on the right team / package.
- Always sort findings deterministically (severity then package name) so the report diffs cleanly.
- When
spec/project/dependency-audit/and this skill disagree, the spec wins; this skill needs the update.
Why this is a skill, not an agent¶
This skill follows the hybrid pattern: the read-only scan phase is delegated to the dependency-audit-scanner agent (context-window isolation, tool restriction), while orchestration and follow-up actions stay in the skill.
- Orchestration role: typical callers run this as one step inside a larger flow (pre-PR gate, release cut, periodic security review); the output flows back into the main conversation so the user can triage.
- Interactivity: the follow-up actions in Step 6 need user approval — bumping a dependency, adding an advisory ignore entry, drafting a license allowlist — so mid-flow interactivity favours the skill side.
- Hybrid split: the pure scanning half (detect lockfile, run auditor, normalise JSON) is self-contained and benefits from context-window isolation; the
dependency-audit-scanneragent handles it. The follow-up-action half stays here so the user can approve each change interactively. - Counter-dimension: the self-contained, summary-returning shape of the scan phase points toward a pure agent, and its verbose JSON output is exactly the context-window pressure that favours isolation. That pull is honoured — but only for the scan half, which is delegated to
dependency-audit-scanner. The interactivity of the follow-up actions outweighs it for the capability as a whole, so the orchestrating surface stays a skill.