Scanverra
Back to Articles
Code Quality

Container Security: A Practical Guide to Scanning Docker Images

·8 min read

A container built from a base image pulled six months ago ships every CVE patched in that time - not because anyone wrote vulnerable code, but because nobody rebuilt the image since.

Where the Risk Actually Lives

  • The base image itself. FROM node:18 without a specific patch version pulls whatever that tag currently resolves to - which drifts over time, and an unpinned tag can silently start shipping a different (and possibly vulnerable) underlying OS version.
  • OS packages baked into the layer.A base image's own package manager entries (glibc, openssl, and similar) carry their own CVEs independent of anything your application code does.
  • Application dependencies copied into the image- the same dependency-CVE problem any codebase has, just now frozen into an image layer that won't update on its own.
  • Secrets baked into layers. An API key set via ENVor copied in during a build step remains in that layer's history even if a later layer removes the file - anyone with access to the image can extract it.

What Image Scanning Actually Checks

A container scanner inspects the image's layers for known-vulnerable package versions - both OS packages and language-specific dependencies - cross-referenced against CVE databases, the same way dependency scanning works for a codebase, just applied to the built artifact instead of the source.

Practical Fixes

1. Pin base image versions explicitly

Use a specific, verifiable tag (node:18.20.4-slim) rather than a moving target like node:18 or latest, so what you tested is what actually ships.

2. Rebuild on a schedule, not just on code changes

A container that hasn't changed in months still accumulates newly-disclosed CVEs in its frozen base layer - rebuilding periodically (even with no application code changes) picks up upstream security patches.

3. Use minimal base images

A slim or alpine variant ships far fewer packages than a full OS image, which directly shrinks the CVE surface simply by having less installed to be vulnerable in the first place.

4. Never bake secrets into a layer

Use build-time secret mounts (Docker's --secret flag) or inject secrets at runtime instead of ENV/COPYduring the build - both leave the value recoverable from the image's layer history otherwise.

Automate this in your next PR

Run a free repo scan and see outdated dependencies, CVEs, and code quality issues before they ship.

Run a free repo scan