Vulnerable dependency remediation (issue template + instructions)
A three-part bundle — a .github/copilot-instructions.md
addendum, a GitHub issue template, and the issue body that gets
created when a scanner projects a finding. Together they shape
the Copilot coding agent into a narrow, reviewable dependency
remediator: one finding per issue, one draft PR per issue, no
auto-merge.
What this prompt does
When the scanner (or a developer) creates an issue from the
security-remediation-dep.yml template and labels it
copilot-remediate, the Copilot coding agent picks it up, reads
the repository-level instructions, applies the minimum viable
version bump in the affected manifest + lockfile, runs CI, and
opens a draft PR linked back to the issue. The agent respects
the house rules — no major bumps, no lockfile-only edits, no
disabling of tests.
Inputs: finding id + affected package (from the issue body),
repository-level instructions (from .github/copilot-instructions.md).
Outputs: a draft PR linked to the issue, passing CI, and a
human reviewer assigned via CODEOWNERS.
When to use it
- You want the shortest-setup remediation path. GitHub-native, no separate orchestration layer.
- Your scanner already supports projecting findings into GitHub Issues (CodeQL, Snyk, Semgrep, Dependabot alerts converted to issues).
- You have branch protection on
mainthat requires review and green CI, so the “agent opens a draft PR” pattern is safe.
Don’t use it for:
- Major version migrations — the repository instructions refuse.
- Cross-repo fanouts — each repo needs its own label + workflow; this prompt is per-repo.
- First-party SAST findings — use the SDE remediation recipe.
The prompt
Three files, checked in to the repo.
.github/copilot-instructions.md — dependency remediation addendum
Append this to your existing copilot-instructions.md:
## Vulnerable dependency remediation
When working on an issue labeled `copilot-remediate` whose body
includes a finding id (CVE- / GHSA-), follow these rules.
### Scope
- ONE finding per PR. Never bundle multiple advisories.
- Branch: `copilot/<finding-id>`.
- Commit: Conventional Commits: `fix(sec): bump <pkg> from <old>
to <new> (<finding-id>)`.
### Version bump policy
- Pick the LOWEST version in the advisory's patched range.
- NEVER bump across a major-version boundary. If the only fix
is a major bump, post a comment on the issue explaining why
(direct dep vs transitive, breaking changes expected) and
stop — do not push a PR.
- Pre-release / rc / beta versions are off by default. If the
advisory lists only a pre-release fix, comment on the issue
and stop.
### Tooling
- Use the native package manager to apply the bump. Never
hand-edit the lockfile.
- After the bump, run the lint and test commands documented at
the top of this file. If they fail because of the bump,
revert, comment on the issue with the failing tests, and stop.
### Paths you may NOT touch
- `db/migrations/**` — any DB migration.
- `infra/terraform/**` — infra-as-code.
- `**/*.generated.*` — generated code.
- Any CI workflow, except to update a pinned action version in
response to a CVE on that action.
### PR shape
- Title: `fix(sec): bump <pkg> to <ver> (<finding-id>)`.
- Body: link the issue (`Closes #NNN`), finding id + link to
advisory, old → new version, direct vs transitive, test
command + pass evidence, one-line revert instructions.
- Keep the PR as DRAFT. Never mark ready-for-review. Never
enable auto-merge..github/ISSUE_TEMPLATE/security-remediation-dep.yml
name: Security — Dependency remediation
description: Open a remediation task for a single CVE / GHSA finding.
title: "Remediate: <finding-id> in <package>"
labels: ["copilot-remediate", "security"]
assignees: ["copilot"]
body:
- type: input
id: finding_id
attributes:
label: Finding id
description: CVE id, GHSA id, or scanner-assigned id.
placeholder: CVE-2026-1234
validations:
required: true
- type: input
id: package
attributes:
label: Affected package
description: Package name (best-effort hint from the scanner).
placeholder: "@example/unsafe-parser"
validations:
required: true
- type: dropdown
id: severity
attributes:
label: Advisory severity
options: [critical, high, medium, low]
validations:
required: true
- type: textarea
id: advisory_url
attributes:
label: Advisory link(s)
description: Paste the GHSA / CVE / NVD URL.
validations:
required: true
- type: textarea
id: notes
attributes:
label: Notes for the agent
description: Anything the scanner couldn't auto-populate.
placeholder: |
- Dependency is transitive via "express@4".
- Workspace package `apps/web` is the caller.
validations:
required: falseWhat gets dispatched
When the issue is created (or labeled), the
assign-copilot.yml Action from the recipe page assigns
@copilot. The coding agent reads:
- The repo-level
copilot-instructions.md(house rules + the dependency addendum above). - The issue body (finding id, package, advisory link).
And produces: a draft PR on copilot/<finding-id> that either
fixes the finding or posts a comment explaining why the fix was
refused (major bump required, pre-release only, tests fail).
Known limitations
- Instructions are a prompt, not enforcement. Pair this with
strict branch protection +
CODEOWNERSrouting on any path the agent must not touch. The addendum’s “paths you may NOT touch” list is advisory;CODEOWNERSmakes it a hard gate. - Single-manifest assumption. For monorepos, file one issue per affected workspace so the agent has a narrow scope.
- Transitive fixes. The agent can only hoist a transitive fix by bumping a parent; if the parent requires a major bump, the addendum forces a stop. That’s the correct behavior, but expect more “refused” comments than raw PRs for older ecosystems.
- Scanner projection cadence matters. If your scanner
projects every low-severity finding into an issue, the Copilot
review queue will balloon. Gate projection on severity
high | criticaland label drift manually.
Changelog
- 2026-04-21 — v1, first published. Covers Node / Python / Go lockfiles. Major-bump refusal is intentional.