# Loop Cve Audit

> Iterative dependency-CVE remediation loop: scan with ecosystem-native advisory tooling, assess whether each high/critical finding is actually reachable (with call-path evidence, not vibes), fix the highest-risk reachable one with the smallest credible change, re-verify, and repeat. Terminates when no exploitable high/critical CVE remains or every remaining finding has an evidence-backed reachability assessment and an approved risk decision. Use when the user mentions CVEs, vulnerability scanning, dependency security, npm/pip/cargo audit, security patching, or wants a recurring dependency-security loop.

- Skill: `tomimor/loop-cve-audit` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tomimor/loop-cve-audit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomimor/loop-cve-audit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: tomimor (https://skillmd.com/u/tomimor)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomimor/loop-cve-audit

---


# Loop: CVE Audit

Drive the current repository's dependencies to a defensible security
state, one verified fix at a time. This is a **loop skill**: it runs in
iterations with explicit terminal states, and it is designed to be
re-run on a schedule (new CVEs appear over time — this loop never
"finishes" permanently).

## Loop contract

The loop ends only in one of these states. Every run must report which
one it reached.

| Terminal state | Meaning |
|---|---|
| **CLEAN** | No exploitable high or critical CVE remains. |
| **ASSESSED** | High/critical findings remain, but every one has evidence-backed reachability analysis and a user-approved risk decision. |
| **BLOCKED** | A precondition failed (no dependency graph, no advisory access, broken build baseline). Reported, nothing changed. |

Hard rules for the whole loop:

- **Never hide findings.** No adding entries to audit ignore files,
  `osv-scanner.toml` exclusions, `--exclude` flags, severity threshold
  changes, or deleting a dependency's usage just to silence a finding.
  The only two ways a finding leaves the list are: a verified fix, or a
  user-approved risk decision.
- **"Not reachable" is a claim that requires evidence** (format below).
  It is never the default.
- **New CVEs introduced by an upgrade count as findings** and go back
  into the queue. Verify the scan after every change, not just at the end.
- **Preserve unrelated work.** Never commit, revert, or stash changes
  that existed before the loop started.

## Phase 0 — Preconditions (fail fast)

Before touching anything, verify you can actually do the job:

1. The repo has a resolvable dependency graph (lockfile or manifest
   present: `package-lock.json`/`pnpm-lock.yaml`/`yarn.lock`,
   `poetry.lock`/`requirements.txt`, `Cargo.lock`, `go.mod`, etc.).
2. An advisory-backed scanner runs successfully (see Phase 1).
3. The baseline is green: build and tests pass **before** any change.
   If they don't, you cannot attribute regressions to your fixes.

If any of these fail → terminal state **BLOCKED**. Report exactly what
is missing and stop. Do not scan from memory; do not guess CVEs.

## Phase 1 — Baseline scan

Use the ecosystem-native tool, falling back to `osv-scanner` (covers
all ecosystems via OSV.dev):

| Ecosystem | Primary tool |
|---|---|
| npm/pnpm/yarn | `npm audit --json` (or `pnpm audit --json`) |
| Python | `pip-audit` (or `uv pip audit`) |
| Rust | `cargo audit` |
| Go | `govulncheck ./...` |
| Any / multi | `osv-scanner --lockfile <path>` |

Record in the baseline (this anchors the whole run — the goalposts
must not move mid-loop):

- Tool name and version, advisory source, and scan timestamp.
- Full list of high/critical findings: CVE/GHSA id, package, installed
  version, direct or transitive, fixed-in version if any.

Low/medium findings are noted in the final report but are **not** loop
targets — chasing them is how this loop loses focus.

## Phase 2 — Reachability triage

For each high/critical finding, determine whether the vulnerable code
is actually reachable in this project. This is the highest-judgment
step and also the loop's cheat vector — declaring things unreachable is
the lazy path to termination, so the evidence bar is strict.

A reachability assessment must contain **all** of:

1. **Vulnerable symbol(s)**: which function/class/behavior the advisory
   actually concerns (read the advisory; the package name is not enough).
2. **Import path**: where this project (or a dependency on the path to
   it) imports the affected module — cite `file:line`, or show the
   transitive chain (`A → B → vulnerable-pkg`).
3. **Verdict with proof**:
   - *Reachable*: a concrete call path from project code to the
     vulnerable symbol, or the exploit precondition shown to exist
     (e.g. the vulnerable parser is fed request data).
   - *Not reachable*: why the call path cannot exist — the vulnerable
     symbol is never invoked (show the search), the feature is disabled,
     the dependency is dev/build-only and never ships, or the exploit
     precondition is structurally absent.

Prose like "this doesn't appear to be used" without a cited search or
call chain **does not qualify** and must be treated as *reachable* by
default. `govulncheck` does call-path analysis natively for Go — prefer
its verdict there.

## Phase 3 — Ranking

Rank the reachable findings by, in order: severity, reachability
confidence, exposure (does it face untrusted input / production?), and
availability of a remediation (patched version exists, upgrade
distance). Work strictly from the top of this list.

## Phase 4 — Remediate (one finding per iteration)

For the highest-ranked finding, apply the **smallest credible change**:

1. Prefer, in order: patch-level bump of the affected package →
   minor bump → lockfile-level override/resolution to force a fixed
   transitive version → direct-dependency upgrade that pulls the fix →
   replacing the dependency (ask first).
2. One finding per iteration. Do not batch unrelated upgrades — when
   verification fails you must know which change caused it.
3. **Two-attempt cap**: if two remediation attempts for the same
   finding fail verification, stop trying. Revert to the last green
   state and route the finding to the risk-decision path (Phase 6)
   with the failed attempts documented.

## Phase 5 — Verify

After every change:

1. Install/lockfile update completes cleanly.
2. Build passes. Tests pass. (Same commands as the Phase 0 baseline.)
3. Re-run the scan **with the pinned baseline tool and version**:
   the finding is gone and no new high/critical finding appeared.

Keep the change only if all three hold; otherwise revert and count the
attempt. Then return to Phase 3 with the updated finding list.

## Phase 6 — Approval gates

Stop and ask the user (do not proceed on your own) before:

- Major-version or otherwise breaking upgrades.
- Replacing or removing a dependency.
- Anything that changes production config or deployment.
- **Accepting risk** on any remaining finding. Present the reachability
  evidence and a recommendation; the user makes the risk decision.

## Final report

Every run ends with, regardless of terminal state:

1. **Terminal state** reached (CLEAN / ASSESSED / BLOCKED).
2. **Inventory**: every high/critical finding from the baseline and its
   outcome (fixed in iteration N / risk-accepted / blocked), plus the
   low/medium list for awareness.
3. **Reachability evidence** for every finding assessed not-reachable
   or risk-accepted, in the Phase 2 format.
4. **Fixes applied**: package, old → new version, and the verification
   results (build/test/scan output summary) proving each.
5. **Remaining risks** and recommended follow-ups.
6. Scanner name, version, and scan timestamps (baseline and final).

Claims without fresh command output are not proof. Show the re-scan.

## Running it on a schedule

This loop is a natural recurring job — new advisories are published
daily against unchanged code. Good triggers:

- Weekly (e.g. `/loop` or a cron/scheduled session invoking this skill).
- After any change to a lockfile or dependency manifest.

On a scheduled re-run, diff against the previous run's report: only new
findings (or findings whose risk decision has expired) create work.
Previously approved risk decisions stay approved unless the advisory's
severity or the project's exposure changed — re-litigate those, not
everything.

