skills/dependency-audit/SKILL.md
Ecosystem-appropriate dependency vulnerability audit. Walks the project for package.json, Cargo.toml, requirements.txt, pyproject.toml manifests and runs npm/cargo/pip-audit. Produces a structured supply-chain signal with normalized severities. Used by build alongside quality-gate; can also be invoked standalone. Triggers on /dependency-audit, 'audit dependencies', 'scan vulnerabilities', 'check CVEs'.
npx skillsauth add raddue/crucible dependency-auditInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Runs ecosystem-appropriate dependency audit commands and produces a normalized supply-chain signal. Independent of code review — produces a separate, parallel signal that callers (build, quality-gate, user) integrate as they choose.
Announce at start: "Running dependency audit on [project path]."
Skill type: Rigid — follow exactly, no shortcuts.
/dependency-audit directly to scan a project ad-hoc.skip_blocking (boolean, default: false) — Global override. When true, disables ALL blocking regardless of min_blocking_severity. Findings are still reported in audit-results.md but no blocking occurs and the result is FINDINGS (not BLOCKED). skip_blocking supersedes min_blocking_severity entirely — they do not interact as independent thresholds.
min_blocking_severity (string, default: "critical", case-insensitive) — The minimum normalized severity at which a finding triggers blocking. Accepted values: "critical", "high", "moderate", "low". Invalid values are rejected with an error before execution begins. This does not change what gets reported — all findings always appear in audit-results.md; it only affects whether the result is BLOCKED vs FINDINGS.
scope_root (string, default: cwd) — Directory to scan for manifests. Defaults to the current working directory.
Walk the directory tree from scope_root, collecting all manifest files matching the supported set:
| Manifest File | Ecosystem |
|---|---|
| package.json | Node.js |
| Cargo.toml | Rust |
| requirements.txt | Python |
| pyproject.toml | Python |
Excluded directories: node_modules/, .git/, target/, dist/, vendor/, third_party/, .venv/, venv/. These contain vendored or installed dependencies, not the project's own manifests.
Symlinks are not followed — following them risks infinite recursion in repos with circular symlinks or deeply nested node_modules.
npm workspace detection: Before scheduling per-directory npm audit runs, inspect each discovered package.json for a top-level "workspaces" field. If a workspace root is detected, schedule a single npm audit from that root directory. Do not schedule separate runs for package.json files in subdirectories that are members of that workspace.
Python dual-manifest handling: When a directory contains both requirements.txt and pyproject.toml, audit both. They may represent different dependency sets. Duplicate findings are deduplicated at result-write time in audit-results.md using the key (package name + CVE ID) — each unique (package, CVE) pair appears once with a note of which sources reported it. Version differences for the same (package, CVE) pair are noted but not double-counted.
Manifest list finalization: The manifest list is written to preflight-audit.md before any audit tool is invoked. This list is the authoritative scope for the run. If compaction occurs after this point, the audit resumes from the recorded list — it does not re-scan.
Zero manifests: If zero manifests are found anywhere in the tree, the audit completes as a no-op and notes this in the output summary.
Detected manifests are audited in fixed order for deterministic output: Node.js → Rust → Python.
| Manifest File | Audit Command | Notes |
|---|---|---|
| package.json | npm audit --json | Run from workspace root if applicable, otherwise cwd = manifest directory |
| Cargo.toml | cargo audit --json | Run with cwd = manifest directory |
| requirements.txt | pip-audit --format json -r requirements.txt | Explicit -r flag; does NOT require active venv |
| pyproject.toml | pip-audit --format json | Requires active venv or lockfile (see below) |
All detected manifests are audited independently (after workspace consolidation). Each runs as an isolated subprocess. A failure in one audit does not abort or skip audits for other manifests. All ecosystems run to completion before the overall result is computed — a BLOCKED result from one ecosystem does not short-circuit audits for remaining ecosystems.
Before invoking any audit tool, the skill checks availability:
| Case | Condition | Action |
|---|---|---|
| Available | Tool in PATH, environment ready | Run audit |
| Tool missing | Tool not in PATH | Write warning to audit-results.md, surface to user |
| Tool broken | Tool found but --version fails | Write warning, skip |
| Environment not ready | Tool found but required environment absent | Write specific reason, skip with warning |
Per-manifest environment readiness checks:
requirements.txt: pip-audit -r requirements.txt reads the file directly. No virtualenv required. Available if pip-audit is on PATH.pyproject.toml: pip-audit without -r inspects the installed environment. Requires an active virtualenv or a lockfile (poetry.lock, pdm.lock, uv.lock). If neither is present, skip with: "pip-audit requires a virtual environment or lock file for pyproject.toml; results would be unreliable."Cargo.toml: Requires Cargo.lock to be present. If absent: "skipped — Cargo.lock absent; run cargo generate-lockfile first."package.json: Requires package-lock.json (or npm-shrinkwrap.json) in the same directory (or workspace root). If absent: "skipped — no lockfile found; run npm install to generate package-lock.json." npm must be on PATH.Python manifest confidence: When only pyproject.toml is present (no requirements.txt or lockfile in the same directory), include a notice in audit-results.md: "Confidence: Reduced — No requirements.txt or lock file found. pip-audit is resolving dependencies from pyproject.toml directly. Results may be incomplete."
Tool availability results are written to audit-results.md (not preflight-audit.md), because they are discovered at execution time, not scan time.
A run where all manifests are skipped (missing tools or environment-not-ready) is reported as INCONCLUSIVE, not passing.
Audit tools exit non-zero for two distinct reasons:
Exit code contracts per tool:
| Tool | Clean | Findings | Error |
|---|---|---|---|
| npm audit | exit 0 | exit 1 | exit 2+ |
| cargo audit | exit 0 | exit 1 | exit 2+ |
| pip-audit | exit 0 | exit 1 | exit 2+ (or non-zero with unparseable stdout) |
Use exit codes to distinguish outcomes. Do not parse stderr substring content to classify results.
Audit tools use different severity vocabularies. The skill normalizes to a common scale. CVSS boundaries are inclusive on the lower bound, exclusive on the upper (e.g., a CVSS score of exactly 9.0 is Critical, not High).
| Level | npm audit | cargo audit | pip-audit |
|---|---|---|---|
| Critical | critical | CVSS >= 9.0 | CVSS >= 9.0 |
| High | high | CVSS >= 7.0 and < 9.0 | CVSS >= 7.0 and < 9.0 |
| Moderate | moderate | CVSS >= 4.0 and < 7.0 | CVSS >= 4.0 and < 7.0 |
| Low | low | CVSS >= 0.1 and < 4.0 | CVSS >= 0.1 and < 4.0 |
| Informational | — | CVSS = 0.0 | CVSS = 0.0 |
CVSS 0.0 findings are classified as Informational — reported in audit-results.md but never count toward blocking. They do not map to any blocking severity level.
If a finding has no CVSS score (advisory-only, no CVE assigned), it is treated as Moderate and flagged with [no-cvss] in the output.
The audit produces two files under its scratch directory scratch/<run-id>/:
preflight-audit.md — Scan-time plan. Written before any audit tool runs. Contains only scan-time information:
generated-at timestamp (ISO-8601)This file is not updated after execution begins. It is the immutable record of what the scan discovered.
audit-results.md — Execution-time output. Written incrementally as each ecosystem completes. Contains:
Each ecosystem section ends with a status: complete sentinel line. A section without this sentinel is considered incomplete and must be discarded and re-run on recovery.
Schema for audit-results.md:
# Dependency Audit
generated-at: <ISO-8601>
run-id: <run-id>
## Tool Availability
- npm audit: available
- cargo audit: available
- pip-audit (requirements.txt): available
- pip-audit (pyproject.toml): unavailable — no venv or lock file
## Summary
Result: CLEAN | FINDINGS | BLOCKED | INCONCLUSIVE | FAILED
Critical: N High: N Moderate: N Low: N Informational: N
## npm — packages/api/package.json — FINDINGS
[findings list: package, severity, CVE, fix-available]
status: complete
## pip — src/requirements.txt — FINDINGS
## pip — src/pyproject.toml — FINDINGS
[deduplicated: CVE-2024-XXXXX reported by both src/requirements.txt and src/pyproject.toml — counted once]
status: complete
## Warnings
[environment-not-ready, reduced-confidence, or deduplication notes]
When results span multiple manifests with mixed outcomes, the overall Result: field uses this precedence (highest wins):
| Priority | Result | Condition |
|---|---|---|
| 1 (highest) | BLOCKED | Findings at or above min_blocking_severity and skip_blocking is false |
| 2 | FINDINGS | At least one manifest returned vulnerability findings (below blocking threshold or override active) |
| 3 | INCONCLUSIVE | At least one manifest was skipped (tool missing, environment not ready); no findings |
| 4 | FAILED | At least one manifest tool errored; no findings and no skips |
| 5 (lowest) | CLEAN | All manifests completed without findings |
INCONCLUSIVE outranks FAILED because unknown coverage (a manifest exists but was never audited) is more dangerous than a known, retryable tool error.
When a finding at or above min_blocking_severity is present and skip_blocking is not true:
npm audit fix / cargo update away; no-fix blockers may require dependency replacement or acceptance.Result: BLOCKED and return to the parent orchestrator without prompting.Whether a session is interactive is a Claude Code runtime property, not something the skill detects via TTY heuristics or environment inspection.
Parent-pipeline integration: When the audit returns with Result: BLOCKED, the parent orchestrator (build, quality-gate caller, or direct user invocation) treats this as a blocking signal. The audit does not itself decide what blocking means downstream — it produces the result.
Read preflight-audit.md to recover the manifest list. Then check audit-results.md for completed ecosystem sections (those ending with status: complete sentinel). Sections without the sentinel are discarded as incomplete. Resume from the first manifest not yet present as a complete section. Recovery re-invokes the audit tool for incomplete manifests — no raw output is cached between compaction events. After all manifests complete, regenerate the Summary section of audit-results.md.
The generated-at timestamp marks when results were produced. Results are valid for that point in time only. The audit does not re-run after caller-initiated remediation. This is an explicit design boundary: an audit run is a point-in-time evaluation.
dependency-audit is invoked:
crucible:quality-gate on phase 4 (code-artifact gate). Both run in parallel; build's gate ledger integrates the results./dependency-audit./loop or /schedule for periodic supply-chain scans.dependency-audit does NOT invoke crucible:quality-gate or crucible:red-team. It produces its own signal; consumption is the caller's responsibility.
| Skill | Relationship |
|---|---|
| crucible:build | Primary caller. Invokes alongside crucible:quality-gate on code-artifact phases. |
| crucible:quality-gate | Sibling skill. Both produce independent signals on code artifacts; quality-gate consumes the same artifact for adversarial review while this skill audits its dependencies. |
| crucible:audit | Audit skill consumes dependency-audit findings as supporting context when auditing subsystems with dependency surface. |
testing
Standalone instance-bug reviewer — runs a parallel finder fan-out + verify gate over a diff or a path and prints ranked, verified findings. Use when the user says "delve", "find bugs in this diff", "review this for bugs", "scan this file/subsystem for defects", "instance-bug sweep", or wants concrete reproducible defects (not a merge verdict, not systemic health). Works on a PR id, a base..head range, or a path, on any forge (GitHub, GitLab, Bitbucket, self-hosted).
testing
Render the Crucible calibration ledger weekly report — the honest "Crucible caught N silent bugs" headline, verdict breakdown, per-skill severity rates, and the inflation detector. Triggers on "/ledger", "weekly report", "weekly ledger", "caught N", "quality ledger", "calibration report", "render the ledger".
development
The Book of Grudges — cross-session bug graveyard. Every fixed bug is recorded as a structured "grudge"; before touching code, skills query the grudgebook for the files in scope and surface past regressions as forced "DO NOT REPEAT" context. Read mode (pre-flight) and write mode (on bug resolution / fix(*) PR). Machine-local, per-repo, never committed. Triggers on /grudge, "check grudges", "record a grudge", "any past bugs here", "regression oracle", "bug graveyard".
testing
Reconcile the Crucible calibration ledger — walk merged fix/hotfix branches to falsify the originating gating-verdicts, compute per-skill Brier calibration scores, and append a falsification log. Triggers on "/calibration-reconcile", "reconcile ledger", "reconcile calibration", "falsify verdicts", "brier score", "calibration reconcile", "compute brier".