Vulnerable Dependency Remediation
Scope. Third-party dependencies only (SCA findings). First-party code vulnerabilities (SAST) go through a separate workflow because the fix shape is wildly different.
Rechecked August 23, 2026: do not invent a fixed version. A named
floor is only usable when live GHAD, NVD, or vendor text still
publishes it. Live GitHub Advisory pages can 404, GHSA
vulnerabilities can be empty, and NVD can return no row.
Leftover CVE dumps stay development / noindex and are not
catalog overrides. Platform-assigned Dependabot-to-agent work is
generally available for Copilot, Claude, and Codex, and still
requires GitHub Code Security plus a Copilot plan with coding-agent
access. That is not the same as one-step Autofix.
What problem this solves
A typical mid-sized repo sees 5–20 new dependency advisories per month. Most of them are routine version bumps — the advisory reports the affected range, there’s a patched version, and the lockfile just needs updating. Historically this work piled up and lost its urgency; by the time someone got to it, three more had landed. This workflow drains the easy cases automatically so the humans only see the hard ones.
Workflow at a glance
Vulnerable Dependency Remediation workflow
Resolve one SCA advisory through the package manager while preserving lockfile integrity and proving the vulnerable version is gone.
Signal
Normalize the advisory
Capture CVE/GHSA/OSV identity, affected range, patched versions, ecosystem, manifest, and lockfile.
Scope
Resolve the installed path
Confirm the affected package and version are present and identify direct, transitive, workspace, or image ownership.
Decision
Select the lowest safe target
Choose a non-yanked patched version and apply policy for major, transitive, or no-fix cases.
Action
Update through the resolver
Run the native package manager against the smallest manifest scope and preserve unrelated lock entries.
Proof
Test and rescan
Run project checks, inspect the resolved graph, and show the affected version and advisory are absent.
Decision gate
Can a policy-approved target be reproduced without an unreviewed major or architecture change?
Prepare the minimal manifest/lockfile update with proof.
Stop for triage on absent packages, malicious rollback advisories, no-fix cases, or unreproducible resolution.
Evidence to retain
- advisory and affected range
- resolved versions before and after
- tests and SCA re-scan
Expected outputs
- dependency update packet
- major/transitive triage note
- agent handoff
High-level flow
flowchart LR
A[CVE feed / Dependabot] -->|advisory| B[Orchestrator]
B --> C{Classifier}
C -->|direct, patch in range| D[Agent: bump]
C -->|transitive| E{Policy engine}
E -->|auto-ok| D
E -->|needs human| F[Triage queue]
C -->|major bump required| F
D --> G[Sandbox: apply bump]
G --> H[Run tests]
H -->|pass| I[PR: auto-remediation label]
H -->|fail| J[Revert + TRIAGE.md]
I --> K[Human reviewer]
K -->|approve| L[Merge]
Intake paths
Most programs start with one intake path and grow into more. Two shapes are worth naming explicitly because they have different orchestration implications:
- Orchestrator-pulled. Your orchestrator polls the advisory feed (CVE / OSV / vendor SCA API), classifies, and dispatches the agent. You own the identity, the queue, the rate limits, and the audit trail end-to-end. This is the shape the rest of this workflow assumes.
- Platform-assigned. The code-hosting platform exposes an “assign to agent” action directly on the native alert UI — Dependabot alerts assignable to Copilot, Claude, or Codex (2026) is the representative example; expect equivalents on GitLab, Bitbucket, and the SCA vendors. The platform spawns the run, opens the draft PR, and ties it back to the alert. No orchestrator required.
If you run both, treat platform-assigned as the fallback path — it catches anything that falls off the main queue and stays useful as a manual-escalation lever for reviewers — and make sure the two audit trails land in a shared stream. “Which agent, on whose authority, touched this PR?” should have one answer, not two.
What ‘eligible’ means
The classifier hands a finding to the agent only when:
- The advisory specifies a live named floor (not “no fix yet,” leftover dump version text, or a guessed next tag).
- The affected package is present in the repo’s lockfile.
- The patched version does not cross a major-version boundary on a direct dependency. Major bumps require a human.
- The repo has a passing CI pipeline and a
make test(or equivalent) target the agent can invoke.
Everything else routes to the triage queue.
What the agent does
sequenceDiagram
participant O as Orchestrator
participant A as Agent
participant S as Sandbox
participant R as Registry
O->>A: advisory + repo snapshot
A->>R: look up patched range
A->>S: locate package in lockfile
A->>S: invoke package manager<br/>(npm / poetry / go)
S-->>A: new lockfile
A->>S: run tests
alt tests pass
A->>O: PR<br/>"bump pkg to X.Y.Z (CVE-...)"
else tests fail
A->>S: revert bump
A->>O: TRIAGE.md<br/>+ failing test names
end
Run this workflow with your AI coding agent
This playbook defines the shared eligibility, verification, and review model. Choose the agent-ready recipe that matches the tool already approved for your repository; each recipe adapts the same bounded dependency-remediation contract to that agent’s native instructions and execution model.
Need to identify or verify the advisory first? Search the CVE Database before selecting a recipe.
Per-ecosystem notes
- Node. Uses
npm,pnpm, oryarndepending on the lockfile present. Never hand-editspackage-lock.json. - Python. Uses
poetry,uv, orpip-compiledepending on the project. Hand-editingrequirements.txtis allowed only when no resolver is configured. - Go. Uses
go getthengo mod tidy. Never editsgo.sumdirectly. - Monorepos. Each lockfile gets its own PR. No cross-lockfile bundling — reviewers need diffs they can reason about in isolation.
Guardrails
- One CVE, one PR. Bundling multiple fixes masks which bump
caused a regression. One is the rule, even if it creates more
PRs. Tag each PR with an auto-remediation label — the site uses
sec-auto-remediationas the illustrative example; rename to your org’s convention. - No code edits outside the lockfile. If a bump requires a code change, the agent stops — that’s a human call.
- No CI skip tokens. The agent will never add
[skip ci],[skip test], or similar. If CI fails, the change fails. - Quota per repo. A per-repo cap on open agent PRs prevents the reviewer queue from becoming a firehose.
- Yanked version guard. The registry is re-queried just before PR open; if the patched version was yanked, the agent stops and writes a triage note.
- Malicious-package downgrade path. “Bump to the latest patched version” is the wrong default when the advisory itself is that the package was compromised (maintainer account takeover, poisoned release, self-propagating worm in the supply chain). In those cases the remediation is a pin backwards to a known-good version — or an eject to a fork — not a forward bump. The classifier should recognise the advisory shape (“malicious code in versions X and later”) and route the agent to a downgrade workflow; if the affected version is the only published version, escalate to a human. See Threat Model → Agent-infrastructure supply-chain compromise for why this case is worth calling out separately.
What it won’t catch
- Transitive deps behind a pin — when a direct dep pins the vulnerable transitive by exact version, the bump requires the direct dep to update first. Routed to triage.
- Private registries the orchestrator hasn’t been granted read access to.
- Native modules that build on the CI runner but fail in prod (different ABI, different glibc). Reviewer checklist flags these; the agent doesn’t detect them.
- Embargoed / pre-disclosure CVEs — these go through an out-of-band process; the automated feed doesn’t see them.
How this workflow evolves
Same principle as the other workflows: orchestration is constant, inputs evolve.
- Prompt. The triage heuristics (when to stop, how to format the PR body) get tuned from reviewer pushback.
- Model. Upgraded when a newer model measurably improves precision on the team’s labelled CVE set.
- Tools. New ecosystem connectors (e.g. Rust
Cargo.lock, PHPcomposer.lock) plug in as MCP servers without touching the orchestrator.
Changelog
- 2026-04-21 — v1 reference workflow, covers Node, Python, and Go. Rust and PHP are typical next-quarter extensions for teams adopting this pattern.