Safeguard
AI Security

Container Security Vulnerabilities: How to Find and Fix Them

The container security vulnerabilities that actually get exploited, where they hide across the image lifecycle, and a practical order for fixing them without rebuilding everything at once.

Marcus Chen
DevSecOps Engineer
6 min read

Container security vulnerabilities cluster into four places — the base image, your application dependencies, the image build and configuration, and the runtime environment — and knowing which layer a finding lives in tells you how urgent it is and who fixes it. A CVE in a base OS package that is never invoked is a different problem from a world-writable secret baked into an image layer, even if a scanner reports both as "high." Treating every finding the same way is how teams end up with a backlog of ten thousand issues and no idea where to start.

This post walks the layers, explains what typically goes wrong in each, and gives you a prioritization order that keeps the fixes tractable.

The base image is the biggest single source

Most vulnerabilities a scanner reports on a container come from the base image, not your code. When you start a Dockerfile with FROM ubuntu:22.04 or a heavy language image, you inherit every OS package that image ships — and many of them carry known CVEs the moment you pull them. A full node:latest image can report hundreds of findings before you have added a single line of your own.

The fix is mostly architectural. Use minimal base images — distroless, Alpine, or a slim variant — so you inherit far fewer packages and therefore far fewer CVEs. A distroless image that contains only your runtime and its direct libraries has almost no OS surface for a scanner to flag or an attacker to exploit. Pin the base image to a digest rather than a floating tag so a rebuild does not silently pull a different, possibly worse, image. And rebuild regularly, because a base image that was clean six months ago has accumulated new CVEs since.

Application dependencies travel into the image

The second layer is the packages your application pulls in — the npm modules, Python wheels, Go modules, or Java JARs your code depends on, plus everything those depend on transitively. A vulnerability three levels deep in your dependency tree ships into the container just as surely as one you imported directly, and it is far easier to miss.

This is where software composition analysis earns its place in the build. Scanning the manifest and lockfile identifies known-vulnerable versions across the full transitive tree, and a tool such as Safeguard's SCA can flag a vulnerable transitive package that no manual review would catch. The important habit is scanning at build time and failing the build on newly introduced high-severity issues, so a bad dependency never reaches a registry. Reserve the full historical backlog for a separate remediation track.

Build and configuration mistakes

The third layer is not a CVE at all — it is how the image is built and configured. These findings are often more directly exploitable than a package CVE because they need no vulnerable code path to abuse.

Running the container as root is the most common. If nothing forces a non-root user, a process that escapes the app can act as root inside the container and has a much easier path to escaping to the host. Add a non-root user and drop to it:

FROM node:22-slim
RUN useradd --create-home --uid 10001 appuser
WORKDIR /home/appuser/app
COPY --chown=appuser:appuser . .
RUN npm ci --omit=dev
USER appuser
CMD ["node", "server.js"]

Secrets baked into layers are the next. A password or API key added in an early RUN or COPY stays in the image history even if a later layer deletes it, because each layer is stored separately. Use build secrets or runtime injection instead. Other frequent issues include copying the entire build context into the image (which leaks .git directories and local config), and leaving package managers and shells in the final image when a multi-stage build could drop them.

Runtime is where exploitation actually happens

The fourth layer is the environment the container runs in, and it is where a vulnerability turns into a compromise. Overly broad capabilities, a privileged container, a writable root filesystem, or a host path mounted into the container all widen what an attacker can do after gaining a foothold. Kubernetes adds RBAC scope and network policy to the picture.

The defenses are configuration, not patching. Run with a read-only root filesystem where possible, drop all Linux capabilities and add back only what the workload needs, refuse privileged containers, and apply network policies so a compromised container cannot freely reach the control plane or other workloads. A base image with zero CVEs running as root with a mounted Docker socket is far more dangerous than a base image with a dozen unreachable CVEs running locked down.

A prioritization order that stays tractable

Given findings across all four layers, fix in this order. First, configuration and runtime issues that enable exploitation — root execution, exposed secrets, privileged containers — because these need no vulnerable code to abuse. Second, vulnerabilities in packages that are actually reachable and network-exposed. Third, dependency CVEs with available patches, batched into regular update passes. Last, base OS CVEs in packages your application never invokes, which you clear largely by switching to a minimal base and rebuilding on a cadence.

The mistake is starting with the ten thousand base-image CVEs because the scanner listed them first. Reachability and exploitability, not raw count, decide what matters. If you want a structured path through this, the Safeguard Academy covers container hardening and image scanning as a sequence rather than a pile of findings.

FAQ

What causes most container security vulnerabilities?

The base image is the single largest source, because you inherit every OS package it ships along with their known CVEs. Switching to a minimal base like distroless or a slim variant, and rebuilding regularly, eliminates the majority of reported findings before you address your own code.

Are container misconfigurations worse than package CVEs?

Often, yes. A misconfiguration like running as root, baking in secrets, or a privileged container is directly exploitable and needs no vulnerable code path. A package CVE in code that is never invoked may not be exploitable at all. Prioritize configuration and runtime issues first.

How do I find vulnerabilities in transitive dependencies inside a container?

Scan the application manifest and lockfile with software composition analysis at build time, which resolves the full transitive dependency tree and flags known-vulnerable versions. Fail the build on newly introduced high-severity issues so a bad dependency never reaches your registry.

Does a container with zero CVEs mean it is secure?

No. A scanner reporting zero package CVEs says nothing about how the container is configured or run. An image running as root with excessive capabilities or a mounted host socket can be trivially exploited despite a clean vulnerability scan. Configuration and runtime hardening are separate concerns.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.