| name | dependency-remediation |
| description | Use when dependency-audit has produced CVE findings and you need to close them. Triages each finding into upgrade / transitive override / patch / replace / accept-with-mitigation, applies the fix per ecosystem (npm, pip, go, cargo, maven), verifies with tests, updates SBOM and lockfiles, and writes the audit trail. |
| safety | writes-shared |
| produces | security/remediation/dependencies-<date>.md |
| consumes | ["security/findings/dependencies.json"] |
When to use
dependency-audit has produced security/findings/dependencies.json and at least one finding is High severity or higher.
- A Dependabot / Renovate PR has stalled and you need a human-driven remediation pass.
- A vendor advisory (GHSA, CVE, ecosystem-specific) has been published affecting a direct or transitive dependency.
- A release is gated on closing CVEs above a configured severity threshold.
Do not use this skill to scan for vulnerabilities — that's dependency-audit. Do not use it for secret leaks in dependency manifests — that's secret-remediation.
Inputs
security/findings/dependencies.json from dependency-audit, or an equivalent CVE list.
- Write access to the repo (to change lockfiles and raise a PR).
- Local environment with the relevant package manager installed (
npm, poetry, go, cargo, mvn).
- Optional: access to the SBOM store (e.g.
cyclonedx-cli configured to publish).
Outputs
security/remediation/dependencies-<date>.md — per-CVE remediation audit trail.
- Updated lockfiles (
package-lock.json, poetry.lock, go.sum, Cargo.lock, pom.xml).
- A PR with: CVEs closed, packages changed, test evidence, SBOM delta.
- For
accept-with-mitigation: a documented exception with review date.
Tool dependencies
- Package managers:
npm, yarn, pnpm, poetry, pip-tools, go, cargo, mvn, gradle.
- Audit tools (to re-verify):
npm audit, osv-scanner, pip-audit, govulncheck, cargo-audit.
cyclonedx-bom or syft for SBOM regeneration.
jq for parsing findings JSON.
Procedure
-
Triage findings into one of five buckets. For each finding in dependencies.json:
| Bucket | Criteria |
|---|
| Upgrade direct | Package is declared in the manifest + fix version exists. |
| Transitive override | Package is a sub-dependency + fix version exists but parent hasn't updated. |
| No patch — replace | No fix available + a maintained alternative exists with compatible API. |
| No patch — accept | No fix, no alternative. Must justify with compensating control. |
| False positive | CVE does not apply to our usage (e.g. server-only CVE in a build-time dep). |
-
Upgrade direct — run the canonical command per ecosystem:
npm / yarn / pnpm:
npm install lodash@^4.17.21
yarn upgrade lodash@^4.17.21
npm ls lodash
npm audit --production
pip / poetry:
poetry add 'urllib3>=2.2.2'
echo 'urllib3>=2.2.2' >> constraints.txt
pip-compile --upgrade-package urllib3
pip-audit
Go:
go get github.com/gin-gonic/gin@v1.10.0
go mod tidy
govulncheck ./...
Cargo:
cargo update -p tokio --precise 1.38.0
cargo audit
Maven:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>
</dependencies>
</dependencyManagement>
Then mvn dependency-check:check.
-
Transitive override — force the sub-dependency version at the package manager level.
npm (≥ 8.3):
{
"overrides": {
"minimist": "1.2.8"
}
}
Then npm install and verify with npm ls minimist — every entry should be ≥ 1.2.8.
yarn (Berry / classic 1.x):
{
"resolutions": {
"**/minimist": "1.2.8"
}
}
pnpm:
{
"pnpm": {
"overrides": {
"minimist": "1.2.8"
}
}
}
pip: add to constraints.txt and rebuild requirements.txt via pip-compile --constraint constraints.txt.
Go: use replace in go.mod:
replace github.com/bad/transitive v1.0.0 => github.com/bad/transitive v1.0.3
Cargo: [patch.crates-io] stanza in Cargo.toml.
-
Replace — when no patch and no parent update are viable, find a maintained alternative.
- Check the ecosystem's maintenance signals: last-release date, open-issue count, download trend.
- Confirm API compatibility or plan the migration (codemod script, compatibility shim).
- Stage the replacement in a small PR with a before/after diff table.
-
Test the upgrade. Never ship a dependency bump without running:
- Full unit test suite.
- Integration tests that exercise the upgraded library (if no existing coverage, add a smoke test hitting the API path).
- For major-version bumps, read the upgrade guide / changelog and
grep the codebase for any deprecated API used.
- For security-relevant changes (e.g. auth library), write an explicit regression test asserting the vulnerable behavior no longer occurs.
-
Regenerate and commit the SBOM.
cyclonedx-bom -o sbom.json
syft . -o cyclonedx-json > sbom.json
git add sbom.json
-
Open the PR using this body template:
## CVEs closed
- CVE-2020-8203 (HIGH, CVSS 7.4) — lodash < 4.17.19
## Changes
| Package | Before | After | Reason |
|---|---|---|---|
| lodash | 4.17.15 | 4.17.21 | direct upgrade |
## Test evidence
- `npm test` — all 327 suites pass (https://ci…/run/1234).
- Manual smoke test against `/api/orders` — pass.
## SBOM delta
See `sbom.json` diff. 1 package updated, 0 added, 0 removed.
-
Accept-with-mitigation requires explicit documentation. If you cannot upgrade / override / replace:
- Why not (date-bound reason, e.g. "vendor patch ETA 2026-05-15" or "blocks prod due to breaking change").
- Compensating control (WAF rule, network isolation, feature flag, runtime patch such as aikido-zen / snyk runtime).
- Review date (no more than 60 days away).
- Owner.
-
Write remediation report. security/remediation/dependencies-<date>.md contains:
- Summary: total CVEs in audit, closed / accepted / residual counts.
- Per-CVE table: ID, severity, package, action taken, PR link, test evidence.
- Exceptions register with review dates.
- Re-audit result (ideally 0 matching the original CVE IDs).
Examples
Example 1 — direct upgrade with no breaking changes
dependencies.json excerpt:
{
"findings": [
{
"cve": "CVE-2020-8203",
"severity": "HIGH",
"cvss": 7.4,
"package": "lodash",
"version_current": "4.17.15",
"version_fixed": "4.17.19",
"direct": true
}
]
}
Actions:
npm install lodash@^4.17.21
npm ci
npm test
npx @cyclonedx/cyclonedx-npm --output-file sbom.json
git add package.json package-lock.json sbom.json
git commit -m "chore(deps): upgrade lodash to 4.17.21 — closes CVE-2020-8203"
gh pr create --title "deps: close CVE-2020-8203 (lodash)" --body-file .github/pr-body-deps.md
npm audit --production
Report excerpt:
### CVE-2020-8203 — lodash prototype pollution
- Severity: HIGH (CVSS 7.4)
- Action: upgraded direct dependency `lodash` 4.17.15 → 4.17.21.
- Test evidence: `npm test` green (run 1234), smoke test on `/api/orders` pass.
- SBOM: 1 package updated.
- PR: #482, merged 2026-04-20T15:42Z.
Example 2 — transitive override with follow-up reminder
dependencies.json excerpt:
{
"findings": [
{
"cve": "CVE-2021-44906",
"severity": "HIGH",
"cvss": 9.8,
"package": "minimist",
"version_current": "1.2.5",
"version_fixed": "1.2.6",
"direct": false,
"depended_on_by": ["mkdirp@0.5.5"]
}
]
}
mkdirp@0.5.5 is a direct dependency; upgrading it to 0.5.6 pulls minimist ≥ 1.2.6 transitively. But we also have mkdirp pinned via legacy tooling. Use an override:
{
"overrides": {
"minimist": "1.2.8"
}
}
npm install
npm ls minimist
npm test
npm audit --production
Follow-up ticket (filed to tech-debt backlog): "Drop overrides.minimist once mkdirp is upgraded to ≥ 3.0 or replaced with fs.mkdir({recursive: true})." Owner: @platform. Review: 2026-06-20.
Report excerpt:
### CVE-2021-44906 — minimist prototype pollution
- Severity: HIGH (CVSS 9.8)
- Action: transitive override via `overrides.minimist = "1.2.8"`.
- Verified: `npm ls minimist` shows 1.2.8 in every path.
- Follow-up: TECH-812 — drop override when mkdirp upgraded.
- Re-audit: CVE-2021-44906 not reported.
Constraints
- Never merge a dependency PR without running the full test suite. A green single unit-test pass is insufficient.
- Never downgrade a package to escape a CVE — always prefer upgrade or override forward.
- Never leave
accept-with-mitigation without a review date and owner.
- Never bulk-merge Dependabot PRs without re-running the audit against the merged result — Dependabot can close one CVE while introducing another.
- Do not open an override without a follow-up ticket to drop it when the parent updates. Overrides tend to survive forever if not tracked.
Quality checks
- Re-run
dependency-audit after changes → 0 findings for the CVE IDs in scope (or justified exceptions only).
npm ci (or ecosystem equivalent) succeeds in a clean checkout with the new lockfile.
- Full test suite green on the remediation PR.
- SBOM has been regenerated and committed.
- Every exception in the report has an owner, a review date ≤ 60 days out, and a compensating control named.