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.

core-remediation
  1. Signal

    Normalize the advisory

    Capture CVE/GHSA/OSV identity, affected range, patched versions, ecosystem, manifest, and lockfile.

  2. Scope

    Resolve the installed path

    Confirm the affected package and version are present and identify direct, transitive, workspace, or image ownership.

  3. Decision

    Select the lowest safe target

    Choose a non-yanked patched version and apply policy for major, transitive, or no-fix cases.

  4. Action

    Update through the resolver

    Run the native package manager against the smallest manifest scope and preserve unrelated lock entries.

  5. 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?

Proceed

Prepare the minimal manifest/lockfile update with proof.

Hold or stop

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, or yarn depending on the lockfile present. Never hand-edits package-lock.json.
  • Python. Uses poetry, uv, or pip-compile depending on the project. Hand-editing requirements.txt is allowed only when no resolver is configured.
  • Go. Uses go get then go mod tidy. Never edits go.sum directly.
  • 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-remediation as 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, PHP composer.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.