Skip to content

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/
What you're looking for: Your installed version compared to the CVE's affected version range. CVEs usually list affected versions as "< X.Y.Z" or ">= A.B.C, < X.Y.Z". Common pitfall: Multiple installed versions. Tools like 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>
What you're looking for: Whether your application ever calls the specific vulnerable function, and whether untrusted input can reach it. Read the CVE's "Attack Vector" field carefully — a "Network" attack vector with "No authentication required" is far more concerning than "Local" access. Common pitfall: Concluding "not reachable" based only on a grep that comes up empty. Some vulnerabilities are in the library's default initialization or auto-triggered by data format. Read the full CVE technical description, not just the summary.

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
What you're looking for: A release that is both >= the minimum fixed version AND compatible with your other dependencies. Check the package's GitHub releases for "Security fix" tags. Common pitfall: The "fixed version" listed in the CVE may not exist yet at the time you are reading it (NVD sometimes lists anticipated fix versions). Always verify the version actually exists before planning an upgrade around it.

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
What you're looking for: Whether you can upgrade it directly (it is in your manifest) or must upgrade a parent package (it is transitive). Transitive CVEs are far harder to fix because you do not control the version pull. Common pitfall: A package may appear as both direct AND transitive (different versions required by different packages). 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
3. Run the full test suite immediately:
make test-all
4. If tests pass: deploy to staging, run smoke tests, deploy to production 5. If tests fail: triage — are failures related to the upgrade (breaking changes)? - If yes: fix compatibility issues; do NOT skip the upgrade - If no (pre-existing test failure): fix the test, then deploy 6. After deploy: run the scanner again to confirm vulnerability is gone:
pip-audit  # or: npm audit, trivy image <new-image>
Verify: Scanner reports zero findings for this CVE. Application is deployed to production and healthy. Close the ticket with deploy SHA and scanner confirmation screenshot.

✅ 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
3. Set a calendar reminder to re-review the suppression in 90 days (reachability can change) 4. Document in the PR / ticket: which lines of code were checked, what the CVE vulnerable function is, why it cannot be reached Verify: Scanner does not report this CVE. Suppression entry has a written justification and a review date. A second engineer has reviewed and approved the suppression.

✅ 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>
2. Open an issue in the upstream repository (or check existing issue) for the fix timeline 3. Set a weekly calendar reminder to check if a fixed version has been released 4. Document the pin in a comment in the requirements file: why it is pinned and what CVE it addresses 5. Apply a mitigating control if the vulnerability is high-severity (WAF rule, disable feature) Verify: Your scanner confirms the pinned version does not trigger the CVE. The tracking ticket is open and has a clear owner.


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