Safeguard prioritises vulnerabilities by combining four signals into one ranked backlog: whether your code can reach the vulnerable function, how likely exploitation is (the FIRST EPSS probability and the CISA KEV catalog), where the affected asset runs, and how much the business depends on it. Every finding gets a Priority Score from 0 to 100, so a reachable, KEV-listed flaw in a production service rises to the top and an unreachable one in a dev sandbox drops to the backlog. You then record why each lower-ranked finding waits, with an expiring mitigation or an uploaded VEX statement, so the order holds up in an audit.
This post walks through the problem that makes this necessary, what the standards say about prioritising by exploitation, exactly how the Safeguard flow works today, and where reachability can mislead you.
Key takeaways
- A backlog sorted by CVSS base score ranks theoretical severity, not likelihood or relevance. FIRST's own guidance says EPSS and CVSS measure different things and should not be multiplied together.
- CISA revoked BOD 22-01 on 10 June 2026. Its replacement, BOD 26-04, keeps the KEV catalog and sets federal remediation timelines from four decision points: public exposure, KEV status, whether exploitation is automatable, and technical impact.
- Vulnerability management in Safeguard (GA) enriches every CVE with EPSS and KEV data from sources including NVD, GitHub Security Advisories, OSV and CISA KEV, and re-checks projects daily.
- Reachability analysis and prioritisation (GA) runs static, dynamic and configuration reachability. Static reachability is GA for JVM languages, Python, JavaScript and TypeScript, Go, Rust, C and C++, and C# and .NET; it is Preview for Ruby, PHP, and Swift and Objective-C. Dynamic reachability is GA everywhere except Swift and Objective-C, where it is Preview.
- Safeguard's docs say reachability filtering typically removes 60 to 80% of findings from the urgent queue. That is Safeguard's stated figure from aggregate tenant data, not a guarantee for your codebase.
- Mitigations (part of vulnerability management, GA) record risk acceptances, false positives, workarounds and not-applicable decisions with a justification, an approver, an optional expiry and a full history. CSAF and VEX upload is GA.
- Policies and gates (GA) can block a release on a reachable KEV-listed finding, and the Safeguard CLI (GA) can fail a CI job when a gate fails.
- Reachability never replaces patching a KEV-listed vulnerability. It decides what you fix first, not what you are allowed to ignore forever.
Why does a CVSS-sorted backlog fail?
Most teams start with the severity column. The scanner reports hundreds of CVEs, someone sorts by CVSS, and the team works down from 9.8. It feels rigorous. It usually is not, for three reasons.
CVSS base scores describe the vulnerability, not your exposure to it. The CVSS v4.0 specification is explicit that the Base group reflects intrinsic qualities that are "constant over time and across user environments" and assumes the reasonable worst case impact. The Threat group (including Exploit Maturity) and the Environmental group are left to the consumer. The specification also notes that public assessment providers such as the National Vulnerability Database typically publish only the base score, which it labels CVSS-B. When nobody fills in Threat metrics, Exploit Maturity defaults to Not Defined, which the specification treats as Attacked for scoring purposes. In other words, an unenriched score assumes the worst on exactly the dimension that matters most for ordering work.
Severity and likelihood are different questions. FIRST, which maintains both CVSS and EPSS, says on its EPSS FAQ that the two are "empirically uncorrelated" and that high severity scores are only slightly better than random at predicting exploitation. The same FAQ warns against multiplying EPSS by CVSS: one is a calibrated probability and the other is an ordinal ranking, so the product has no interpretable meaning.
Most CVEs are never exploited, so a severity sort spends most of its effort on noise. FIRST's EPSS pages put the base rate of observed exploitation activity in any 30 day window at roughly 2 to 3% of published CVEs, and note that the KEV catalog lists roughly 0.5% of them. The FAQ on CISA's page for the now revoked BOD 22-01 makes the same point, saying less than 4% of CVEs had been publicly exploited. A list sorted by severity puts dozens of never exploited criticals ahead of a medium that is being used in attacks today.
Then there is the question no public score can answer: does your application actually execute the vulnerable code? A typical service pulls in hundreds of direct and transitive packages. Many vulnerable functions inside them are never called. A severity sort treats all of them as equally urgent.
What CISA says about prioritising by exploitation
The federal rules changed this year, so it is worth being precise.
BOD 22-01 (November 2021, now revoked). BOD 22-01 created the CISA Known Exploited Vulnerabilities catalog and required federal civilian agencies to remediate listed vulnerabilities within two weeks for CVE IDs assigned from 2021 onward, and within six months for older CVE IDs. CISA's directive page now carries a banner saying the directive has been revoked and superseded by BOD 26-04. If your internal policy still cites BOD 22-01 timelines, it is citing a retired document.
BOD 26-04, "Prioritizing Security Updates Based on Risk" (10 June 2026). BOD 26-04 revokes both BOD 22-01 and BOD 19-02 and consolidates them. It keeps the KEV catalog and its inclusion criteria unchanged, and it sets remediation urgency from four variables:
- Asset exposure. Is the vulnerable asset publicly exposed?
- KEV status. Is the CVE ID in the KEV catalog?
- Exploit automation. Can an adversary automate every step of exploitation?
- Technical impact. Does exploitation give partial or total control of the asset?
The most urgent combination requires remediation or mitigation within three days plus a forensic triage of the asset to check for compromise. The least urgent is "fix on system upgrade", meaning the fix can wait for the next scheduled major upgrade or rebuild. The directive says timelines are dynamic: taking a system off the internet changes its exposure value and relaxes the deadline, while a new KEV addition shortens it. CISA publishes KEV status, automatability and technical impact for CVE IDs through its Vulnrichment program. The full timeline table is published as an image on CISA's page, so check it there rather than relying on a summary.
BOD 26-04 binds federal civilian executive branch agencies only. CISA's KEV page still "strongly recommends" that every organisation, including private industry, address KEV catalog vulnerabilities immediately as part of its vulnerability management plan.
The KEV catalog itself. CISA adds a vulnerability only when three criteria are met: it has a CVE ID, there is reliable evidence of active exploitation in the wild, and there is clear remediation guidance such as a vendor update. Exploitability on paper is not a criterion; observed exploitation is. CISA's guidance says it aims to update the catalog within 24 hours of confirming exploitation.
SSVC. Carnegie Mellon's Software Engineering Institute created Stakeholder-Specific Vulnerability Categorization with CISA in 2019. CISA's own decision tree turns five values (exploitation status, technical impact, automatable, mission prevalence and public well-being impact) into one of four decisions: Track, Track*, Attend or Act. BOD 26-04 says its timeline table is informed by SSVC. The shift is the same everywhere: decide with exploitation evidence and context, not with a single severity number.
What NIST and the SSDF expect
NIST SP 800-218 (SSDF version 1.1, February 2022) puts prioritisation inside practice RV.2, "Assess, Prioritize, and Remediate Vulnerabilities". Task RV.2.1 asks you to analyse each vulnerability to gather enough information about risk to plan its remediation or other risk response, with risk calculations that weigh exploitability and potential impact as a notional example. Task RV.2.2 asks you to plan and implement risk responses, including a risk-based decision on whether each vulnerability will be remediated or addressed another way, such as risk acceptance.
NIST SP 800-40 Revision 4 (April 2022) names four risk responses: accept, mitigate, transfer and avoid. It also lists the real reasons a patch cannot always go in immediately: no patch exists yet, the software is end of life, testing or outage windows are needed, or other work has higher priority. The publication's point is that deciding not to patch something today is a legitimate response, as long as it is a decision and not an accident.
That is the audit angle. An auditor will not ask why you fixed the right things first. They will ask why a known vulnerability is still open, who decided that, on what evidence, and when the decision will be revisited.
What are EPSS and KEV, and how do they differ from CVSS?
The three signals answer three different questions. Mixing them up is the most common prioritisation mistake.
| Signal | Who publishes it | Question it answers | Output | Updated | What it cannot tell you |
|---|---|---|---|---|---|
| CVSS v4.0 base (CVSS-B) | FIRST owns the standard; usually scored by the vendor or NVD | How bad is this flaw in the reasonable worst case? | 0.0 to 10.0 plus a vector string | When re-scored | Whether anyone is exploiting it, or whether it affects you |
| EPSS | FIRST EPSS SIG | How likely is exploitation activity in the next 30 days? | Probability 0 to 1, plus a percentile | Daily | Impact, or whether the flaw is present and reachable in your environment |
| CISA KEV | CISA | Has this been exploited in the wild, with a fix available? | Listed or not listed, with a due date and required action | As evidence is confirmed | Anything about CVEs without confirmed exploitation, which is most of them |
| Reachability | Your own analysis tooling | Does my code actually execute the vulnerable function? | Reachable, conditionally reachable, unreachable or unknown | Every scan | Exploit likelihood or impact |
EPSS in practice
EPSS is a machine learning model that estimates the probability that exploitation activity for a published CVE will be observed in the next 30 days. FIRST publishes a probability from 0 to 1 and a percentile for every scored CVE every day, free through a CSV download and an API with no registration.
Three details from FIRST's own documentation matter when you set thresholds:
- Scores cluster near zero by design. FIRST says the mean score is around 2.8% and the median around 0.7%. A probability of 0.10 currently sits around the 95th percentile.
- There is no universal threshold. FIRST says 0.10 is commonly cited but carries no special authority, and that a 50% threshold is not sensible given a base rate near 2%. Its "Using EPSS" guide suggests that a team which acts on CVSS Critical today can get a similar volume of work from roughly the 90th EPSS percentile (about 0.04), while selecting for what the threat data supports.
- EPSS is not a risk score. It has no view of your assets, controls or impact. FIRST describes three local checks to pair it with: presence (do you have the vulnerable component), reachability (can an attacker reach the conditions for exploitation) and consequence (what happens if it is exploited).
That last point is the whole design brief for a prioritisation engine. EPSS supplies likelihood. Your tooling has to supply presence, reachability and consequence.
KEV in practice
KEV is a record of history, not a forecast. FIRST's guidance is that when a vulnerability appears in KEV, you treat it as actively exploited and prioritise it regardless of its EPSS score, and use EPSS for the large majority of CVEs that KEV does not cover. A low EPSS score on a KEV entry is not a contradiction: one measures observed past exploitation, the other predicts the next 30 days across the whole population.
Where CVSS still helps
CVSS still describes impact and attack conditions well, and its vector string says more than its number. Most teams never add the Threat and Environmental metrics per finding, so separate exploitation and reachability signals do that work instead.
What do static, dynamic and configuration reachability mean in Safeguard?
Safeguard's reachability analysis and prioritisation (GA) docs define a CVE as reachable when your code actually calls the vulnerable function or triggers the vulnerable code path. Safeguard runs three kinds of analysis:
- Static reachability. Safeguard builds a call graph from your source, lockfile and bytecode, then checks whether any path leads from your code to the vulnerable sink.
- Dynamic reachability. At runtime, a collector records which modules and functions are actually loaded and invoked in production workloads. Vulnerable paths that never execute are flagged as cold.
- Configuration reachability. Safeguard parses framework configuration, with Spring bean definitions and Next.js route files given as examples, to determine whether a piece of code is wired in at all.
The result is one of four classes:
| Class | Meaning in Safeguard's docs | How to treat it |
|---|---|---|
| Reachable | A real call path exists and is instantiated at runtime | Fix in priority order |
| Conditionally reachable | A path exists but needs a feature flag, route or configuration that is not currently enabled | Fix soon; it becomes reachable the day someone flips the flag |
| Unreachable | No call path exists in your compiled code | Defer with a recorded, expiring decision, unless KEV-listed |
| Unknown | The analyser could not decide, for example because of dynamic dispatch, reflection or native code | Treated by Safeguard as potentially reachable and not suppressed |
The "unknown" class is the honest part of the design. When the analyser cannot prove a path does not exist, Safeguard does not quietly suppress the finding.
How does Safeguard combine reachability, exploitability, runtime and business context?
Safeguard's prioritisation engine combines four signals into a single ranked backlog.
Signal 1: reachability
Described above. It answers FIRST's "presence" and part of its "reachability" check at the code level: the component is in the build, and the vulnerable function is or is not on a live path.
Signal 2: exploitability, from EPSS and KEV
Safeguard ingests the daily EPSS feed and the CISA KEV catalog. The Vulnerabilities page in ESSCM shows an EPSS score and an Exploitable flag per finding, and "Exploitable" is one of the five vulnerability categories alongside Critical, High, Medium and Low. It marks vulnerabilities with known active exploitation in the wild. The reachability docs also describe a weaponization signal: whether a public proof of concept is available, used as an additional exploit-availability input.
Vulnerability data comes from the National Vulnerability Database, GitHub Security Advisories, OSV, CISA KEV, vendor security bulletins and security researcher disclosures, and projects are re-evaluated daily as new CVEs are published.
Signal 3: runtime context
A vulnerability in a container that only runs in a local compose file is not the same as one in a production API. Safeguard's runtime collector labels each asset with:
env: production, staging, dev or sandboxexposure: internet-facing, internal or air-gappeddata_class: customer PII, PHI, PCI or publiccriticality: inferred from asset ownership and business tags
These map closely onto BOD 26-04's "publicly exposed" decision point and FIRST's "consequence" check.
Signal 4: business context
Through policy, you declare which assets are regulated (for example PCI or HIPAA scope), which handle customer data, and which sit on the critical path for revenue. Findings on those assets rank higher.
The Priority Score
Each finding gets a Priority Score from 0 to 100. Safeguard's docs describe the inputs as a function of the following, with the exact weights configurable per tenant under Settings › Prioritization:
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 defaults map to an ordering most security teams would recognise:
| Default band | Typical combination | Score |
|---|---|---|
| P0 | Reachable, KEV-listed, production | 90 to 100 |
| P1 | Reachable, EPSS above 0.5, production | 75 to 89 |
| P2 | Reachable, high CVSS, staging | 50 to 74 |
| Backlog | Unreachable, any severity | 0 to 30, auto-suppressed in some views |
Two honest notes on these defaults.
First, the score is a weighted function of many inputs, not EPSS multiplied by CVSS, which is the specific pattern FIRST warns against. It still uses the CVSS 3.1 base score as one input, so where a vendor publishes only a v4.0 score, check how your tenant handles it.
Second, the default P1 band uses an EPSS cut of 0.5. FIRST's own guidance says a 50% threshold excludes almost every scored CVE because the base rate of exploitation is around 2 to 3%. If you want EPSS to promote more reachable findings into P1, lower the EPSS weight threshold in Settings › Prioritization, or use the epss:>0.3 style filter (or a lower value) when you work the queue. Set the threshold from your remediation capacity, as FIRST recommends, and write the reason down.
How much noise does it remove?
Safeguard's docs say, based on aggregate data across customer tenants, that reachability filtering typically removes 60 to 80% of findings from the urgent queue, with further reductions from low EPSS scores, asset-class filtering and deduplication. Treat that as Safeguard's stated figure, not a promise. Your number depends on your languages (Preview languages resolve conservatively), how much dynamic code you run, and whether the runtime collector is feeding environment labels. Measure your own drop rate on the Prioritization dashboard before quoting a figure to anyone.
Step by step: from a scan to a reachability-ranked vulnerability list
This is the flow as Safeguard's docs describe it today. Where the docs do not show a screen or a command, the outcome is described instead.
-
Get the code into Safeguard. Connect the repository in the web app, or use the Safeguard CLI (GA). From the CLI, run
safeguard, type/login, then/scan .in the interactive terminal. Scanning from the terminal while signed in uploads the SBOM and findings to the platform, and the docs state the runner never uploads your source. The non-interactivesafeguard scancommands build an SBOM, match it against vulnerability databases and write JSON results locally:# A local directory (your own or proprietary code) safeguard scan --dir . # A container image safeguard scan --image nginx:latest # An SBOM you already have (SPDX or CycloneDX, detected automatically) safeguard scan --sbom sbom.jsonResults land in a folder per scan under
./output, includingvuln.json,license.jsonandcollection.json.safeguard scanexits 0 whenever the scan ran, whatever it found, so it is not a gate on its own. -
Let continuous scanning keep the list current. Safeguard re-evaluates projects daily against new CVEs, so the EPSS and KEV values attached to existing findings change without a new commit. A finding that was P2 last week can become P0 the morning CISA adds its CVE to KEV.
-
Open the project's Vulnerabilities tab in ESSCM. The table has a Priority column and a Reachability column, and the default sort is Priority descending. Each row shows the CVE ID, CVSS score, EPSS score, Exploitable flag, affected component and version, fixed version, description, references and reachability status.
-
Filter to the work that matters this week. The reachability docs list these 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 backlogYou can also filter by severity, CVSS range, EPSS range, exploitable status, component, fix availability and date range.
-
Ask in plain language if you prefer. Griffin natural language search (in-app agentic search, early access) understands the prioritisation surface. The docs give examples such as "Show my P0 findings in production that are reachable" and "Which of last week's new CVEs are reachable but not yet on KEV?"
-
Read why a finding is reachable, or why it was ranked down. Click into the finding to see its reachability class next to its EPSS and KEV data, and use View Fix to see upgrade options and breaking change warnings. The docs say Griffin can explain why a finding was de-prioritised, which the docs call essential for exception reviews. Use that explanation as a starting point for your justification, not as the justification itself.
-
Hand the top of the list to remediation. From the Vulnerabilities page, AI Remediate asks Griffin to generate remediation plans you review before pull requests are created. Autonomous remediation (GA) ships switched off, with every category set to Manual, so nothing changes in your repositories until you turn it on and choose which categories run automatically. The batch post on autonomous remediation in Safeguard covers that flow.
-
Record every deferral. For each finding you will not fix now, add a mitigation (next section) so the decision, its evidence and its review date travel with the finding.
-
Gate the release. Turn the priority logic into a policy so a reachable KEV-listed finding cannot ship. See "Turning priority into a release gate" below.
-
Watch the trend. The Prioritization dashboard shows total finding volume against the urgent queue over time, time to remediate by priority band, the drop rate for each signal, and backlog ageing. Backlog ageing is the chart an auditor will care about.
There is no CLI command for querying findings by priority yet, and the /v1 REST paths shown in the reachability docs are not available today (every path under api.safeguard.sh/v1/ returns 404, according to the banner on that page). Work the ranked list in the web app or through an assistant connected to the Safeguard MCP server (GA).
Turning priority into a release gate
Policies, gates and guardrails (GA) turn the ranking into enforcement. Policies evaluate your SBOMs and generate findings; gates are checkpoints that enforce them. Each policy has an Action that decides what a failure does to the gate: Block fails the gate, Warn passes it with a warning, and Notify passes it and sends a notification.
The reachability docs show this production admission policy as an example of reachability feeding a guardrail:
- id: no-reachable-kev-in-prod
condition: |
env == "production"
AND any(vulnerabilities,
reachability_class in ["reachable", "conditional"]
AND kev == true
)
effect: BLOCK
Note that it blocks conditionally reachable findings too. That is the right default: a feature flag is one deploy away from being on.
To fail a CI job on the gate, Safeguard's GitHub Actions docs show this workflow, which installs the CLI and runs a platform scan:
name: Safeguard
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Safeguard scan
env:
SAFEGUARD_API_KEY: ${{ secrets.SAFEGUARD_API_KEY }}
SAFEGUARD_INSTALL_DIR: ${{ runner.temp }}/safeguard-bin
run: |
set -eu
curl -fsSL https://cli.safeguard.sh/install | bash
"$SAFEGUARD_INSTALL_DIR/safeguard" ci scan --wait --fail-on critical
The docs list --fail-on-gate all as the option to fail on a policy gate. With policy outcomes turned on for your workspace, safeguard ci scan --wait --fail-on-gate <gate> waits for the scan and for the gate's policies to be evaluated, exits 0 when the gate is Passed or Warning, and exits non-zero when it is Failed, Error or Not evaluated. Policy outcomes are enabled per workspace by Safeguard, so ask for them if the Outcome column is missing from your Gates and Policies tables. The batch post on plain English security policies and release gates goes deeper on gate logic.
Safeguard's Compliance and GRC suite (GA) also includes an internal KEV framework that is on unless you switch it off in Compliance › Manage. Its scan requirement, KEV-1, checks that no component matches an entry in the KEV catalog, and its control catalog includes remediation due date tracking, patch SLA compliance and a KEV exception process. That gives your KEV handling a home in the same evidence trail your auditors already read.
Which languages and package managers have full support, and which are Preview?
Safeguard's docs say reachability quality varies by language. This is the current support table as published:
| Language or ecosystem | Static reachability | Dynamic reachability | Notes from the docs |
|---|---|---|---|
| Java, Kotlin, Scala (JVM) | GA | GA | High-fidelity analysis of compiled code |
| Python | GA | GA | Handles virtualenv layouts |
| JavaScript, TypeScript | GA | GA | Handles CommonJS, ESM and webpack bundles |
| Go | GA | GA | Module-aware call paths |
| Rust | GA | GA | Cargo-aware call-path extraction |
| C, C++ | GA | GA | Call-path reachability across common package managers |
| C#, .NET | GA | GA | NuGet-aware |
| Ruby | Preview | GA | Conservative resolution for metaprogramming |
| PHP | Preview | GA | Composer-aware; Symfony and Laravel aware |
| Swift, Objective-C | Preview | Preview | Framework-aware |
Software composition analysis (GA) builds the dependency inventory underneath this across npm, PyPI, Maven and Gradle, Go, Cargo, NuGet, Composer and Bundler, plus container layers, with deep transitive dependency scanning (GA).
What Preview means for you in practice: expect more findings to land in "unknown" or to resolve conservatively, which means more of them stay in the urgent queue. That is the safe failure mode, but it also means the noise reduction you see on a Ruby or PHP service will likely be lower than on a Go or Java one. Plan your exception workload accordingly.
How do you record a decision not to fix an unreachable finding?
Ranking a finding low is not the same as deciding not to fix it. The decision needs an owner, a reason, evidence and an expiry. Safeguard's Mitigations tab exists for this.
Mitigation types
The ESSCM Mitigations docs define four types:
| Type | When the docs say to use it |
|---|---|
| Risk Acceptance | Not exploitable in your context, remediation cost exceeds risk, a fix would break critical functionality, or temporary acceptance while you plan remediation |
| False Positive | The scanner misidentified the vulnerability, the component is not present, version detection was wrong, or a different component shares the name |
| Workaround Applied | A configuration change, network control or compensating control mitigates the issue, or partial remediation has been applied |
| Not Applicable | The vulnerable code path is not used, the feature is disabled, the component is only in development, or the platform does not apply |
An unreachable finding usually belongs under Not Applicable ("vulnerable code path is not used") or Risk Acceptance, depending on how confident you are. If the reachability class is "unknown", it is not an unreachable finding and should not be recorded as one.
Step by step: record the decision
- Open the project, go to the Vulnerabilities tab and find the finding.
- Click the ⋮ menu, then Add Mitigation.
- Fill in the form. Type, Justification, Approved By and Scope (this version only, or all versions) are required. Expiration Date (for risk acceptances) and Evidence are optional, but for an audit treat both as required.
- Save. For many findings at once, select them in the Vulnerabilities tab and use Bulk Actions › Add Mitigation.
Write the justification for a reader who was not in the room. A good one names the reachability class and how it was established, the EPSS value and KEV status on the date of the decision, the compensating controls, the ticket number for the eventual fix, and the date it will be reviewed.
What makes the record audit-ready
- Expiry and review. Risk acceptances take an expiration date (30, 90, 180 or 365 days, or a custom date). Safeguard notifies you 14 days and 7 days before expiry and on the day. Expired mitigations show up under Status: Expired for you to extend, revoke or replace with a fix.
- History. Every mitigation action is logged: creation (who, when, justification), modification, approval (approver and timestamp), expiry and revocation (who, when, reason). Open a mitigation and click its History tab to see it.
- Reports. Reports › Mitigations produces a PDF, CSV or Excel report of active and expired mitigations, a breakdown by type, an approver summary and an expiration timeline.
- Gate behaviour. Active mitigations exclude the vulnerability from gate failures. Expired mitigations do not. That means an acceptance that lapses without review starts failing your release gate again, which is the behaviour you want.
- Policy-based mitigations. Under Settings › Mitigation Policies you can auto-apply mitigations by CVE, package, severity threshold or development-only scope. Use these sparingly; a blanket rule is harder to defend than a per-finding decision.
The Vulnerabilities page also offers Mark False Positive (with a justification, submitted for approval if policy requires) and Accept Risk (with an expiration date and business justification). The Policies and Gates docs describe a parallel policy exception workflow with a justification, an expiry and an approval step, all recorded in the audit trail.
This maps onto NIST's language directly. SP 800-40r4's "accept" response and SSDF task RV.2.2's risk-based decision are what a Risk Acceptance records. A Workaround Applied record is the "mitigate" response through compensating controls.
Where VEX fits
A mitigation is your internal record. A VEX document is how a supplier tells a customer, in machine-readable form, whether a product is affected by a vulnerability.
The OpenVEX specification defines four statuses (not_affected, affected, fixed and under_investigation) and requires a not_affected statement to carry either a justification label or a free-text impact statement. The justification labels are component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary and inline_mitigations_already_exist. The same labels appear as flags in the OASIS CSAF 2.0 standard (section 3.2.3.5). An unreachable finding corresponds most closely to vulnerable_code_not_in_execute_path. The OpenVEX spec itself notes that the adversary-control and inline-mitigation justifications "could be difficult to prove conclusively", which is a good reason to avoid them unless you have evidence.
Safeguard's CSAF and VEX upload (GA) accepts CSAF 2.0, CycloneDX VEX, OpenVEX and the CSAF VEX profile. The flow in the docs:
- Go to Integrations in the sidebar and filter by Upload.
- Click Connect on the Upload CSAF/VEX card.
- Enter a name and optional description, then drag in or browse to the file. It is validated automatically.
- Set the project name and version, then click Upload.
The docs list marking vulnerabilities as not affected, documenting mitigation status and reducing false positives in vulnerability reports as uses. You can check a file before uploading it with safeguard validate, which the CLI docs say checks CycloneDX and SPDX SBOMs and VEX documents in CSAF and OpenVEX form against their specifications. The docs show it with an SBOM file:
safeguard validate sbom.json --strict -o report.json
Safeguard's internal VEX Readiness framework checks whether your SBOMs can carry VEX statements well: package URLs on components, exact versions, hashes, a unique document identifier and timestamp, and dependency relationships so status can propagate through transitive components. Those requirements cite CISA's minimum requirements for VEX. If your SBOMs fail them, VEX statements will be hard for your customers' tools to match. The batch post on ingesting and sharing VEX and CSAF in Safeguard covers the sharing side.
Where can reachability be wrong, and how do you handle it?
Safeguard's docs include a section titled "When Reachability Doesn't Help". Take it seriously. Reachability analysis is weakest in three situations.
Dynamic execution. Reflection, eval, dynamic imports, plugin loaders, dependency injection containers and metaprogramming all hide call edges from a static call graph. Safeguard marks these cases as unknown and does not suppress them. Ruby and PHP static analysis are Preview partly for this reason; the docs describe Ruby resolution as conservative for metaprogramming.
Data-driven vulnerabilities. Some flaws trigger on malicious input reaching a parser or deserialiser through a path that static analysis does not see as a call to the vulnerable function. A function can be statically unreachable and still be exercised by input. Deserialisation and template injection are the classic patterns.
Configuration the analyser cannot enumerate. Configuration reachability reads framework configuration, but it cannot see every environment variable, runtime flag or tenant-specific setting. A code path that runs only under a specific configuration may be classed as unreachable or conditional when it is live for one customer.
Practical ways to handle these limits:
- Never use reachability to defer a KEV-listed finding indefinitely. KEV means confirmed exploitation. Use reachability to choose the order among KEV findings, then fix them all. BOD 26-04's top tier for federal agencies is measured in days, and CISA recommends everyone treat KEV entries as urgent.
- Treat "unknown" as reachable. Safeguard already does this in scoring. Do the same in your mitigation decisions.
- Use dynamic reachability to check static results. Static analysis says what could run; the runtime collector shows what did run. Where both agree a path is cold, your evidence is stronger. Remember that "never executed so far" is not "cannot execute".
- Lean on the other signals. The docs say to rely on KEV, EPSS and runtime observation, with conservative defaults, where reachability is weak.
- Set short expiries on unreachable deferrals. Unreachable today can become reachable after the next code change. A 90 day review on an unreachable finding in a service that ships weekly is a reasonable starting point.
- Upgrade anyway when it is cheap. If a patch-level upgrade removes the CVE and your tests pass, the cheapest audit record is a fixed vulnerability.
Requirement, what Safeguard does, and what you still own
| Requirement or guidance | What Safeguard does | What you still own |
|---|---|---|
| BOD 26-04 decision points: exposure, KEV, automation, impact (federal agencies) | Ingests KEV, labels assets with exposure and env through the runtime collector, ranks KEV plus reachable plus production as P0 by default | Your asset inventory and exposure attestation, forensic triage, reporting to CISA, and checking automatability and technical impact through CISA's Vulnrichment data |
| KEV catalog: treat listed CVEs as actively exploited | Flags KEV and Exploitable findings, KEV filter, internal KEV framework with due date and SLA controls, gates that block reachable KEV in production | Patching every KEV-listed vulnerability you run, including in systems Safeguard does not scan |
| FIRST EPSS guidance: pair likelihood with presence, reachability and consequence | Daily EPSS feed, EPSS filters, reachability classes, runtime and business context in one score | Choosing EPSS thresholds that match your remediation capacity |
| NIST SSDF RV.2.1 and RV.2.2: analyse risk, plan and implement risk responses | Priority Score, fix options, AI Remediate, mitigations with types, approvers and expiry | The risk decision itself and who is accountable for it |
| NIST SP 800-40r4 risk responses: accept, mitigate, transfer, avoid | Risk Acceptance, Workaround Applied and Not Applicable records with history and reports | Transfer responses (insurance, contracts) and decommissioning decisions |
| VEX (CISA minimum requirements, OpenVEX, CSAF, CycloneDX) | CSAF and VEX upload, VEX validation in the CLI, VEX Readiness checks on SBOMs | Authoring the VEX statements you publish to customers and keeping them current |
What Safeguard does not do
Be clear about these limits before you build a process on top of the ranking.
- It does not guarantee the noise reduction figure. The 60 to 80% figure is Safeguard's stated figure from aggregate tenant data. Preview languages, heavy reflection and missing runtime labels all reduce what you will see.
- It does not give full static reachability for every language. Ruby, PHP, and Swift and Objective-C static reachability are Preview, and Swift and Objective-C dynamic reachability is Preview too. Languages outside the support table get dependency-level findings without call-path evidence.
- It does not see runtime without a collector. Dynamic reachability and the
env,exposureanddata_classlabels come from the runtime collector. Without it, the score leans on static analysis and the business context you declare in policy. Also check with Safeguard how static reachability behaves for projects you scan with the local runner, since the runner uploads SBOMs and findings rather than source. - It does not freeze a reachability verdict. Unreachable today can become reachable after a refactor, a new route or a flipped feature flag. Daily re-evaluation picks up new CVEs and new scans pick up code changes, but a deferral you recorded last quarter does not re-justify itself.
- It does not replace patching KEV-listed vulnerabilities. Reachability orders the work. It is not a reason to leave a confirmed, actively exploited vulnerability in place, and Safeguard's own default bands put reachable KEV findings at the very top.
- It does not make the risk decision for you. Griffin can explain why a finding was ranked down, but the justification, approver and expiry in a mitigation must come from a person who is accountable for it.
- It does not cover what you do not scan. Vendor appliances, hosted services, operating system packages on hosts you do not image, and network devices still need their own vulnerability management, and BOD 26-04 applies to all of them for federal agencies.
- It does not offer a priority query API or CLI command yet. The
/v1REST endpoints on the reachability and mitigations pages return 404 today. Use the web app, gates in CI, or the MCP server.
Common mistakes
- Sorting by CVSS and calling it risk-based. A base score is the reasonable worst case for everyone. Add exploitation and reachability, or you are prioritising by the worst day of a hypothetical customer.
- Multiplying EPSS by CVSS. FIRST says the product has no interpretable meaning. Use EPSS as a probability and CVSS as context.
- Setting the EPSS threshold at 0.5 because it sounds like "more likely than not". With a base rate near 2 to 3%, that threshold excludes almost everything. Pick a percentile you can actually work through.
- Treating "unreachable" as "safe forever". Record it, set an expiry, and re-check after major code changes.
- Recording "unknown" as "unreachable". Unknown means the analyser could not decide. It belongs in the queue.
- Letting mitigations expire silently. Expired mitigations stop excluding findings from gates. Build the 14 day and 7 day notifications into someone's week.
- Using blanket mitigation policies. "Auto-accept everything below High" is convenient and hard to defend. Prefer per-finding decisions with evidence.
- Citing BOD 22-01 timelines in 2026. BOD 22-01 was revoked on 10 June 2026. Update your vulnerability management policy to reference BOD 26-04 or your own risk-based timelines.
- Confusing the Risk Score with the Priority Score. Safeguard's Risk Score (0 to 10) rates component supply chain integrity, such as attestation level, provenance and package health. The Priority Score (0 to 100) ranks vulnerability findings. They answer different questions.
- Publishing VEX "not affected" claims with weak justifications.
vulnerable_code_cannot_be_controlled_by_adversaryis hard to prove. If your evidence is a call graph, sayvulnerable_code_not_in_execute_pathand keep the evidence.
FAQ
How does Safeguard decide if a vulnerability is reachable?
It runs three analyses. Static reachability builds a call graph from source, lockfile and bytecode and looks for a path to the vulnerable function. Dynamic reachability uses a runtime collector to see which modules and functions actually execute. Configuration reachability reads framework configuration to see whether code is wired in. Each finding is then classed as reachable, conditionally reachable, unreachable or unknown.
Is reachability analysis in Safeguard generally available?
Yes. Reachability analysis and prioritisation is GA. Per-language support varies: static reachability is Preview for Ruby, PHP, and Swift and Objective-C, and dynamic reachability is Preview for Swift and Objective-C. Everything else in the published support table is GA.
What is the difference between EPSS and CVSS?
CVSS base scores describe how severe a flaw is in the reasonable worst case. EPSS, published daily by FIRST, estimates the probability that exploitation activity will be observed in the next 30 days. FIRST says the two are empirically uncorrelated and should not be multiplied together.
Should KEV or EPSS take priority?
KEV. FIRST's guidance is to treat a KEV-listed vulnerability as actively exploited regardless of its EPSS score, and to use EPSS for the much larger set of CVEs that KEV does not cover. Safeguard's default bands follow that order: reachable plus KEV plus production is P0.
Is BOD 22-01 still in force?
No. CISA revoked BOD 22-01 and BOD 19-02 when it issued BOD 26-04, "Prioritizing Security Updates Based on Risk", on 10 June 2026. The KEV catalog and its inclusion criteria continue under BOD 26-04, which sets federal remediation timelines from asset exposure, KEV status, exploit automation and technical impact.
Does Safeguard ignore unreachable vulnerabilities?
No. Unreachable findings are ranked low (0 to 30 by default) and may be auto-suppressed in some views, but they remain in the data. You record a decision about them with a mitigation, and findings the analyser cannot decide are marked unknown and treated as potentially reachable.
How do I prove to an auditor why a vulnerability was not fixed?
Add a mitigation with the right type, a written justification, an approver, evidence and an expiry date. Safeguard logs creation, edits, approval, expiry and revocation in the mitigation's History tab, and Reports › Mitigations exports the record as PDF, CSV or Excel. Expired mitigations stop excluding the finding from gates.
Can I change how Safeguard weights EPSS, KEV and reachability?
Yes. Safeguard's docs say the exact weights are configurable per tenant under Settings › Prioritization. The defaults rank reachable, KEV-listed production findings highest and unreachable findings lowest.
Can I fail a build on reachable KEV vulnerabilities?
Yes, through a policy gate. Write a policy that blocks reachable or conditionally reachable KEV findings in production, link it to a gate, and run safeguard ci scan --wait --fail-on-gate with the gate name, or --fail-on-gate all, in CI. safeguard scan on its own does not fail on findings.
Does Safeguard generate VEX documents from my mitigations?
The docs reviewed for this post describe uploading and validating CSAF and VEX documents, not generating them from mitigations. If you publish VEX to customers, author the statements with your own tooling, validate them with safeguard validate, and keep your Safeguard mitigations as the internal record behind them.
From a ranked list to a shipped fix
Prioritisation tells you which fix to ship first, but it does not ship the fix. Once the reachable and KEV-listed findings sit at the top of your queue, the remaining risk is the time it takes a fixed version to reach production, or the time before a fix exists at all, which is what the block below covers.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo is a real Safeguard CLI session. It installs the CLI, opens the interactive terminal, and asks it to "fix the lodash CVE in this repo": the assistant reads the finding and advisory, asks before each command, edits package.json to lodash 4.17.21, runs npm install and npm test with approval, prints a summary and pushes nothing, then shows the /permissions, /model and /scan commands. This is the step after prioritisation: once a reachable finding is at the top of your list, the same terminal flow turns it into a tested change you review.
Click through it at your own pace, or open it full screen.
The second demo connects Claude to the Safeguard MCP server with OAuth and shows how you choose which tools Claude can use. It covers the connection and permission step only. Once connected, an assistant is one more way to work a ranked backlog without a priority API, and the related post on asking Claude whether a finding is reachable walks through that use.
Click through it at your own pace, or open it full screen.
The third demo sets up the local runner on your own machine, approves it with its device code, and then scans from the terminal. Use it when you want findings in the platform's prioritised view without granting Safeguard repository access, and confirm with Safeguard how reachability evidence is gathered for runner-scanned projects.
Click through it at your own pace, or open it full screen.
To try the ranked view on your own code, download the apps and CLI at safeguard.sh/download and read the setup guides at docs.safeguard.sh/docs.
For more depth, read inside Safeguard's reachability engine, how SCA reachability handles indirect dependencies, Safeguard research on reachability noise reduction, scanning a repo and reading a reachable finding with the CLI, and the batch overview of what Safeguard does today, every capability labelled.
Quick check
A finding is KEV-listed but Safeguard classes it as unreachable. Can you close it with a Not Applicable mitigation and move on?
Not as a permanent answer. KEV means confirmed exploitation in the wild, and reachability can be wrong for reflection, data-driven flaws and configurations the analyser cannot enumerate. Fix it in priority order among your KEV findings; if you must defer, record a short, expiring risk acceptance with evidence and a ticket for the fix.
Your team wants to treat every finding with an EPSS score above 0.5 as urgent. What does FIRST's guidance suggest instead?
FIRST says a 50% threshold excludes almost every scored CVE because the base rate of observed exploitation is around 2 to 3%. Pick a threshold from your remediation capacity, for example a percentile you can sustain, and combine it with presence, reachability and consequence, which is what Safeguard's Priority Score does.
Sources
- Safeguard docs: Vulnerability Prioritization (Reachability Analysis)
- Safeguard docs: ESSCM Vulnerabilities
- Safeguard docs: ESSCM Mitigations
- Safeguard docs: Risk Score (RS)
- Safeguard docs: Policies and Gates
- Safeguard docs: Policy outcomes and actions
- Safeguard docs: CLI Usage
- Safeguard docs: GitHub Actions
- Safeguard docs: CSAF/VEX Upload
- Safeguard docs: CISA KEV internal framework
- Safeguard docs: VEX Readiness internal framework
- FIRST: Exploit Prediction Scoring System (EPSS)
- FIRST: EPSS FAQ
- FIRST: Using EPSS
- FIRST: CVSS v4.0 Specification Document
- CISA: Known Exploited Vulnerabilities Catalog
- CISA: Reducing the Significant Risk of Known Exploited Vulnerabilities (KEV criteria)
- CISA: BOD 22-01 (Revoked)
- CISA: BOD 26-04, Prioritizing Security Updates Based on Risk
- CISA: BOD 26-04 Implementation Guidance
- CISA: Stakeholder-Specific Vulnerability Categorization (SSVC)
- CISA: Minimum Requirements for Vulnerability Exploitability eXchange (VEX)
- NIST SP 800-40 Rev. 4, Guide to Enterprise Patch Management Planning
- NIST SP 800-218, Secure Software Development Framework version 1.1
- OpenVEX specification
- OASIS CSAF version 2.0
- CycloneDX: Vulnerability Exploitability eXchange (VEX)
Last reviewed: 24 October 2026. Product capabilities change; check docs.safeguard.sh for the current state.