skip to content

Why does scanning container images not replace SCA on manifests and lockfiles?

level: juniorimportance: must knowfreq 70%

answer

  1. Different inputs, different inventories
  2. One reads layers, one reads resolution
  3. OS packages versus application libraries
  4. Distroless: few OS findings, same jars
  5. Only manifests record dependency scope

basics

~20 s

They build different inventories. An image scanner lists what it can find inside a built image, mostly OS packages installed by the distro. Manifest and lockfile SCA reads the dependency graph your build resolved. Each misses what the other finds.

solid answer

~50 s

An image scanner such as Trivy or Grype opens a built image layer by layer and inventories what it can see there: OS packages recorded in the distribution's package database, plus language libraries it recognises on disk. Manifest and lockfile SCA — for example OSV-Scanner pointed at a `package-lock.json`, a `go.sum` or a resolved Maven tree — reads the dependency graph the build actually resolved, so it names direct and transitive libraries with exact versions and knows which ones are dev-only. The overlap is partial in both directions. A Debian- or Alpine-based image typically reports OS-package findings no manifest scanner will ever see, and a minimal or distroless image carrying a fat jar can report almost nothing at the OS layer while shipping libraries the manifest scan lists precisely. "We scan images" and "we do SCA" are two different coverage claims.

go deeper

for a junior

Be ready to name the two inputs plainly: a lockfile or manifest on one side, a built image or filesystem on the other. Saying "we scan images so we do SCA" is the answer that gets marked down.

for a middle

Explain what each class can and cannot enumerate — OS package databases versus a resolved dependency graph — and why dependency scope only exists on the manifest side.

for a senior

Show you treat an empty result as information about the scanner's input, not about the artifact, and that you can say which inventory a given finding came from and therefore what the fix is.

for a principal

Own the coverage claim itself: what your organisation can honestly assert it has inventoried, which artefact shapes fall through the gaps, and how that claim is worded when a customer or auditor asks.

## Two inventories, not one Every vulnerability finding starts with an **inventory**: a list of components with versions, which is then matched against advisory data. The tool classes in this area differ far less in their matching step than in *what they were able to inventory in the first place*. That is the whole answer to this question, and it is where weak candidates go wrong — they treat "the scanner found nothing" as a statement about the artifact when it is really a statement about the scanner's input. ## What manifest and lockfile SCA reads This class reads your project's dependency declarations and, ideally, the **resolved** form of them: `package-lock.json`, `yarn.lock`, `poetry.lock`, `go.sum`, `Cargo.lock`, a resolved Maven or Gradle dependency tree. OSV-Scanner is a straightforward example — point it at a lockfile or a directory of them and it reports the packages it resolved, matched against the OSV database. Because it reads the resolution rather than the range, it gets exact versions for **transitive** dependencies, not just the ones you declared. It also knows **scope**: a lockfile records that a package is a development or test dependency, which is how you separate "vulnerable thing that ships" from "vulnerable thing that only ever ran on a build machine". What this class cannot see: anything not expressed in a manifest. The base image's OS packages. A library dropped into the image by a `curl` in the build. A binary you copied in. The manifest describes what your build *intended to resolve*, not what ended up in the artifact. ## What an image scan reads An image scanner works from the built artifact — the layers of an OCI image, or a filesystem. Its strongest signal is the **OS package database** that a distribution maintains inside the image (the dpkg, rpm or apk metadata), which gives it an exact list of installed system packages and versions. On top of that it looks for language-ecosystem artefacts it recognises on disk: jars, site-packages, `node_modules`, and so on. What this class sees that the manifest scan does not: the entire OS layer. A stock distro base can easily carry dozens of packages with open advisories that appear in no manifest anywhere in your repository, because you never declared them — the base image did. What it can miss: dependency **scope** (it has no idea a jar was a test dependency), packages shaded or relocated into a fat artefact, and anything whose on-disk form the analyser does not recognise. ## The two cases that make the point in an interview **A full distro base with a small app.** The image scan reports a long tail of OS-package findings; the manifest scan reports a handful of application libraries. Neither list is a superset of the other. If you only ran the manifest scan, you would report a service as clean while it ships a vulnerable system library. **A distroless or minimal base with a fat jar.** The image scan has almost no OS package database to read and comes back nearly empty. That result is often misread as "the artefact is clean". It is not — it means the base carried few OS packages. The application's own libraries, which are where most of the risk lives in that shape, are exactly what the manifest or SBOM-based inventory enumerates. ## Where the fourth class sits Update bots such as Renovate are a different animal again: they propose version changes based on what is declared in a manifest and what newer versions exist. They are a remediation channel, not an inventory or a detector, and they cover only ecosystems where they can read and rewrite a manifest. ## How to answer Say it as coverage, not as tool preference: manifest and lockfile SCA gives you the resolved application dependency graph with scope; image and filesystem scanning gives you the OS package surface and whatever else physically shipped. A real programme runs both, and the useful question is which inventory a given finding came from — because that tells you whether the fix is a dependency bump, a base-image rebuild, or neither.

  • Which class tells you a finding is a dev-only dependency, and why does that matter?
    Only manifest and lockfile SCA, because the resolved graph records scope. It matters because a vulnerable dev or test dependency usually never enters the shipped artifact, so it is a different risk from a runtime library. Dropping that distinction is one of the cheapest ways to cut triage noise without hiding anything real.
  • You run only an image scan on a distroless Java service and it comes back empty. What do you say?
    That the base carries almost no OS packages, so the scan had almost nothing to inventory. It says nothing about the jars inside the application archive. I would add a manifest or SBOM-based inventory of the resolved Java dependencies before calling the service assessed.
  • Where do the OS-package findings actually come from?
    From the distribution's own package metadata inside the image — the dpkg, rpm or apk database — matched against that distribution's advisory feed. They exist because the base image installed them; nothing in your repository declared them, which is why no manifest scan will ever report them.

One tool reads the shipping manifest the shipper filled in; the other opens the container and counts what is inside. Both are useful, and neither is a substitute for the other.

saying these in an interview costs you the question

  • Says an image scan covers everything the artifact depends on
  • Reads an empty image-scan result as a clean artifact
  • Cannot say where OS-package findings come from
  • Thinks a distroless base means fewer library vulnerabilities
  • Assumes dev-only dependencies show up in an image scan

context