When a vulnerability is disclosed in a package you use, read the advisory for exact versions, find every project that contains it, rank each by exploit evidence and reachability, fix the urgent ones with a reviewed change, verify with a fresh scan, and record the projects that are not affected. The Safeguard CLI does most of this on your machine.
This is a general playbook for whoever gets the message "are we affected?" and needs an answer for every project. The steps are the same whether the package is a parser in Node, a logging library in Java or a module in Go.
You do not have to give up the assistant you already use to follow it. 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 your projects from the chat you already use.
What you will have at the end
By the end of one pass through this playbook you will have:
- A one line fact card for the advisory: package, ecosystem, affected range, fixed version and what an attacker needs.
- A list of every project, service and container image that contains an affected version, with the path that pulls it in.
- A priority for each one, based on evidence rather than the headline score.
- Reviewed fixes for the urgent projects, each one tested, with nothing pushed until you say so.
- A fresh scan proving the affected version is gone, and a CI check that stops it coming back.
- VEX statements for the projects that contain the package but are not affected, validated and ready to share with customers.
Keep one notes file open while you work. By the end it is your incident record.
The playbook, step by step
1. Read the advisory for facts, not headlines
The first thing you see is usually a headline and a score. Find the advisory itself and write down five facts:
- Package and ecosystem. The exact name on its registry. Two packages in different ecosystems can share a name.
- Affected range. Which versions are vulnerable, for example "below 3.4.2" or "2.0.0 up to 2.9.1".
- Fixed versions. Often more than one, one per maintained release line.
- What is vulnerable. A function, a parser option, a configuration. This is what reachability will check later.
- Preconditions. Does the attacker need network access, a crafted file, a specific setting turned on?
Cross check the range with OSV, which aggregates databases in the OpenSSF Vulnerability format and has a public API:
curl -s -X POST https://api.osv.dev/v1/query \
-d '{"package": {"name": "example-parser", "ecosystem": "npm"}, "version": "3.4.1"}'
Write the fact card at the top of your notes file:
ADVISORY: example-parser (npm)
Affected: below 3.4.2 | Fixed: 3.4.2, 2.11.7
Vulnerable: parse() with allowExpressions enabled
Precondition: attacker controls input passed to parse()
Sources: vendor advisory, OSV
Advisories change in their first days, so note when you read it.
2. Find every project that contains it
You need two answers: which projects contain the package, and at which versions.
From your Safeguard data. If your projects are already scanned into Safeguard, their SBOMs are already there. Open the CLI and ask in plain language:
which of our projects include example-parser, and at which versions?
The assistant has tools for your Safeguard data and for local files, and picks whichever the question needs. You can ask the same question from Claude or ChatGPT once they are connected to the Safeguard MCP server.
From your machine. For repositories you have checked out, sweep them with SBOMs. safeguard sbom generates an SPDX JSON SBOM for a folder using the same path the scanner uses, and it works whether or not vulnerability databases have caught up with the advisory yet:
#!/usr/bin/env bash
# sweep.sh: which checked-out repos contain the package?
PKG="example-parser"
mkdir -p sboms
for d in ~/code/*/; do
name=$(basename "$d")
safeguard sbom --dir "$d" --output "sboms/$name-sbom.json" >/dev/null 2>&1
if grep -qi "$PKG" "sboms/$name-sbom.json"; then
echo "$name: contains $PKG"
fi
done
A plain text match can catch similar names, so confirm each hit with the ecosystem's own tool, which also shows the path that pulls the package in:
# npm: every path to the package, not only direct dependencies
npm ls example-parser --all
# Maven: filter the tree to one artifact
mvn dependency:tree -Dincludes=com.example:example-parser
# Gradle: why this version was selected
./gradlew -q dependencyInsight --dependency example-parser --configuration runtimeClasspath
# Go: shortest import path from your module to the module
go mod why -m example.com/parser
# Python: installed version and what it requires
python -m pip show example-parser
From your images. A service can be clean in source and still ship the package inside its container image through the base image or a system package. Scan the image you actually deploy:
safeguard container-scan --image registry.example.com/payments-api:2026.10.20
Note every hit: project, version, direct or transitive, and the parent that pulls it in.
3. Rank each project by evidence
Not every hit needs a fix today. Use these signals together:
| Signal | Where it comes from | What it tells you |
|---|---|---|
| Affected version present | Your SBOM sweep and npm ls style checks | Whether the project is in scope at all |
| Known exploited | CISA Known Exploited Vulnerabilities catalog (CSV and JSON feeds) | Attackers are using it now |
| Exploit probability | FIRST EPSS, published daily for every CVE | Estimated chance of exploitation in the next 30 days |
| Severity | CVSS v4.0 Base, plus Threat and Environmental metrics | How bad a successful attack is in your setting |
| Reachability | Safeguard reachability analysis | Whether your code can reach the vulnerable function |
| Exposure | Your own knowledge of the service | Internet facing, handles untrusted input, holds sensitive data |
CISA describes the KEV catalog as input to your prioritisation framework: a listing moves a project up, it does not replace judgement. FIRST makes a similar point, that CVSS is not just the Base score.
Turn the signals into four buckets and write one next to each project:
- Today. Affected, reachable, exposed, and either known exploited or high exploit probability.
- This week. Affected and reachable, but not exposed or with low exploit evidence.
- Next routine upgrade. Affected version present, vulnerable code not reachable.
- Not affected. Package present but outside the affected range, or the vulnerable feature is off. These still get a record in step 6.
4. Fix the urgent projects with a reviewed change
Start with the "today" bucket. Each fix takes one of three shapes.
The package is a direct dependency. Upgrade it to the fixed version on your release line and update the lockfile in the same change:
npm install example-parser@3.4.2 # npm
go get example.com/parser@v1.8.3 && go mod tidy # Go
python -m pip install "example-parser>=3.4.2" # then update requirements
The package arrives transitively. First check whether a newer version of the parent already pulls in the fix; upgrading the parent is the cleanest change. If not, pin the transitive version. In npm, the overrides field in the root package.json replaces a package anywhere in the tree, and npm's docs list replacing a version with a known security issue as a typical use:
{
"overrides": {
"example-parser": "3.4.2"
}
}
Maven and Gradle have their own ways to force a version; use the one your build already uses, and confirm with mvn dependency:tree or dependencyInsight afterwards.
There is no fixed version yet. Turn off the vulnerable feature if the advisory says how, add input validation in front of it, or restrict who can reach the service. Record the project as affected with the mitigation you applied, and look again when the maintainers publish a fix.
The Safeguard CLI can make the dependency change for you. Preview first, then apply:
safeguard fix --package example-parser --dry-run
safeguard fix --package example-parser --patch-only
--dry-run previews the changes without modifying files, and --patch-only keeps the upgrade to a patch version. If your team prefers pull requests, safeguard fix can open one with --create-pr --draft and a --repo-url. On the platform, autonomous remediation can open fix PRs for you too; it is off by default and you choose where to turn it on.
Then run the full test suite.
5. Verify the fix and stop it coming back
A fix is done when a fresh scan agrees. Rescan the folder and check that the package now resolves to a fixed version:
safeguard scan --dir .
grep -i "example-parser" output/*/vuln.json || echo "no matches for example-parser"
npm ls example-parser --all
safeguard scan builds an SBOM, matches it against vulnerability databases and writes JSON results under ./output, including vuln.json and license.json.
Then make sure the next merge cannot bring the old version back. In CI, safeguard ci scan scans the commit as a new version of its Safeguard project. With --local the scan runs on the CI runner and only the results are uploaded:
safeguard ci scan --local --wait --fail-on high
safeguard ci scan --wait --fail-on-gate Release
The second form waits for the platform to evaluate a gate's policies against the scan and fails the step when the gate fails. Policies and gates are evaluated in the platform, not by the CLI, so the rule lives in one place for every project. The CI guide for safeguard exec covers the pipeline setup in more detail.
6. Record the projects that are not affected
Customers will ask about this disclosure for months. Projects that are not affected need a machine readable record of why. VEX does this. OpenVEX defines four statuses: not_affected, affected, fixed and under_investigation. A not_affected statement needs a justification label or an impact statement; the labels include component_not_present, vulnerable_code_not_present and vulnerable_code_not_in_execute_path.
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://example.com/vex/payments-api-2026-10-21",
"author": "Security team",
"timestamp": "2026-10-21T10:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": { "name": "ADVISORY-ID" },
"products": [ { "@id": "pkg:npm/payments-api@4.2.0" } ],
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"impact_statement": "parse() is only called with allowExpressions disabled."
}
]
}
Validate it before it leaves the building:
safeguard validate vex/payments-api.json --strict
safeguard validate checks CycloneDX, SPDX and VEX documents, and --strict treats warnings as errors.
7. Close the loop
Two days later, read the advisory again. If the range widened, rerun the sweep. If a fix appeared for a project you mitigated, move it to "today". Then file the notes.
Do the work properly with the Safeguard CLI
The Safeguard CLI puts the sweep, the questions, the fix and the verification in one terminal session. It 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 it, so a finding turns into a reviewed change without switching tools. Your source code stays on your machine; only SBOMs and findings are uploaded.
Install
On macOS and Linux:
curl -fsSL https://cli.safeguard.sh/install | bash
On Windows, in PowerShell:
irm https://cli.safeguard.sh/install.ps1 | iex
It is a single static binary with no Node, Python or JVM needed, for macOS, Linux and Windows on x86_64 or arm64 (Windows is x86_64 only). Platform specific steps are in the macOS install guide. If anything looks wrong afterwards, safeguard doctor checks the installation, sign in and tools and prints what is wrong without changing anything.
First session on an affected repository
cd ~/code/payments-api
safeguard
Type /login to sign in through the browser, then /scan . to scan the folder. Then ask what you need to know, in plain language:
is example-parser in this repo, which version, and what pulls it in?
does our code call parse() with allowExpressions enabled anywhere?
Type @ to attach a file from the folder you started in, such as the advisory saved as text or your package.json, so the assistant reads exactly what you mean.
Writing and fixing code
Ask for the fix the way you would ask a colleague:
upgrade example-parser to 3.4.2, update the lockfile, run the tests and summarise the change
The recorded session below shows the same flow on a sample project called payments-api with test data. After installing with the curl command (checksum verified) and checking safeguard --version, the user runs safeguard 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 an edit goes wrong, /rewind or safeguard checkpoints restore with --dry-run previews putting files back as they were. Checkpoints cover files the agent writes or edits; anything a shell command writes, such as a lockfile rewritten by npm install, is not snapshotted, so keep the work on a branch.
Using your Claude, ChatGPT or Codex plan
As covered at the top, you can keep the plan you already pay for and use it in the CLI, with your plan's own limits and terms. Run /model to see or switch the model for the session, and see the CLI docs for connecting one. To ask about disclosures from Claude itself, follow connecting Claude to the Safeguard MCP server.
Approvals
/permissions sets how much the assistant may do without asking:
- 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. Useful on a repository you do not know, to see the whole plan before anything changes.
- Auto: read only commands and what the policy allows run; anything else asks. This is the default.
- Strict: pre approved actions only, for CI.
There is also a --yolo flag that approves everything. Keep it for throwaway sandboxes, never for a production repository during an incident.
Approving from your phone
Disclosures do not wait for you to be at your desk. 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.
CI with safeguard exec
For a summary across the run, safeguard exec runs one non interactive assistant turn:
safeguard exec "summarise the critical findings in this repository" --output-format json
Its default permission mode is strict: nothing is asked, and anything the policy file does not pre approve is denied. It refuses to run outside a git repository, because there would be no way to undo what it changes.
A worked example across three projects
An illustrative run: a Node payments service, a Java reporting service and a Go worker under ~/code. The package is made up; the commands are real.
09:10, fact card. The advisory says example-parser below 3.4.2 is affected when parse() runs with expressions enabled. Fixed in 3.4.2. The OSV query confirms the range.
09:20, sweep. sweep.sh reports two hits: payments-api and reporting-service. The Go worker is clean. npm ls example-parser --all in payments-api shows version 3.3.0, pulled in by a templating library. In reporting-service, the hit is a Java package with a similar name; mvn dependency:tree -Dincludes shows it is a different artifact. One real hit, one false match, which is why step 2 confirms with the build tool.
09:45, rank. The advisory is not in the KEV catalog. Reachability shows payments-api calls parse() on request bodies. The service is internet facing. Bucket: today.
10:00, fix. The templating library's latest release already depends on 3.4.2, so the assistant proposes upgrading the parent instead of an override. In Ask mode it shows the package.json edit, then waits for approval before npm install and npm test. Tests pass. It suggests a commit message and pushes nothing.
10:20, verify. safeguard scan --dir . no longer lists the package in vuln.json, and npm ls shows 3.4.2. The branch goes through review, and the pipeline's --fail-on-gate Release step passes.
10:40, record. reporting-service gets a VEX statement with component_not_present, validated with safeguard validate --strict. The notes file goes into the incident folder with a reminder to reread the advisory on Thursday.
Troubleshooting
The scan does not list the advisory at all. Databases take time to pick up new disclosures. Trust the SBOM sweep in step 2 for presence, and rerun the scan later for the database match.
npm ls shows two versions of the package. Different parents pull in different versions. Fix each path, or use an override, then check npm ls again until only fixed versions remain.
The upgrade breaks the build. Use safeguard fix --patch-only to stay on your release line, and read the changelog before a major jump.
The assistant says it cannot run a command. Check the mode in the bar under the input. Plan and Strict modes do not run unapproved commands. Switch with /permissions for the session.
safeguard exec exits immediately in CI. Make sure the job runs inside a git checkout; exec refuses to run outside one unless you pass --skip-git-repo-check.
Sign in or tool problems. Run safeguard doctor and read its lines; the exit code is 0 whenever the checks ran.
Common mistakes
- Ranking by the headline score. A critical score on unreachable code can wait; a medium on an exposed, reachable path may not.
- Checking only direct dependencies. Most packages in a modern project arrive transitively. Always use
--allwithnpm lsand the full tree in Maven and Gradle. - Forgetting container images. Source can be clean while the image you deploy is not. Scan the tag that is running.
- Overriding when the parent has the fix. An override is a pin you now own.
- Marking projects not affected without a record. "We checked" is not an answer a customer can file. Write the VEX statement.
- Letting an agent push during an incident. Keep pushes and merges with a person, and review AI generated changes before merge.
FAQ
Can I keep using Claude, ChatGPT or Codex for this?
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 which projects are affected from the chat you already use.
Does my source code leave my machine during the sweep?
No. The CLI scans on your machine and uploads only SBOMs and findings. safeguard findings sca --dir . goes further: it runs a scanner locally and prints the findings without uploading anything at all.
How do I know whether our code reaches the vulnerable function?
Safeguard's reachability analysis checks whether your code can reach the vulnerable part of the package. You can also ask the assistant to search your code for calls to the function the advisory names, then read the results yourself.
Should every affected project be fixed the same day?
No. Use the four buckets. Exposed, reachable and exploited goes first. Unreachable code goes into your next routine upgrade, with a record of why.
Can Safeguard open the fix pull requests for us?
Yes. safeguard fix --create-pr --draft opens a pull request from your machine, and autonomous remediation on the platform can open fix PRs. It is off by default.
Can I approve the fix when I am away from my desk?
Yes. The session shows on the phone, and commands wait for Allow once, Allow for turn or Deny. See Safeguard on Android phones and tablets for setup.
What do we send customers who ask if they are affected?
A short status per product and, for products that are not affected, a validated VEX document. Validate it with safeguard validate before you send it.
Try it yourself
Set up the runner on your own machine in this walkthrough: 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 for the next disclosure before it lands? Download the Safeguard CLI, run safeguard, type /login, then /scan . in each repository so the sweep is a question rather than a project. The full reference is in the CLI docs.
Quick check
Why confirm SBOM sweep hits with npm ls, Maven or Gradle?
A text match can catch packages with similar names. The build tool confirms the exact package and shows the path that pulls it in, which you need to choose the fix.
Which OpenVEX status records a project that contains the package but cannot reach the vulnerable code?
not_affected, with a justification such as vulnerable_code_not_in_execute_path, or an impact statement explaining why.
Sources
- Safeguard CLI overview
- Safeguard CLI interactive terminal
- Safeguard downloads
- CISA Known Exploited Vulnerabilities catalog
- FIRST Exploit Prediction Scoring System
- FIRST CVSS v4.0
- OSV documentation and API
- OpenVEX specification
- npm ls documentation
- npm package.json overrides
- Maven dependency:tree
- Gradle: viewing and debugging dependencies
- Go modules reference: go mod why
- pip show documentation
Last reviewed: 21 October 2026.