Safeguard
Application Security•

OSV-Scanner Walkthrough: Scan a Lockfile and Read the Results

A hands-on run of osv-scanner against a real npm lockfile holding two known fast-jwt advisories, with the commands, the output, and the exit code explained.

Safeguard Security Team
5 min read

OSV-Scanner, Google's open source scanner, shipped v2.7.0 on 12 October 2026. It reads a lockfile, asks the OSV database which versions are known vulnerable, and prints a table. This walkthrough runs it on a real lockfile so you can see every step and reproduce it.

The test project depends on @fastify/jwt 10.2.0, with the lockfile resolved as of 29 July 2026. That pins fast-jwt at 6.3.0, which has two published advisories as of 18 October 2026. We ran version 2.5.1 locally, so your output may differ in small ways from newer releases.

What's affected

In the test lockfile, one package is affected by two advisories:

AdvisorySeverity in outputFixed in
GHSA-8wpc-h4q6-8fxv (published 8 October 2026)CVSS 7.46.3.1
GHSA-x937-hj6v-793pCVSS 4.26.3.4

The first is a CVE-2026-107720 signature bypass: createVerifier skips signature verification when the key is an empty string or null and an algorithms list is set. It is mapped to CWE-347, improper verification of a cryptographic signature.

How to check if you're exposed

Recreate the project and scan it:

mkdir demo && cd demo
echo '{"name":"demo","version":"1.0.0","dependencies":{"@fastify/jwt":"10.2.0"}}' > package.json
npm install --before=2026-07-29 --ignore-scripts
osv-scanner scan -L package-lock.json

The --before flag makes npm resolve as if it were that date, which gives a realistic stale lockfile. The scan prints a summary and a table:

Total 1 package affected by 2 known vulnerabilities (0 Critical, 1 High, 1 Medium, 0 Low, 0 Unknown) from 1 ecosystem.
2 vulnerabilities can be fixed.

| OSV URL                             | CVSS | ECOSYSTEM | PACKAGE  | VERSION | FIXED VERSION | SOURCE            |
| https://osv.dev/GHSA-8wpc-h4q6-8fxv | 7.4  | npm       | fast-jwt | 6.3.0   | 6.3.1         | package-lock.json |
| https://osv.dev/GHSA-x937-hj6v-793p | 4.2  | npm       | fast-jwt | 6.3.0   | 6.3.4         | package-lock.json |

Other forms from the official usage docs:

osv-scanner scan -r ./my-project-dir/
osv-scanner scan -L package-lock.json --format json

The first scans a directory recursively. The second emits JSON, which you can filter:

osv-scanner scan -L package-lock.json --format json \
  | jq -r '.results[0].packages[] | select(.vulnerabilities | length > 0) | .package.name, (.vulnerabilities[].id)'

The exit code is 1 when vulnerabilities are found and 0 when clean, which makes it usable as a CI gate. In our run it exited 1.

Where the data comes from

OSV-Scanner matches each package name and version in the lockfile against the OSV database, which aggregates advisories from sources such as GitHub's advisory database. That is why the table links to osv.dev pages and why the identifiers are GHSA IDs. The same record is available from the API:

curl -s https://api.osv.dev/v1/vulns/GHSA-8wpc-h4q6-8fxv | jq '{id, aliases, published}'

It shows the alias CVE-2026-107720 and a published date of 8 October 2026. If your policy is written in CVE terms, use the aliases field to translate.

Two other details from the release notes for v2.7.0 are worth knowing. JSON output now includes package URLs where available, and CycloneDX output includes vulnerability severity and rating. The same release made the --offline mode with downloaded databases stop running plugins that need the network. If you scan in an air gapped build, check the release notes for your version.

In the JSON output each affected package has a groups field. The scanner groups advisories that are aliases of one another, so one underlying bug published as both a GHSA and a CVE counts once. In our run each of the two fast-jwt advisories was its own group.

What to do now

  1. Run the scan on every lockfile you ship, not just the root one. Use -r for monorepos.
  2. Read the FIXED VERSION column first. Both advisories here have a fix, and 6.3.4 covers both.
  3. Check whether your range already allows the fix. Here @fastify/jwt asks for ^6.2.4, so npm update fast-jwt is enough, with no overrides needed.
  4. Re-run the scan after the update and confirm exit code 0.
  5. Add the command to CI so a newly published advisory fails the build the next day rather than the next quarter.

Why it matters

A lockfile is a snapshot. The code in it stays the same while the advisory database keeps growing, so a lockfile that passed in July fails in October without a single commit. Scanning on a schedule matters as much as scanning on a pull request.

The scan also tells you what is present, not what is reachable. A vulnerable function that your code never calls is a lower priority than one on a hot path, which is the question reachability analysis answers.

Try it yourself

Run the four setup commands above, then change the date in --before to 2026-10-16 and compare the scan results. The second run should come back clean.

Click through it at your own pace, or open it full screen.

You can also drop a package.json into the free scanner at gold.safeguard.sh/scan. It runs in your browser and the file never leaves your machine.

Quick check

Why does a lockfile that was clean in July fail a scan in October?

The lockfile did not change but the vulnerability database did. New advisories were published against the versions it pins.

What does exit code 1 from osv-scanner mean?

It found at least one known vulnerability, so a CI job running it will fail.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.