Cut CVE noise with reachability
Measure how many of your CVEs can't be reached from your code, across every repository, with no setup.
Most CVEs in a dependency tree sit in code your application never calls. Reachability analysis finds those and marks them unreachable, so your team works on the rest. Socket has three tiers:
| Tier | What it looks at | Setup | Reduction Socket cites |
|---|---|---|---|
| Dependency reachability | Whether each transitive package is imported by anything at all | None, every scan | Up to 35% |
| Precomputed | Call paths from your direct dependencies down to vulnerable transitive packages | None, every scan | Up to 80% of transitive CVEs |
| Full application | Call paths from your own code, through direct and transitive dependencies | Add --reach to a scan | Up to 90% |
Results vary by codebase, which is why the POC measures yours. It runs in two steps: precomputed reachability across every repository for the headline number (this page), then full application reachability on one or two apps for depth.
Before you start: a baseline for your repositories (Connect your repositories). Precomputed results are already in it.
1. Agree the bar
Before you look at any numbers, agree with Security what counts as a pass, and which of the two figures in step 2 it applies to. Bars teams have set range from 60% to 70% noise reduction. Writing it down first keeps the result from being argued after the fact.
2. Get the headline number
- Open Alerts in the dashboard. It covers the default branch of every repository.
- Filter Category to Vulnerability. Write down the total.
- Set the CVE Reachability filter to Unreachable. Write down the count.
- Set the CVE Reachability filter to Direct dependency. Write down the count.
- Work out both figures:
- Overall noise reduction: unreachable divided by the total, times 100.
- Transitive noise reduction: unreachable divided by (total minus direct dependency), times 100.
Direct-dependency CVEs pull the overall figure downPrecomputed analysis doesn't cover CVEs in your direct dependencies. They stay in the total but can never count as unreachable, so if they're a large share of your CVEs, the overall figure will be low. In one test app, 35 of 49 CVEs were in direct dependencies: overall noise reduction was 6%, transitive was 21%, and full application analysis on the same app reached 41%. Report the transitive figure for the estate, and use full application reachability for the overall figure on the apps you pick.
To turn it into hours, fill one row per business unit or repository group:
| Total CVEs | Direct dependency | Unreachable | Overall % | Transitive % | Hours to fix one CVE | Hours saved |
|---|---|---|---|---|---|---|
Hours saved is the unreachable count times the hours to fix one CVE. Use your team's own figure for the hours; if you don't have one, state the assumption you used.
Read the verdicts
| Dashboard label | API value | Meaning | What to do |
|---|---|---|---|
| Reachable | reachable | A call path from your code may trigger the CVE (full application only) | Fix it |
| Potentially reachable | maybe_reachable | A call path through your dependencies may trigger it; use from your own code is unknown (precomputed only) | Keep in scope; full application analysis settles it |
| Unreachable | unreachable | No call path triggers the CVE | Deprioritize |
| Pending precomputation | pending | Queued for analysis, usually minutes | Run a new scan; existing scans don't update |
| Not yet supported | missing_support | Socket hasn't yet mapped the vulnerable functions | Check again later |
| Direct dependency | direct_dependency | The CVE is in a direct dependency, which precomputed analysis can't cover | Run full application analysis |
| Not determinable | undeterminable_reachability | Usually a CLI or binary not meant to be called from code | Judge it by hand |
| Unknown | unknown | The full application scan hit a problem, often missing private dependencies | Install all dependencies and rescan |
| Error | error | Analysis failed | Check the scan logs, or contact Socket |
Known limits
- Private packages hide edges. Precomputed analysis can't see inside a private package, so a vulnerable public package reached only through one can be marked unreachable. Use full application analysis on repositories that lean on internal packages.
- Python needs a lock file. With
requirements.txt, Socket can't tell direct from transitive dependencies, so precomputed analysis has nothing to work with. Commituv.lockorpoetry.lock. - Every package manager needs a lockfile for a complete tree. See the troubleshooting table in Connect your repositories.
Verify
- Vulnerability alerts show a reachability verdict with no extra setup
- The CVE Reachability filter narrows the list
- Total, direct dependency and unreachable counts recorded, and both figures worked out
- Every verdict you see maps to a row in the table above
Automate it
Count the verdicts on one scan. The scan ID is the last part of the scan's report URL, after /sbom/. SOCKET_CLI_API_TOKEN is the variable the Socket CLI reads, so one export covers both the CLI and these calls.
export SOCKET_CLI_API_TOKEN=...
SCAN_ID=your-scan-id
curl -s -H "Authorization: Bearer $SOCKET_CLI_API_TOKEN" \
"https://api.socket.dev/v0/orgs/your-org/full-scans/$SCAN_ID" \
| jq -r '.alerts[]? | select(.type | test("CVE|cve"))
| .reachability.head.results[0].type // "none"' \
| sort | uniq -cThe verdict is a top-level reachability field on each alert, not under props. CVE alert types are tiered (criticalCVE, cve, mediumCVE, mildCVE), so match on CVE|cve rather than one name.
Next
Full application reachability on one or two apps, then Fix what matters and Patch what you can't upgrade.
Reference
Updated 8 days ago