Vulnerability Prioritization (Reachability Analysis)
How Safeguard uses call-graph reachability, EPSS, KEV, and runtime context to cut 60-80% of CVE noise so teams patch what actually matters.
Vulnerability Prioritization
A typical enterprise application has 200–500 direct and transitive dependencies, and 5–15% of them have known CVEs at any moment. Most are unreachable in the context of how the application actually uses the library. Reachability analysis tells you which CVEs are actually relevant, so your team patches the ones that matter.
Safeguard's prioritization engine combines four signals: reachability, exploitability (EPSS + KEV), runtime context, and business context. The result is a single ranked backlog that typically shrinks open findings by 60-80% without missing real risk.
The Four Signals
1. Reachability
A CVE is "reachable" when your code actually calls the vulnerable function or triggers the vulnerable code path.
Safeguard performs three kinds of reachability analysis:
- Static reachability — builds a call graph from your source, lockfile, and bytecode, and checks whether any path leads to the vulnerable sink.
- Dynamic reachability — at runtime, the collector records which modules and functions are actually loaded and invoked in your production workloads. Flags never-executed vulnerable paths as cold.
- Configuration reachability — parses framework configuration (e.g., Spring bean definitions, Next.js route files) to determine if code is wired in at all.
A vulnerability is classified as:
- Reachable — a real call path exists and is instantiated at runtime.
- Conditionally reachable — path exists but requires a feature flag, route, or configuration not currently enabled.
- Unreachable — no call path exists in your compiled code.
- Unknown — the analyzer couldn't decide (e.g., dynamic dispatch, reflection, native code). Marked conservatively as potentially reachable.
2. Exploitability — EPSS and KEV
- EPSS (Exploit Prediction Scoring System) estimates the probability of exploitation within 30 days. Safeguard ingests the daily EPSS feed.
- KEV (CISA Known Exploited Vulnerabilities) lists vulnerabilities observed being exploited in the wild. If a CVE is on KEV, exploitation is not hypothetical.
- Weaponization signal — Safeguard tracks public PoC availability (exploit-db, GitHub PoC repos, Metasploit modules) as an additional exploit-availability signal.
3. Runtime Context
A vulnerability in code that only runs in a dev Docker compose file is not the same as a vulnerability in the production API. The runtime collector labels each asset with:
env: production, staging, dev, sandbox.exposure: internet-facing, internal, air-gapped.data_class: customer PII, PHI, PCI, public.criticality: inferred from asset ownership and business tags.
These labels feed into the prioritization score.
4. Business Context
Via policy you can declare which assets are regulated (PCI, HIPAA, FedRAMP), which handle customer data, and which are on the critical path for revenue. Findings on those assets rank higher.
The Unified Priority Score
Each finding gets a Priority Score from 0-100:
priority = f(
base_severity, // CVSS 3.1 base score
epss_probability, // 0.0 - 1.0
kev_listed, // boolean
poc_available, // boolean
reachability_class, // reachable | conditional | unreachable | unknown
env, // production > staging > dev
exposure, // internet-facing > internal
data_sensitivity, // regulated > public
business_criticality // policy-defined
)The exact weights are configurable per tenant (Settings → Prioritization). Defaults map to the intuitive ordering:
- Reachable + KEV-listed + production → Priority 90-100 (P0).
- Reachable + EPSS > 0.5 + production → Priority 75-89 (P1).
- Reachable + high CVSS + staging → Priority 50-74 (P2).
- Unreachable + any severity → Priority 0-30 (backlog; auto-suppressed in some views).
Typical Noise Reduction
Based on aggregate data across customer tenants:
| Source of reduction | % findings removed from "urgent" queue |
|---|---|
| Reachability filtering | 60-80% |
| EPSS < 0.05 | additional 5-15% |
| Asset-class filtering (dev/sandbox) | additional 5-10% |
| Combined deduplication | additional 5-10% |
A typical mid-sized team sees an open urgent queue of 20-60 CVEs per week instead of 200-500.
Language and Ecosystem Support
Reachability analysis quality varies by language. Current support:
| Language / Ecosystem | Static reachability | Dynamic reachability | Notes |
|---|---|---|---|
| Java / Kotlin / Scala (JVM) | GA | GA | Bytecode-level call graph. High fidelity. |
| Python | GA | GA | Import- and AST-based; handles virtualenv layouts. |
| JavaScript / TypeScript | GA | GA | Handles CommonJS, ESM, and webpack bundles. |
| Go | GA | GA | Module-aware; ssa-based call graph. |
| Rust | GA | GA | cargo metadata + LLVM IR path extraction. |
| C / C++ | GA | GA | clang-based call graph for reachability on common package managers. |
| Ruby | Preview | GA | Method resolution conservative for metaprogramming. |
| PHP | Preview | GA | Composer-aware; Symfony / Laravel-aware. |
| C# / .NET | GA | GA | Roslyn-based; NuGet-aware. |
| Swift / Objective-C | Preview | Preview | Framework-aware. |
How to View It
Vulnerabilities Tab (ESSCM)
The vulnerabilities table has a Priority column and a Reachability column. Default sort is Priority descending.
Common filters:
reachable:true— only reachable findings.kev:true— only KEV-listed findings.epss:>0.3— only probability above 30%.env:production reachable:true kev:true— the P0 backlog.
Griffin Natural Language
Griffin understands the prioritization surface directly:
"Show my P0 findings in production that are reachable."
"Which of last week's new CVEs are reachable but not yet on KEV?"
"Summarize the top 10 priorities for the payments service."Dashboards
The Prioritization dashboard shows:
- Total finding volume vs. urgent queue (trend over time).
- Time-to-remediate by priority band.
- Drop rate for each signal (how much each layer filtered out).
- Backlog aging — how long findings sit before being patched, suppressed, or excepted.
Integrating With Policy
Reachability feeds directly into guardrails. A production-admission policy might say:
- id: no-reachable-kev-in-prod
condition: |
env == "production"
AND any(vulnerabilities,
reachability_class in ["reachable", "conditional"]
AND kev == true
)
effect: BLOCKSee Guardrails & Enforcement for the full policy language.
When Reachability Doesn't Help
Reachability is not a silver bullet. It's weakest when:
- Code executes dynamically via reflection, eval, or unsafe dispatch. Safeguard marks these as
unknownand does not suppress them. - Vulnerabilities are data-driven (e.g., accepts a malicious input even though the function is unreachable by static analysis).
- The vulnerable code path runs only under specific configurations that the analyzer cannot enumerate.
For these cases, rely on the other signals (KEV, EPSS, runtime observation) and conservative defaults.
Auto-Fix and Griffin Integration
Once a finding is prioritized, Griffin can:
- Propose a minimum-risk upgrade that resolves the vulnerability.
- Generate a safe-downgrade when a patched version doesn't yet exist.
- Explain why a finding was de-prioritized (essential for exception reviews).
- Auto-open PRs for the top-N priorities on a schedule.
See Auto-Fix.
API
Query findings sorted by priority:
safeguard findings list \
--asset my-api \
--env production \
--reachable true \
--min-priority 75Or via API:
curl -H "Authorization: Bearer $SG_TOKEN" \
"https://api.safeguard.sh/v1/findings?asset=my-api&reachable=true&min_priority=75"Related
- Continuous Scanning — how findings get detected.
- Vulnerabilities — the ESSCM vulnerabilities UI.
- Auto-Fix — how Griffin remediates prioritized findings.
- Guardrails & Enforcement — enforcing policies on reachable findings.
- AI Models — Griffin, Eagle, Lino and how they power prioritization.
Continuous Scanning
Safeguard re-evaluates every asset against new vulnerabilities, new malware signatures, and new policy rules continuously — not just at build time.
Deep Dependency Scanning (100 Levels)
Scan dependencies 100 levels deep — 40 more than most SCA tools — to find vulnerabilities hidden in transitive dependencies.