# Plan Vulnerability Remediation

> plan safe, idempotent remediation work for known dependency vulnerabilities. use when asked to triage CVEs, dependency alerts, security advisories, Dependabot alerts, or npm/yarn/pnpm/pip/bundler/cargo/go audit findings, and to group them into remediation PR plans before changing code. for CodeQL or code scanning alerts, use plan-code-scanning-remediation instead.

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

---


# Plan Vulnerability Remediation

## Overview

Create an actionable, idempotent remediation plan for known **dependency** vulnerabilities before making changes. The plan consumes a stack of vulnerability findings from dependency scanners and GitHub Dependabot alerts, determines whether each finding is already addressed by an existing open PR or by the current dependency state, groups closely related vulnerabilities that can be fixed together, and emits one or more PR-ready remediation plans.

This skill is designed to pair with the `remediate-vulnerability` skill. Use this skill first when the user asks for automation, batching, triage, grouping, or a plan. Use `remediate-vulnerability` when it is time to implement one plan and open or update a PR.

For **code scanning alerts** (CodeQL, SAST), use `plan-code-scanning-remediation` instead.

## Goals

- Be idempotent: rerunning should not create duplicate branches or PRs for the same vulnerability set.
- Prefer the smallest safe remediation that resolves the vulnerability.
- Group vulnerabilities only when doing so creates one coherent dependency change.
- Detect existing remediation PRs before starting new work.
- Produce enough structured information for an automated loop to process one plan at a time.
- Track longer-running remediation progress in a repository-local markdown file that can be read by later invocations.

## Inputs

Accept vulnerability findings from any of these sources:

- User-provided CVE IDs, GHSA IDs, package names, versions, ecosystem names, severity, and advisory URLs.
- GitHub Dependabot alerts via `gh api` or `gh dependabot` if available.
- Dependency audit commands such as `npm audit --json`, `pnpm audit --json`, `yarn npm audit --json`, `pip-audit --format json`, `bundle audit`, `cargo audit --json`, `govulncheck`, or ecosystem-specific scanners.
- Security tracking issues or files in the repository.

Each finding should be normalized to these fields when possible:

- `id`: CVE, GHSA, or scanner advisory identifier.
- `ecosystem`: npm, pip, bundler, cargo, gomod, maven, gradle, nuget, docker, github-actions, etc.
- `package`: affected package or component name.
- `current_version`: vulnerable installed or resolved version.
- `fixed_versions`: known safe version range(s), if available.
- `severity`: critical, high, moderate, low, or unknown.
- `source`: where the finding came from.
- `manifest_paths`: package manifests likely affected.
- `lockfile_paths`: lockfiles likely affected.

## Safety rules

- Do not change source code, manifests, lockfiles, dependency files, or branches in this planning skill unless the user explicitly asks you to proceed with remediation.
- It is acceptable to create or update the progress tracker at `docs/tmp/vulnerability-remediation.md` when planning a longer-running or automated remediation run. This tracker is the handoff artifact between planning and remediation.
- Treat vulnerability scanner output as advisory, not absolute truth. Verify against manifests, lockfiles, and installed dependency graphs when possible.
- Do not ignore a finding merely because a package name is absent from a top-level manifest; it may be transitive.
- Do not group unrelated major upgrades, unrelated ecosystems, or unrelated application code changes into one PR unless the user explicitly requests broad batching.
- Do not expose secrets from scanner output, environment files, package registries, or CI logs.
- Prefer read-only commands until the plan is accepted.

## Workflow

### 1. Establish repository and tooling context

Identify the repository root and available package managers. Use read-only inspection first:

```bash
git status --short --branch
git remote -v
git branch --show-current
find . -maxdepth 3 \( -name package.json -o -name package-lock.json -o -name pnpm-lock.yaml -o -name yarn.lock -o -name pyproject.toml -o -name requirements.txt -o -name Pipfile.lock -o -name Gemfile.lock -o -name Cargo.toml -o -name Cargo.lock -o -name go.mod -o -name go.sum -o -name pom.xml -o -name build.gradle -o -name build.gradle.kts -o -name packages.lock.json -o -name composer.lock -o -name Dockerfile -o -name '*.csproj' -o -name '.github' \) -print
```

Determine:

- Base branch (`main`, `master`, or repository default from `gh repo view --json defaultBranchRef` when available).
- Package ecosystems present.
- Monorepo package boundaries.
- Existing dependency update conventions such as Dependabot config, Renovate config, lockfile policy, and CI commands.

### 2. Gather vulnerability findings

If the user supplied specific findings, normalize those first.

If the user asks you to pull from GitHub, verify `gh` is available and authenticated:

```bash
gh --version
gh auth status
```

Then list open Dependabot alerts with useful fields:

