Scan the lockfile first, because it lists every package with exact versions and runs early in CI. Add a bundle or built output check for code the lockfile does not describe, such as vendored files, copied snippets and CDN scripts. Each view finds things the other misses.
What each scan sees
| Lockfile scan | Built bundle scan | |
|---|---|---|
| Source of truth | package-lock.json or equivalent | Output files that users download |
| Exact versions | Yes, recorded by the package manager | Often inferred from banners or code patterns |
| Dev only tools | Included unless omitted | Absent, if tree shaking and bundling removed them |
| Vendored or copy pasted libraries | Missed | Seen if recognizable |
| Scripts loaded from a CDN | Missed | Missed unless the page is crawled |
| Speed and timing | Early, before a build | After the build |
npm's lockfile records version, resolved URL and an integrity hash for each package, according to the npm docs, and it is meant to be committed so everyone installs the same tree.
Why the lockfile comes first
It is precise. A scanner reads names and versions and matches them against advisories, with no guessing. It also finds vulnerable packages that are in the dependency tree but not in the final bundle, which is useful for upgrade planning even if the exposure is lower. The npm audit command works this way: it sends your dependency descriptions to the registry and asks for known vulnerabilities, and by default it needs the lockfile.
Why the bundle still matters
Bundlers rename, concatenate and minify. A library someone dropped into a vendor folder years ago will never appear in the lockfile. A script copied from a blog post will not either. Scanning the output, or crawling a deployed page, can recognize old library signatures.
The reverse also helps. The bundle shows what actually ships, so you can see which of the vulnerable packages from the lockfile scan really reached users. See are devDependencies in your production bundle.
A layered approach
- Commit the lockfile and install with a lockfile enforcing command in CI.
- Run the dependency scan on every pull request.
- Add a periodic scan of a deployed page to catch vendored and CDN scripts.
- Keep an inventory of anything the first three do not see, see how to list every third-party script.
- Triage by reachability, not only by severity.
Reading results with care
A finding in a package that your code never calls is lower priority than one on a path your app uses. Reachability analysis helps separate those. Safeguard offers reachability analysis alongside SBOM and dependency scanning, and you can try a free scan at gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen. Watch how findings drop when only reachable code is counted.
Quick check
Is a lockfile scan enough on its own?
Not if you vendor libraries or load CDN scripts, because those never enter the lockfile.
Does npm audit scan CDN scripts?
No. It works from your dependency tree and lockfile, so scripts loaded by URL are outside it.
Sources
- npm Docs, package-lock.json: https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json
- npm Docs, npm audit: https://docs.npmjs.com/cli/v10/commands/npm-audit
Last reviewed: 19 October 2026.