Remediation

Fixing dependencies

Upgrade the packages a CVE names — direct and transitive — with the package manager you already use, verified by re-detecting the vulnerability afterwards.

Fixing dependencies

A dependency CVE has a known answer: move to the fixed version. The work is figuring out how — whether the package is one you declared or something four levels down, which of your project's package managers owns it, and whether the upgrade will hold.

xgrep fix does that part.

xgrep scan .               # find the vulnerable packages
xgrep fix verify           # see the upgrade plan — offline, writes nothing
xgrep fix apply --online   # do it

The fixed version comes from the advisory; xgrep never invents one. Which package manager to use, and whether the package is direct or transitive, come from an offline bill of materials of your project.

Look before you leap

fix verify is offline and never writes. For each vulnerable package it shows the commands it would run, any manifest edit they need first, and how risky the change is. That is usually the thing to read first — it tells you whether this is a one-line version bump or something that will move your lockfile around.

fix apply needs --online, because actually upgrading means running your package manager, which needs the network and the toolchain. Without it, dependency findings come back marked as needing it rather than being silently skipped.

Nothing is left half-done

Before running anything, xgrep snapshots every file the plan could touch. If a command fails, or the vulnerability is still there afterwards, everything is rolled back to where it started.

Success is decided by re-detecting the vulnerability, not by the package manager's exit code — a command that succeeds while leaving the vulnerable version in the tree is not a fix.

Direct, transitive, and the awkward ones

A dependency you declared is a targeted upgrade — go get pkg@1.2.3, npm install pkg@1.2.3, bundle update pkg, cargo update -p pkg — inside the constraints your manifest already states. Low risk.

A transitive dependency is upgraded through the resolver, where the manager has one that can reach it: Go, Cargo, Bundler, Composer, Mix, SwiftPM, pub and the Python lockers can all be pointed at a package they did not directly declare, with no manifest edit.

Where the resolver won't cooperate, the fix is an override written into your manifest — npm and bun overrides, pnpm.overrides, yarn resolutions, Maven dependencyManagement, Gradle constraints, a NuGet direct pin. That forces a version the resolver would not have picked, which can break a peer or parent constraint, so it is reported as the higher-risk change it is, with a line on what it affects. Decide it with your eyes open.

xgrep edits manifests, never lockfiles: a lockfile is regenerated by the manager that owns it.

Package managers supported: Go, npm, pnpm, yarn (classic and berry), bun, pipenv, poetry, uv, pdm, Cargo, Bundler, Composer, Maven, Gradle, NuGet, pub, Mix and SwiftPM.

When the advisory is vague

By default only confident upgrades are applied — the advisory named the fixed version. --confirm-deps opts in to the rest: a version parsed out of advisory prose, or an update-to-latest when no fixed version is stated anywhere. That is a deliberate second signature, not a default, because those upgrades are a guess at a target rather than a stated one.

Fix what matters first

Upgrading every flagged package is how dependency work becomes a chore nobody does. Pair this with reachability and start with the vulnerabilities your code can actually reach:

xgrep scan --reachability .   # rank by whether your code reaches the vulnerable code
xgrep fix verify              # then plan the upgrades that matter

Scope a run further with --rule, --max-severity and --min-confidence.

On this page