```bash
gh api repos/:owner/:repo/dependabot/alerts --paginate --jq '.[] | {number, state, dependency: .dependency, security_advisory: .security_advisory, security_vulnerability: .security_vulnerability, html_url, created_at, fixed_at, dismissed_at}'
```

If local audit commands are appropriate, select commands based on discovered manifests. Prefer JSON output when available. Use timeouts for expensive commands. Examples:

```bash
npm audit --json
pnpm audit --json
yarn npm audit --json
pip-audit --format json
bundle audit
cargo audit --json
govulncheck ./...
```

If scanners require missing tools or credentials, note that in the plan instead of inventing results.

### 3. Check whether each finding is already resolved locally

For each finding:

1. Inspect relevant manifests and lockfiles to find the resolved version.
2. Compare the resolved version against the fixed version range.
3. If the resolved version is already fixed on the current branch, mark the finding as `already-fixed-locally` and do not plan a PR for it.
4. If the package is absent, determine whether it is transitive and whether the dependency graph still includes it.
5. If the scanner result is stale or cannot be reproduced, mark it as `needs-verification` and include the exact verification gap.

Useful read-only commands by ecosystem:

```bash
npm ls <package> --all
pnpm why <package>
yarn why <package>
pip show <package>
pipdeptree --packages <package>
bundle info <gem>
cargo tree -i <crate>
go mod why -m <module>
mvn dependency:tree -Dincludes=<groupId>:<artifactId>
```

### 4. Check for existing open PRs that already address findings

Before proposing a new remediation PR, search open PRs. Use multiple signals because branch names and titles vary.

Fetch open PR metadata:

```bash
gh pr list --state open --limit 100 --json number,title,headRefName,baseRefName,body,labels,url,updatedAt
```

For each finding or candidate group, look for:

- CVE or GHSA ID in title, body, branch name, commits, or labels.
- Affected package name in title, body, branch name, or changed files.
- Dependabot or Renovate branches that update the affected package.
- Branch names created by this automation. Use the convention `security/<ecosystem>-<slug>-<advisory-or-package>`.

Inspect likely PRs before deciding they match:

```bash
gh pr view <number> --json number,title,body,headRefName,baseRefName,state,mergeable,url

gh pr diff <number> --name-only

gh pr diff <number> -- <manifest-or-lockfile-path>
```

A PR counts as already addressing a finding only if its diff updates the vulnerable package, removes the vulnerable dependency path, or otherwise implements a credible mitigation. If uncertain, classify as `possibly-covered-by-pr` rather than creating a duplicate.

### 5. Group findings into remediation units

Create candidate remediation groups. Group findings when all or most of these are true:

- Same package, same ecosystem, or same dependency family.
- Same manifest and lockfile.
- Same direct dependency upgrade resolves multiple CVEs.
- Same transitive parent dependency upgrade resolves multiple CVEs.
- Same toolchain update, base image update, or GitHub Action version bump resolves multiple advisories.
- The upgrade path is small and coherent, such as one patch/minor version bump or one package family bump.

Do not group findings when any of these are true unless explicitly requested:

- Different ecosystems or unrelated manifests.
- Multiple major upgrades with separate migration risks.
- One finding needs application code changes while another only needs a lockfile update.
- Fixes have conflicting version constraints.
- Combining would make ownership, review, or rollback unclear.

Group priority:

1. Critical and high severity exploitable findings.
2. Findings with known fixes and low upgrade risk.
3. Multiple findings resolved by one direct dependency update.
4. Findings already covered by existing PRs should be marked and excluded from new work.

### 6. Define a remediation plan for each group

For every proposed group, include:

- `group_id`: stable slug such as `npm-express-cve-2024-12345` or `pip-django-ghsa-abcd-1234`.
- `findings`: advisory IDs included.
- `ecosystem` and package manager.
- `affected_packages` and current versions.
- `target_versions` or target dependency constraints.
- `manifest_paths` and `lockfile_paths` to change.
- `branch_name`: stable and deterministic, e.g. `security/npm-express-cve-2024-12345`.
- `pr_title`: e.g. `fix(security): update express to address CVE-2024-12345`.
- `remediation_strategy`: direct upgrade, transitive parent upgrade, lockfile refresh, replace dependency, configuration mitigation, code mitigation, base image update, or upstream PR already exists.
- `commands_to_run`: package-manager commands expected to update only the intended files.
- `verification_commands`: audit and test commands that should prove remediation.
- `existing_pr`: PR URL or number if already covered.
- `risk_notes`: major version changes, peer dependency risks, migrations, or uncertainty.
- `automation_status`: one of `ready`, `in-progress`, `pr-opened`, `covered-by-existing-pr`, `already-fixed-locally`, `needs-user-input`, `needs-verification`, `blocked`, or `done`.
- `progress_notes`: concise notes about decisions, blockers, or verification gaps.
- `last_updated`: timestamp or date if known.

