It depends on the tool, and the three sources can differ. A manifest holds ranges, a lockfile holds resolved versions, and the installed directory holds what actually landed. Find out which one your scanner reads, then make sure the other two stay in agreement with it.
Three records of the same thing
| Source | What it holds | Risk if the scanner reads only this |
|---|---|---|
| Manifest | Version ranges | Cannot know the exact version |
| Lockfile | Resolved versions | May be stale versus the build |
| Installed packages | What truly exists | Only present after install |
For npm, the documentation of npm ci describes the discipline that keeps these aligned. It requires an existing package-lock.json or npm-shrinkwrap.json, and if dependencies in the lock do not match package.json it exits with an error instead of updating the lock. It never writes to package.json or the lockfiles. That is why teams use it in CI: the install is frozen to the lockfile.
Where disagreement comes from
- Someone ran an install that changed the tree and forgot to commit the lockfile.
- Flags change the tree shape. The npm ci documentation notes that if you create the lockfile with flags that affect the dependency tree, such as legacy peer dependency handling, you must supply the same flags on install.
- The build uses a different command than the lockfile was made for.
- A base image or script adds packages outside the lockfile.
When these drift, a scanner reading the lockfile reports one world while production runs another.
Find out what yours reads
- Check the scanner documentation for which files it parses.
- Run a canary: put a vulnerable version in the lockfile only, scan, then put a different version in the installed directory only, scan again.
- Note which change the scanner reacts to.
- Document the answer in your coverage notes.
Keep the three in agreement
- Install in CI with the frozen command for your ecosystem, so the installed tree always equals the lockfile. See frozen installs in CI: the command for each language.
- Fail the build when the lockfile is out of date.
- Scan the built artifact or image as well, since that is the truth about what ships.
- Review lockfile diffs in pull requests.
Which should you prefer?
Scan the lockfile early for fast feedback, and scan the built output late for accuracy. Using both catches drift. A single point scan, whichever it is, leaves one blind side.
Safeguard offers SBOM and dependency scanning and a free scan at gold.safeguard.sh/scan. Confirm what any scanner reads with a canary.
Quick check
If my lockfile is committed, am I covered?
Only if the build installs exactly what it says. Frozen installs and a scan of the built output confirm that.
Why not scan only installed packages?
You can, but it requires an install and comes late. Lockfile scanning gives earlier feedback.
Sources
Last reviewed: 19 October 2026.