Reachability analysis tells you whether your code actually calls the vulnerable function inside a dependency. In Safeguard every finding carries a reachability class and a 0 to 100 Priority Score, so in practice you sort by Priority, filter to reachable findings in production, fix those first from the CLI or with AI Remediate, then gate on them.
This is a working guide, not a theory piece. By the end you will have:
- A clear picture of what Reachable, Conditionally reachable, Unreachable and Unknown mean, and what to do with each.
- A repeatable triage routine in the Vulnerabilities tab, with the exact filters to type.
- A fix loop in the Safeguard CLI that edits the dependency, runs your tests with your approval and pushes nothing on its own.
- A policy that fails a build only when a reachable, known exploited vulnerability reaches production.
- A short list of the traps that make reachability data look wrong when it is not.
You do not have to give up the AI tools you already use. Bring the Claude, ChatGPT or Codex plan you already pay for and use it inside the Safeguard CLI. Safeguard adds no extra charge for that usage; your plan's own usage limits and terms still apply. If you would rather stay in the chat you already use, connect Claude or ChatGPT to the Safeguard MCP server and ask about your findings from there.
How Safeguard decides a finding is reachable
A typical enterprise application pulls in hundreds of direct and transitive dependencies, and some of them have known vulnerabilities at any given moment. Most of those vulnerabilities sit in functions your application never calls. Safeguard's docs describe reachability as the signal that tells you which ones are actually relevant, so the team patches what matters.
A vulnerability is "reachable" when your code calls the vulnerable function or triggers the vulnerable code path. Safeguard looks at this three ways.
Static reachability. Safeguard builds a call graph from your source, lockfile and bytecode, then checks whether any path leads to the vulnerable sink.
Dynamic reachability. Where a runtime collector records which modules and functions are loaded and invoked in production workloads, paths that never execute are flagged as cold.
Configuration reachability. Safeguard parses framework configuration, such as Spring bean definitions or Next.js route files, to work out whether a piece of code is wired in at all.
The result is one of four classes:
| Class | What it means | What to do first |
|---|---|---|
| Reachable | A real call path exists and is instantiated at runtime | Fix now, starting with production and anything on KEV |
| 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 that flag |
| Unreachable | No call path exists in your compiled code | Backlog; upgrade during normal dependency work |
| Unknown | The analyzer could not decide, for example dynamic dispatch, reflection or native code | Treat as reachable; Safeguard marks it as potentially reachable and does not suppress it |
Reachability is one of four signals
Reachability on its own does not set the order. Safeguard's prioritization engine combines four signals into a single ranked backlog:
- Reachability, as above.
- Exploitability. Safeguard ingests the daily EPSS feed, checks CISA's Known Exploited Vulnerabilities (KEV) list, and tracks public proof of concept availability as a weaponization signal. FIRST describes EPSS as an estimate of the probability that a published CVE will be exploited in the wild in the next 30 days, published every day as a score from 0 to 1. CISA says organizations should use KEV as an input to their vulnerability management prioritization.
- Runtime context. Assets carry labels for environment (production, staging, dev, sandbox), exposure (internet facing, internal, air gapped), data class (customer PII, PHI, PCI, public) and criticality.
- Business context. Through policy you declare which assets are regulated, 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. The default bands in the docs are:
| 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 |
The weights are configurable per tenant under Settings → Prioritization. Keep the defaults for your first month so you have a baseline before tuning.
Safeguard's docs put the typical effect of reachability filtering at 60 to 80 percent fewer findings in the urgent queue, based on aggregate data across customer tenants. Your own number depends on your languages and how much of each library you use, so measure it on your projects rather than assuming it.
Language support
Analysis quality varies by language. This is the current support table from the docs:
| Language or ecosystem | Static | Dynamic |
|---|---|---|
| Java, Kotlin, Scala (JVM) | GA | GA |
| Python | GA | GA |
| JavaScript, TypeScript | GA | GA |
| Go | GA | GA |
| Rust | GA | GA |
| C, C++ | GA | GA |
| C#, .NET | GA | GA |
| Ruby | Preview | GA |
| PHP | Preview | GA |
| Swift, Objective-C | Preview | Preview |
If most of your code is in a Preview column, expect more Unknown results and lean harder on KEV and EPSS.
Step by step: your first reachability triage
Run this on one project first.
- Get scan data in. Connect the repository as an integration so the platform scans it, or scan from your own machine with the Safeguard CLI (
safeguard scan --dir .). Safeguard re-evaluates projects daily as new vulnerabilities are disclosed, so you do not need to rescan to catch a new advisory. - Open the project and go to the Vulnerabilities tab. The table has a Priority column and a Reachability column, and the default sort is Priority, highest first.
- Type the P0 filter. In the filter box enter
env:production reachable:true kev:true. This is the "drop what you are doing" list: reachable, known exploited, in production. On a healthy project it is often empty. If it is not, stop here and fix these before anything else. - Widen to P1. Replace
kev:truewithepss:>0.3to see reachable findings with a meaningful exploit probability. Then tryreachable:truealone to see the full reachable set across environments. - Open a finding. Each record shows the CVE ID, CVSS score, EPSS score, whether it is exploited in the wild, the affected component and version, the fixed version, references and the reachability status. Click View Fix to see upgrade options and any breaking change warnings.
- Decide. For each reachable finding, pick one of three outcomes: fix it, mitigate it with a documented reason, or accept the risk with an expiry date. Do not leave it open without an owner.
- Record the decision. Mitigations are added from the finding's menu with Add Mitigation. The types are Risk Acceptance, False Positive, Workaround Applied and Not Applicable. Not Applicable explicitly covers "vulnerable code path is not used", which is the honest label for a finding you have checked by hand. Every mitigation needs a justification and an approver, and risk acceptances take an expiry date (30, 90, 180 or 365 days, or a custom date).
- Save the sweep. Open Search and ask Griffin the same question in plain English, for example "Show my P0 findings in production that are reachable." Star the conversation. Next week, open it from Starred searches instead of retyping filters.
- Check the trend. The Prioritization dashboard shows total findings against the urgent queue over time, time to remediate by priority band, how much each signal filtered out, and backlog aging. Look at it once a week, not once a day.
Two more Griffin prompts from the docs are worth keeping: "Which of last week's new CVEs are reachable but not yet on KEV?" catches findings about to become urgent, and "Summarize the top 10 priorities for the payments service" is ready for a standup.
When reachability does not help
Reachability is a strong filter, not a verdict. The docs list three places where it is weakest:
- Code that runs through reflection, eval or unsafe dispatch. Safeguard marks these Unknown and does not suppress them.
- Data driven vulnerabilities, where the danger is a malicious input even though static analysis shows no path.
- Paths that only run under configurations the analyzer cannot enumerate.
For these, fall back on KEV, EPSS, runtime observation and conservative defaults. A KEV listed Unknown in production deserves the same urgency as a KEV listed Reachable.
Fix what is reachable, from the platform or your machine
Once the list is short, you have two routes to a fix.
AI Remediate in the platform. On a finding, click AI Remediate, review the proposed change and click Create Pull Request. Griffin analyzes the vulnerability and your codebase, picks the safest upgrade path, checks for breaking changes and opens a PR with a clear title, a description of the vulnerability, a summary of changes, breaking change warnings and test results when tests are configured. Bulk AI Remediate batches compatible fixes into one PR per repository. Breaking changes are rated Safe, Minor, Major or Critical; review the last two and do not auto-merge them. Automatic fix PRs are off by default; you turn them on and scope them under Settings → AI Remediate, where you can also require approval before PRs are created and run tests first.
Griffin can also explain why a finding was de-prioritized, which is the answer you need when an exception review asks why a high CVSS finding sits in the backlog.
The Safeguard CLI on your machine. This is the route when you want the change in your own working tree, your own test run and your own commit. The next section walks through it.
Do the work properly with the Safeguard CLI
The Safeguard CLI scans code, container images and SBOMs on your own machine, sends only the results to the platform, and includes an assistant that can fix what it finds. It writes code, not only scans. For reachability work, that means you can go from "this finding is reachable" to "the fix is tested and ready to commit" without leaving the terminal.
Install
On macOS and Linux:
curl -fsSL https://cli.safeguard.sh/install | bash
safeguard --version
On Windows, in PowerShell:
irm https://cli.safeguard.sh/install.ps1 | iex
It is a single static binary with no Node, Python or JVM to install alongside it. It runs on macOS, Linux and Windows, on x86_64 or arm64 (Windows ships x86_64 only). Step by step platform guides are in installing the Safeguard CLI on macOS.
First session
Run safeguard with no arguments to open the interactive terminal. Type /login and approve the session in your browser. Then:
/scan .
how many critical vulns do we have
/scan . scans the folder you are in. The plain language question is answered from your Safeguard data. Type @ to attach a file from the folder you started in, such as a lockfile, and /resume to pick up the last session in that folder tomorrow.
Fix a reachable finding
Take the top reachable finding from your triage and ask for it by name. The recorded session below shows exactly this on a sample project called payments-api with test data. After installing with the curl command (checksum verified) and checking safeguard --version, the user opens the interactive terminal and asks "fix the lodash CVE in this repo". The assistant reads the finding, the files and the advisory, then asks before each command, where you press y or n. It edits package.json to lodash 4.17.21, runs npm install and npm test with approval, and prints a summary with a suggested commit message. It pushes nothing. The session ends with /permissions, /model and /scan ..
Click through it at your own pace, or open it full screen.
If you prefer a direct command to a conversation, safeguard fix updates vulnerable dependencies. Preview first:
safeguard fix --package lodash --patch-only --dry-run
--dry-run shows the change without touching files, --patch-only limits it to patch upgrades, and --severity sets the minimum severity to fix (the default is high). When you are happy, run it without --dry-run, or add --create-pr --draft with --repo-url to open a draft pull request instead of editing locally. Then have the CLI read the result:
safeguard review --base main
safeguard review reviews working tree changes for correctness and security, against a git ref when you pass --base. For more on that step, see reviewing AI generated code before merge.
If an edit goes wrong, the CLI keeps file checkpoints for each session. safeguard checkpoints list --session followed by the session id shows them, and a restore with --dry-run previews the diff before anything moves. Files the agent writes or edits are snapshotted before each change; anything a shell command writes is not, so commit before you let it run a script that generates files.
Use your Claude, ChatGPT or Codex plan
As noted at the top, the Claude, ChatGPT or Codex plan you already pay for works inside the CLI with no extra Safeguard charge, under your plan's own limits and terms. /model shows or switches the model, and the CLI docs cover connecting one.
Approvals
/permissions chooses how often the assistant asks before it acts:
| Mode | Behaviour |
|---|---|
| Ask | Reads run; edits and commands ask first |
| Accept edits | Edits in this folder and read only commands run; other commands ask |
| Plan | Read and plan only |
| Auto | Read only commands and what the policy allows run; anything else asks. The default |
| Strict | Pre-approved actions only, for CI |
There is also a --yolo flag that approves everything. Keep it for throwaway sandboxes. For dependency fixes, Ask or Accept edits is a good fit: you see every npm, pip, Maven or Go command before it runs.
Approve from the desktop or your phone
A CLI session is live. It shows in the web app, the desktop app and the phone app, and other devices can send work to it. In the recorded Android session below, the same conversations appear on the phone; "Work on my computer" picks a computer running the CLI and a folder; the command waits for Allow once, Allow for turn or Deny on the phone; and the answer shows the tests passed.
Click through it at your own pace, or open it full screen.
The download page lists the desktop app (3.2.2) for Windows, macOS and Linux, the Android app on Google Play (plus a direct APK) and the iOS app on the App Store. Start a fix in the terminal, check it on the desktop, approve the test run from your phone. The Android guide covers phone setup.
CI with safeguard exec
safeguard exec runs one non-interactive assistant turn, which is handy for a pipeline step that writes a readable summary next to the raw results:
safeguard exec "summarise the critical findings in this repository"
Its default permission mode is strict: nothing is asked, and anything not pre-approved by the policy file is denied. It also refuses to run outside a git repository, because there would be no way to undo what it changes. The full setup is in running the Safeguard CLI in CI with safeguard exec.
Your source code stays on your machine when the local runner runs platform scan jobs; only SBOMs and findings are uploaded.
Turn reachability into a policy
Triage clears the backlog; a policy keeps it from refilling. Reachability data feeds directly into Safeguard's guardrails, so you can block on the combination that matters instead of raw CVSS.
The docs give this production rule as an example:
- id: no-reachable-kev-in-prod
condition: |
env == "production"
AND any(vulnerabilities,
reachability_class in ["reachable", "conditional"]
AND kev == true
)
effect: BLOCK
Read it carefully. It blocks on Reachable and Conditionally reachable, because a conditional path is one configuration change away from live. It does not block on Unreachable findings with a high score, which is the noise you are trying to remove from the release path.
Policies are created under Settings → Policies with the visual builder or YAML, and each policy's Action decides what a failure does to a gate: Block fails the gate, Warn passes it with a warning naming the policy, and Notify passes it and sends a notification. Gates are evaluated by the platform on the scans it runs for your connected repositories, not by the CLI.
To fail a CI job on the gate, run the scan on Safeguard and wait for the gate:
safeguard ci scan --wait --fail-on-gate Release
That waits for the scan and for the gate's policies to be evaluated against it. Passed and Warning pass the step; Failed, Error or Not evaluated fail it. It needs policy outcomes turned on for your workspace, which Safeguard does per workspace; if you do not see an Outcome column on the Gates and Policies tables, ask your Safeguard contact.
Start every new reachability policy on Warn for a week or two. Read what it would have blocked, fix the obvious ones, then switch it to Block.
Troubleshooting and common mistakes
Troubleshooting
A finding shows Unknown instead of a class. Check the language table. Ruby, PHP and Swift static analysis are in Preview, and reflection or native code anywhere produces Unknown. Unknown is treated as potentially reachable on purpose.
The P0 filter returns nothing, and you expected something. The production bands depend on assets carrying an environment label. If your production service is not labelled as production, env:production cannot match it. Try reachable:true kev:true without the environment term to confirm.
The CLI scan works locally but fails in CI. safeguard scan runs syft and grype, and a fresh CI runner does not have them. Install both on the PATH or point TOOLS_DIR at them. safeguard doctor checks the installation, sign-in and tools without changing anything.
You want to query priority from a script. The docs say there is no CLI command for querying findings by priority yet, and the /v1 REST paths on that page are not available yet. Use the Vulnerabilities tab, Search, or the hosted MCP server.
Common mistakes
- Treating Unreachable as fixed. It is backlog, not closed. Reachability can change with the next commit that imports a new function. Upgrade during routine dependency work.
- Ignoring Conditionally reachable. The feature flag that keeps it cold today is a configuration change away from production.
- Suppressing without an expiry. Risk acceptances should expire. Expired mitigations stop excluding findings from gate failures, which is the point.
- Fixing the manifest and not the lockfile. Scanners read what is installed. Let the assistant run the install so the lockfile changes too, and read that diff.
- Merging a Major or Critical breaking change unreviewed. AI Remediate flags these for a reason.
- Expecting
safeguard scanto fail the build. It reports findings and exits 0 when the scan ran. Usesafeguard ci scanwith--fail-onor--fail-on-gateto fail a step.
Frequently asked questions
What does "reachable" mean in Safeguard?
A real call path exists from your code to the vulnerable function and is instantiated at runtime. Safeguard works this out with static call graphs, runtime observation where a collector is present, and framework configuration parsing.
Is an unreachable vulnerability safe to ignore?
No. It is safe to schedule. Unreachable findings land in the 0 to 30 backlog band, and you should still upgrade them during normal dependency work, because a future code change can make them reachable.
Which languages does reachability analysis support?
Static and dynamic analysis are GA for JVM languages, Python, JavaScript and TypeScript, Go, Rust, C and C++, and .NET. Ruby and PHP static analysis are in Preview with dynamic GA, and Swift and Objective-C are in Preview for both.
Can I keep using Claude, ChatGPT or Codex?
Yes. Bring the Claude, ChatGPT or Codex plan you already pay for and use it inside the Safeguard CLI. Safeguard adds no extra charge for that usage; your plan's own usage limits and terms still apply. You can also connect Claude or ChatGPT to the Safeguard MCP server and ask about reachable findings from the chat you already use. See connecting Claude to the Safeguard MCP server.
Can I fail a build only on reachable findings?
Yes, through a policy and a gate. Write a policy that matches reachable or conditional findings, set its Action to Block, link it to a gate, and run safeguard ci scan --wait --fail-on-gate with the gate name in your pipeline. Policy outcomes must be on for your workspace.
Does my source code leave my machine?
Not when the local runner runs platform scan jobs; only SBOMs and findings are uploaded. In CI, safeguard ci scan --local scans the checkout on the runner and uploads only the results.
Does reachability replace EPSS and KEV?
No. It is one of four signals. A KEV listed finding marked Unknown still deserves urgency, and EPSS helps you order reachable findings that are not on KEV yet.
Try it yourself
This walkthrough sets up the runner on your own machine: pick the platform, start it, approve it with its device code, then use the same CLI to scan from the terminal.
Click through it at your own pace, or open it full screen.
Ready to triage your own projects? Download Safeguard, run safeguard, type /login, then /scan ., and open the Vulnerabilities tab sorted by Priority. The full reference is in the CLI docs.
Quick check
What should you do with a finding marked Unknown?
Treat it as reachable. Unknown means the analyzer could not decide, often because of reflection or native code, and Safeguard deliberately does not suppress it.
Which filter shows the P0 backlog in the Vulnerabilities tab?
env:production reachable:true kev:true shows reachable, known exploited findings in production.
Does safeguard scan fail a build when it finds a critical vulnerability?
No. It reports findings and exits 0 when the scan ran. Use safeguard ci scan with a fail on severity or fail on gate option to fail the step.
Sources
- Safeguard docs: Vulnerability Prioritization (Reachability Analysis)
- Safeguard docs: Vulnerabilities
- Safeguard docs: AI Remediate
- Safeguard docs: Mitigations
- Safeguard docs: Search
- Safeguard docs: Guardrails and Enforcement
- Safeguard docs: Policy outcomes and actions
- Safeguard docs: CI/CD Integration
- Safeguard docs: Safeguard CLI
- Safeguard docs: CLI interactive terminal
- Safeguard downloads
- FIRST: Exploit Prediction Scoring System (EPSS)
- CISA: Known Exploited Vulnerabilities Catalog
Last reviewed: 21 October 2026.