### 7. Create or update the progress tracker

For longer-running tasks, automated loops, or when the user asks for persistent tracking, create or update this repository-local file:

```text
docs/tmp/vulnerability-remediation.md
```

If `docs/tmp` does not exist, create it. This file is the explicit handoff mechanism between `plan-vulnerability-remediation` and `remediate-vulnerability`. It should be committed only if the user wants task-tracking docs in the PR; otherwise it may remain as local working notes. Do not include sensitive scanner output.

Use this structure:

```markdown
# Vulnerability Remediation Tracker

## Run metadata

- Started: <date/time if known>
- Repository: <owner/repo or remote URL>
- Base branch: <base branch>
- Sources: <Dependabot alerts, npm audit, user-provided CVEs, etc.>
- Tracker version: 1

## Status summary

- Ready: <count>
- In progress: <count>
- PR opened: <count>
- Covered by existing PR: <count>
- Already fixed locally: <count>
- Needs input: <count>
- Blocked: <count>
- Done: <count>

## Remediation queue

### <group_id>

- Status: ready
- Branch: security/<group_id>
- PR: <none or URL>
- Findings: <CVE/GHSA IDs>
- Ecosystem: <ecosystem>
- Packages: <package current -> target>
- Manifests: <paths>
- Lockfiles: <paths>
- Strategy: <direct upgrade/transitive parent upgrade/etc.>
- Commands:
  - `<command>`
- Verification:
  - `<command>`
- Risk notes: <notes>
- Progress notes:
  - <timestamp or date> — planned remediation group
```

Keep each remediation group under a stable `### <group_id>` heading so later runs can find and update it. Preserve existing notes and append new progress notes instead of replacing history.

When rerunning the planner:

1. Read the existing tracker first if it exists.
2. Preserve groups that are `in-progress`, `pr-opened`, `blocked`, or `done` unless the current repository state proves they should change.
3. Add newly discovered groups.
4. Update statuses for findings that are now covered by PRs or fixed locally.
5. Avoid reordering groups unless doing so materially improves priority.

### 8. Handoff contract for `remediate-vulnerability`

There are two supported handoff modes:

1. **Conversation handoff** — output a full group plan in the response and then invoke `remediate-vulnerability` with that group in the conversation context.
2. **Tracker-file handoff** — write the group plan into `docs/tmp/vulnerability-remediation.md`; `remediate-vulnerability` reads the next `Status: ready` group from the tracker and updates that same section as it works.

For automated or long-running work, prefer tracker-file handoff. The tracker is the durable queue and audit log. `remediate-vulnerability` should process exactly one `ready` group per invocation, then update that group's status and notes.

### 9. Output an automation-friendly summary

When the user wants a loop-friendly output, end with a concise queue:

```text
READY:
1. <group_id> — <branch_name> — <short remediation summary>

SKIP:
1. <finding id> — already fixed locally
2. <finding id> — covered by <PR URL>

NEEDS INPUT:
1. <group_id> — reason
```

If the user asks for JSON, produce valid JSON with an array of remediation groups and no markdown decorations.

## Idempotency contract

A subsequent run should make the same decision for the same repository state. Preserve idempotency by using:

- Stable `group_id` values derived from ecosystem, package, and advisory IDs.
- Stable branch names derived from `group_id`.
- PR titles containing `fix(security)` plus package and advisory identifiers when available.
- PR bodies containing a machine-readable marker:

```text
<!-- vulnerability-remediation: group_id=<group_id>; findings=<comma-separated ids>; packages=<comma-separated packages> -->
```

On rerun, search for this marker in open PR bodies before creating any new plan. Also read `docs/tmp/vulnerability-remediation.md` when present and reconcile tracker state with live repository and PR state.

## When to hand off to `remediate-vulnerability`

Use `remediate-vulnerability` for each plan with `automation_status: ready` or tracker `Status: ready`.

If using conversation handoff, pass along:

- Full group plan.
- Existing branch name if present.
- Exact files to change.
- Exact commands to run.
- Verification commands.
- Existing PR markers to reuse.

If using tracker-file handoff, tell `remediate-vulnerability` to read `docs/tmp/vulnerability-remediation.md` and process the next `Status: ready` group. The tracker section for that group is the durable handoff record.

## Final response

Report:

- Number of findings examined.
- Path to the progress tracker if created or updated.
- Findings already fixed locally.
- Findings covered by existing PRs.
- Proposed remediation groups ready for implementation.
- Findings that need user input or manual verification.
- The next `group_id` that `remediate-vulnerability` should process, if any.
- Recommended next command or next skill invocation.

