Decision Tree: Dependency Has a CVE¶
Category: Security Response Starting Question: "A dependency we use has a published CVE — what do I do?" Estimated traversal: 2-5 minutes Domains: security, devops, cicd, compliance
The Tree¶
A dependency we use has a published CVE — what do I do?
│
├── What is the CVE CVSS score?
│ (Look up at https://nvd.nist.gov/vuln/detail/CVE-XXXX-XXXXX)
│ │
│ ├── FIRST: Check our current version vs affected version range
│ │ `pip show <package>` / `npm ls <package>` / `go list -m all | grep <pkg>`
│ │ Compare against CVE's "Affected Versions" field
│ │ │
│ │ ├── Our version is NOT in the affected range
│ │ │ └── ✅ ACTION: No action needed — document the check and close out
│ │ │ Record: CVE ID, our version, affected range, date checked
│ │ │
│ │ └── Our version IS affected — continue triage below
│ │
│ ├── Critical (CVSS ≥ 9.0)
│ │ │
│ │ ├── Is a fixed version available?
│ │ │ │
│ │ │ ├── YES — fixed version available
│ │ │ │ │
│ │ │ │ ├── Is the vulnerable code path reachable in our usage?
│ │ │ │ │ (See reachability check below)
│ │ │ │ │ │
│ │ │ │ │ ├── YES — reachable → ✅ ACTION: Upgrade Immediately
│ │ │ │ │ │ SLA: same business day or next release within 24h
│ │ │ │ │ │
│ │ │ │ │ └── NO — not reachable (false positive)
│ │ │ │ │ └── ✅ ACTION: Add to Suppression List with Justification
│ │ │ │ │ But upgrade anyway if it is low-friction
│ │ │ │ │
│ │ │ │ └── Is the upgrade a breaking change?
│ │ │ │ `pip install <pkg>==<fixed-version> --dry-run`
│ │ │ │ `npm install <pkg>@<fixed> --dry-run`
│ │ │ │ │
│ │ │ │ ├── Minor breaking changes — fix code + upgrade
│ │ │ │ │ (CVSS 9+ is worth the breaking change pain)
│ │ │ │ │
│ │ │ │ └── Major breaking changes (API redesign, requires weeks)
│ │ │ │ → Apply mitigating control NOW (WAF rule, disable feature)
│ │ │ │ → Track upgrade as P1 project with deadline
│ │ │ │
│ │ │ └── NO — no fix available
│ │ │ │
│ │ │ ├── Is there a workaround or mitigating control?
│ │ │ │ (WAF rule, config change, disable feature, unpublish endpoint)
│ │ │ │ │
│ │ │ │ ├── YES → Apply mitigating control + escalate to security team
│ │ │ │ │
│ │ │ │ └── NO → ✅ ACTION: Escalate to security team for risk acceptance
│ │ │ │
│ │ │ └── ✅ ACTION: Pin to Last Non-Affected Version + Track Upstream
│ │ │ Monitor for patch release; set calendar reminder weekly
│ │ │
│ │ └── Is there an exploit available in the wild?
│ │ (Check: Exploit-DB, Metasploit modules, GitHub PoC repos,
│ │ CISA Known Exploited Vulnerabilities catalog)
│ │ `curl -s "https://exploits.shodan.io/api/search?query=CVE-<id>"` (if available)
│ │ │
│ │ ├── YES — exploit is public → Upgrade Immediately (no SLA debate)
│ │ │
│ │ └── NO — no known exploit → Standard SLA applies
│ │
│ ├── High (CVSS 7.0–8.9)
│ │ │
│ │ ├── Is the vulnerable code path actually called? (reachability check)
│ │ │ │
│ │ │ ├── YES — code path is reachable
│ │ │ │ └── ✅ ACTION: Upgrade Within Current Sprint (≤ 7 days)
│ │ │ │
│ │ │ └── NO — not reachable
│ │ │ └── ✅ ACTION: Add to Suppression List with Justification
│ │ │ Schedule low-priority upgrade anyway
│ │ │
│ │ └── Is this a runtime or build-only dependency?
│ │ │
│ │ ├── Build-only (test runner, linter, code generator — not in prod image)
│ │ │ └── Upgrade within sprint; not a production risk
│ │ │
│ │ └── Runtime dependency (ships in the deployed container/binary)
│ │ → Treat as affecting production; standard high-severity SLA
│ │
│ ├── Medium (CVSS 4.0–6.9)
│ │ │
│ │ ├── Is the vulnerable code path reachable?
│ │ │ │
│ │ │ ├── YES — reachable → ✅ ACTION: Schedule Upgrade (≤ 30 days)
│ │ │ │
│ │ │ └── NO — not reachable
│ │ │ └── ✅ ACTION: Add to Suppression List
│ │ │ Revisit if reachability analysis changes
│ │ │
│ │ └── Is this in a build-only dependency?
│ │ └── Even lower urgency — schedule in next maintenance window
│ │
│ └── Low (CVSS < 4.0)
│ └── ✅ ACTION: Add to Security Backlog — best-effort upgrade
│ Include in next dependency refresh cycle
│ Only suppress if clearly unreachable; otherwise upgrade opportunistically
│
├── Is the package a direct or transitive dependency?
│ │
│ ├── Direct dependency (in requirements.txt / package.json / go.mod)
│ │ └── You control the version; upgrade directly
│ │ Easiest case — just bump the version
│ │
│ └── Transitive dependency (pulled in by another package)
│ │
│ ├── Check which direct dependency pulls it in
│ │ `pip-audit --fix --dry-run`
│ │ `npm audit --fix --dry-run`
│ │ `go mod graph | grep <package>`
│ │ `mvn dependency:tree | grep <package>`
│ │ │
│ │ ├── Direct dep has a version that pulls in the fixed transitive version
│ │ │ └── Upgrade the direct dep (may have its own breaking changes)
│ │ │
│ │ └── No version of the direct dep pulls in the fixed version yet
│ │ │
│ │ ├── Force-pin the transitive dep in your manifest
│ │ │ Python: add `<pkg>>=<fixed-version>` to requirements.txt
│ │ │ Node: use `overrides` in package.json (npm 8+) or `resolutions` (Yarn)
│ │ │ Go: add `replace` directive in go.mod
│ │ │
│ │ └── If force-pin causes conflicts → wait for direct dep to release update
│ │ Apply mitigating control in the meantime
│
└── Reachability check: Is the vulnerable code path called?
│
├── Read the CVE description — what specific function/class/feature is vulnerable?
│ (e.g., "XML deserialization in version < 2.3", "SSRF in proxy feature")
│ │
├── Search your codebase for usage of that specific feature
│ `grep -r "xml_deserializ\|etree.parse\|lxml" --include="*.py" src/`
│ `grep -r "require.*vulnerable-pkg" --include="*.js" src/`
│ │
├── Check if the vulnerable feature is enabled at all
│ (Some CVEs only affect optional features that must be explicitly enabled)
│ │
├── Check if the attack vector is accessible from your deployment
│ (Network-accessible? Requires auth? Requires specific input format?)
│ │
├── NOT REACHABLE criteria (must satisfy ALL):
│ - The vulnerable function is not imported or called in our code
│ - OR the feature requires an explicit config option we do not set
│ - OR the attack vector requires unauthenticated network access and we have auth
│ - AND there is no path for untrusted user input to reach the vulnerable code
│ │
└── REACHABLE if any of:
- We call the vulnerable function (even indirectly)
- The library auto-activates the vulnerable behavior
- Untrusted input could reach the vulnerable path
→ Treat as reachable; upgrade
Node Details¶
Check 1: Are we on an affected version?¶
Command/method:
# Python
pip show <package-name>
pip-audit # scans all installed packages against NVD
# Node.js
npm audit
npm ls <package-name> --depth=0
# Go
go list -m all | grep <package>
govulncheck ./... # official Go vulnerability checker
# Java (Maven)
mvn dependency:tree | grep <package>
mvn org.owasp:dependency-check-maven:check
# Containers (scan image)
trivy image <image:tag>
grype <image:tag>
# Infrastructure as code
trivy config devops/helm/
trivy config devops/terraform/
pip can have the same package installed in different virtual environments or at different levels. Run the check in the same environment that runs in production (ideally inside the Docker image).
Check 2: Is the vulnerable code path reachable?¶
Command/method:
# Static search — find usage of the vulnerable function/class
# Replace with actual vulnerable symbol from CVE description
grep -r "parse_xml\|XMLParser\|fromstring" --include="*.py" src/ app/
grep -r "require('xml2js')\|require('lodash')" --include="*.js" src/
# Check if feature requires explicit opt-in
python3 -c "import <pkg>; print(dir(<pkg>))" | grep <vulnerable-function>
# Dynamic analysis — check if the code path is exercised in tests
pytest --cov=. --cov-report=html # check coverage for the vulnerable import path
# For network-accessible exploits — check if the endpoint is exposed
kubectl get ingress --all-namespaces
curl -s -o /dev/null -w "%{http_code}" https://<endpoint>/<vulnerable-path>
Check 3: Is there a fixed version available?¶
Command/method:
# Check available versions
pip index versions <package>
npm view <package> versions --json | python3 -m json.tool | grep -A5 '"<current-version>"'
go list -m -versions <module-path>
# Check the library's changelog/release notes for the fix
# GitHub: https://github.com/<org>/<repo>/releases
# PyPI: https://pypi.org/project/<pkg>/#history
# NPM: https://www.npmjs.com/package/<pkg>?activeTab=versions
# Try upgrading dry-run to see conflicts
pip install <pkg>==<fixed-version> --dry-run 2>&1 | grep -E "ERROR|Conflict|Would install"
npm install <pkg>@<fixed> --dry-run
Check 4: Direct vs transitive dependency¶
Command/method:
# Python — show full dependency tree
pip-tree # or: pipdeptree
pip show <vulnerable-pkg> | grep "Required-by"
# Node.js — find who requires the vulnerable package
npm ls <vulnerable-pkg> --all
# Go — show dependency chain
go mod graph | grep <vulnerable-module>
# Check if it is in requirements.txt / package.json directly
grep "<vulnerable-pkg>" requirements.txt package.json go.mod pom.xml
npm ls <pkg> --all will show every path. Address the direct dependency first.
Terminal Actions¶
✅ Action: Upgrade Immediately (CVSS ≥ 9.0, reachable, exploit available)¶
Do: 1. Create an emergency ticket and assign to yourself (do not let this wait for sprint planning) 2. Run the upgrade in a branch (exception to "commit to main" — changes with risk deserve a PR review):
pip install --upgrade <package>==<fixed-version>
pip freeze > requirements.txt
# Or for Node:
npm install <package>@<fixed-version>
npm ci # verify lockfile is consistent
✅ Action: Upgrade Within Current Sprint (CVSS ≥ 7.0, reachable)¶
Do:
1. Create a ticket with label security, high-severity, and a due-date matching end of sprint
2. Add the upgrade task to current sprint immediately (do not push to next sprint)
3. Same process as above (upgrade → test → deploy) but can happen within the sprint window
4. Document: CVE ID, affected version, fixed version, test results, deploy date
Verify: Ticket closed within sprint. Scanner confirms no finding. Deployment verified healthy.
✅ Action: Schedule Upgrade (CVSS < 7.0 or unreachable code path)¶
Do:
1. Create a ticket with label security, medium-severity (or low-severity), and a due-date:
- Medium: 30 days
- Low: next quarterly dependency refresh
2. Add a brief note explaining the reachability analysis if the code path is unreachable
3. Assign to the team's security maintenance rotation, not a specific person
Verify: Ticket exists with an owner and due date. It does not live in an unreviewed backlog — confirm it appears in the team's sprint review cadence.
✅ Action: Add to Suppression List with Justification¶
Do: 1. Verify the false positive conclusion with a second pair of eyes (another engineer reviews the reachability analysis) 2. Add a suppression entry to your scanner config with a written justification:
# .trivyignore or equivalent
# CVE-2024-XXXXX — dependency: <pkg>, reason: vulnerable function parse_xml() is
# never called; we use only <pkg>.safe_function(). Verified by code audit on 2026-03-19.
# Review date: 2026-06-19
CVE-2024-XXXXX
✅ Action: Pin to Fixed Version + Track Upstream¶
Do: 1. Pin the dependency to the last non-vulnerable version as an immediate fix:
# requirements.txt — use >= for minimum non-vulnerable version when available
<package>>=<last-safe-version>,<minimum-version-with-fix-when-available>
# Or hold back:
<package>==<last-safe-version>
Edge Cases¶
- The CVE is for a sub-component of the package (e.g., a specific codec or optional feature you have never installed): Still verify — optional features are sometimes included by default in some distributions. Check whether the sub-component is actually present in your install.
- The upgrade requires upgrading 15 other packages simultaneously: This is dependency debt materializing as a security cost. Upgrade anyway — a managed migration with tests is safer than leaving a CVSS 9.0 vulnerability in production. Break the upgrade into steps if needed.
- The CVE scanner flags a package that is present in the image but not actually used by your application (included by a base image): Still your responsibility — the scanner sees it in your deployed artifact regardless of whether your code uses it. Either upgrade the base image to a version that has the fix, or use a slimmer base image.
- You receive a CVE report from a customer security scan of your product: This is a formal vulnerability report. Respond within your committed SLA, provide a timeline for the fix, and keep the customer informed. Do not dismiss it because "it is not reachable in our usage" without sending a documented explanation.
- The CVE is disputed (NVD shows "DISPUTED" status): Read the dispute details — sometimes the original reporter was wrong, sometimes the vendor is disputing in bad faith. Verify independently and make an explicit accept/reject decision with documentation.
Cross-References¶
- Topic Packs: security, cicd, devops
- Related trees: found-vulnerability.md, container-running-as-root.md
- Runbooks: incident_response.md