A scanner gets its component list from one or more of five sources: lockfiles, manifests, installed package databases, container image layers, and compiled binaries. Lockfiles give exact versions, manifests give declared ranges, and the others show what is really on disk. Each one misses something, so coverage depends on which sources the scanner reads.
What each source tells you
| Source | What it records | Strength | Blind spot |
|---|---|---|---|
| Lockfile | Exact resolved versions, often including transitive packages | Precise and reproducible | Only exists if committed; can drift from what ships |
| Manifest | Declared dependencies and version ranges | Always present | Ranges do not say which version was installed |
| Installed package database | What the operating system or runtime installed | Reflects a real machine or image | Misses files copied in without a package manager |
| Image layers | Files added by each build step | Shows the final container contents | Bundled or vendored code may carry no metadata |
| Binary inspection | Strings and embedded metadata in compiled files | Finds statically linked code | Version detection can be uncertain |
Lockfile versus manifest
This distinction trips up many teams. A manifest might say a package must be version 2 or higher. The lockfile records that the build resolved 2.4.1. Matching against the range alone could flag versions you never installed, or miss the one you did. Scanners that read lockfiles give sharper results.
Why package identifiers matter
Once the scanner has a list, each item needs a stable name. The Package-URL (purl) specification gives a standard URL-style syntax that identifies a package independent of ecosystem or distribution channel. Using one format means the same package is recognised whether it came from an SBOM, a lockfile or an image scan. For details, see what is a package URL.
Combining sources
No single source is complete, so stronger scanners combine them. A common pattern is:
- Read the lockfile in the repository for exact application dependencies.
- Read the package database in the built image for operating system packages.
- Compare both against what the image layers actually contain.
- Flag files that exist on disk but appear in no inventory.
Step four is the useful one. Unlisted files are where bundled libraries and copied binaries hide.
Questions to ask about your own scanner
- Does it read lockfiles, or only manifests?
- Does it scan the built artifact, or only the source?
- What happens with vendored directories and copied jars?
- Does it report what it could not read?
The last question matters most. A scan that silently skips a directory looks identical to a scan that found nothing there.
Try it yourself
Click through it at your own pace, or open it full screen.
Look at how direct and transitive components appear in the inventory, and which ecosystems each one came from.
Where Safeguard fits
Safeguard produces an SBOM and dependency inventory as the first step of its scanning, and its asset discovery helps find repositories and projects nobody listed. You can run a free scan to see an inventory for your own code.
Quick check
Is a manifest enough to scan accurately?
Usually not. A manifest holds ranges, not the exact versions that were installed, so results can be wrong in both directions. A lockfile or a built artifact is more precise.
Why scan the built image if the repository is already scanned?
The image can contain operating system packages and copied files that never appear in the repository. Scanning both gives a fuller list.
Sources
Last reviewed: 19 October 2026.