Most vulnerable packages in a Node project are ones you never asked for. In October 2026, fast-jwt, a JWT library pulled in by @fastify/jwt, had two published advisories: GHSA-8wpc-h4q6-8fxv on 8 October, fixed in 6.3.1, and GHSA-x937-hj6v-793p, fixed in 6.3.4. A project whose lockfile still pins fast-jwt 6.3.0 will fail npm audit, and the fix is usually one command.
This post reproduces that exact situation and walks the standard sequence: explain, update, verify, and only then reach for overrides.
What's affected
fast-jwt 6.3.0, published 28 July 2026, and anything older than the fixes. The first advisory covers CVE-2026-107720: createVerifier accepts unsigned tokens when the key is an empty string or null and an algorithms list is set.
@fastify/jwt 10.2.0 declares fast-jwt as ^6.2.4. That range allows 6.3.5, the latest release, published 15 October 2026. So the vulnerable version in your lockfile is a stale resolution, not a constraint.
How to check if you're exposed
Build the stale project:
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
npm audit
The audit reports (URLs trimmed):
fast-jwt <=6.3.3
Severity: high
fast-jwt: Verifier cache accepts expired JWTs without iat.
fast-jwt: createVerifier accepts unsigned JWTs when key is '' or null and algorithms is explicitly set
fix available via `npm audit fix`
Now trace why it is there:
npm explain fast-jwt
fast-jwt@6.3.0
node_modules/fast-jwt
fast-jwt@"^6.2.4" from @fastify/jwt@10.2.0
node_modules/@fastify/jwt
@fastify/jwt@"10.2.0" from the root project
Read it bottom up: your project asks for @fastify/jwt 10.2.0, which asks for fast-jwt in the 6.2.4 to 6.x range. npm ls fast-jwt shows the same chain as a tree.
Reading the audit in JSON
For scripts and dashboards, npm audit --json carries the same facts in fields you can filter:
npm audit --json | jq '.vulnerabilities["fast-jwt"] | {severity, isDirect, range, fixAvailable}'
On the stale project this returned severity: "high", isDirect: false, range: "<=6.3.3" and fixAvailable: true. The isDirect: false field is the cue to run npm explain: the package is not in your package.json, so you need to find the parent before you can decide between update, audit fix and override.
The range <=6.3.3 also tells you something the text report hides. The audit treats every version up to 6.3.3 as affected, because the second advisory was fixed in 6.3.4. A project that updated to 6.3.1 to fix only the signature bug would still be flagged, and rightly so.
npm audit fix is the one command version of the same fix. It changes the lockfile within the declared ranges, which is what npm update fast-jwt does for a single package. Prefer the targeted update when you want a lockfile diff that touches one package and is easy to review.
What to do now
- Update only the transitive package within its allowed range:
npm update fast-jwt
- Verify:
npm ls fast-jwtnow showsfast-jwt@6.3.5, andnpm auditprintsfound 0 vulnerabilities. In the lockfile, thenode_modules/fast-jwtentry moved to 6.3.5. - Commit the lockfile change alone, so the diff is reviewable.
- If the parent's range had excluded the fix, add an override in package.json instead:
{
"overrides": {
"fast-jwt": "^6.3.5"
}
}
We tested this on the stale project and npm install resolved 6.3.5 as well.
5. Remove the override when the parent releases a fixed range. @fastify/jwt 10.2.5, published 17 October 2026, now asks for ^6.3.4.
6. Re-run the explain command after any change so you can see the new chain.
How this could have been avoided
- Scheduled lockfile refreshes. The fix existed within the declared range. A weekly
npm updatepull request would have picked it up. - A scan in CI.
npm auditor an OSV scan on every build flags the stale pin when the advisory appears, not at the next release. - Tight overrides discipline. Overrides hide debt. Track each one with a removal condition.
- Automated alerts. The first advisory was GitHub-reviewed, which is the kind that triggers Dependabot alerts, so an enabled alert would have flagged the stale pin on 8 October.
Why it matters
Updating a lockfile is routine maintenance, and a stale one turns it into an audit finding. The commands here take under a minute.
The same reasoning applies to any ecosystem. The question is always whether the parent's range permits the fix, and the answer is in the tree.
Try it yourself
Run npm explain on the most frequently flagged package in your own project and write down which direct dependency pulls it in and what range it requests.
Click through it at your own pace, or open it full screen.
Quick check
When is an override better than npm update?
When the parent package's declared range excludes the fixed version, so update cannot move the transitive dependency.
Why did npm update fix fast-jwt without touching package.json?
The range ^6.2.4 already allowed 6.3.5, so only the lockfile resolution needed to change.