The container security stack is broken because it treats each layer independently while vulnerabilities flow freely between them. When you deploy a container, code travels through four distinct security boundaries — and most organizations defend only one or two of them.
graph TB Source["Layer 1: Source Code<br/>Git Repos<br/>Dependencies<br/>npm: 3.2M packages"] Build["Layer 2: Build<br/>CI/CD Pipeline<br/>Compilation<br/>Secret Leaks Risk"] Registry["Layer 3: Registry<br/>Container Image Storage<br/>Scanning<br/>8.3M+ CVEs"] Runtime["Layer 4: Runtime<br/>Kubernetes<br/>Execution Context<br/>Privilege Escalation"] Source --> Build Build --> Registry Registry --> Runtime Gap1["Misses:<br/>Transitive deps"] Gap2["Misses:<br/>Runtime behavior"] Gap3["Misses:<br/>Source vulns"] Gap4["Misses:<br/>Image history"] Build -.-> Gap1 Registry -.-> Gap2 Runtime -.-> Gap3 style Gap1 fill:#ffcccc style Gap2 fill:#ffcccc style Gap3 fill:#ffcccc style Gap4 fill:#ffccccThe Four-Layer Container Security Stack
The container security stack consists of four interconnected layers. Layer 1: Source Code includes Git repositories and application code plus dependencies with a vulnerability surface of 3.2M packages on npm alone. The source layer misses transitive dependencies and sub-dependencies.
Layer 2: Build encompasses the CI/CD pipeline, compilation, linking, and packaging, with a vulnerability surface including leaked secrets, unverified base images, and non-hermetic builds. The build layer misses vulnerabilities from the source layer and runtime behavior.
Layer 3: Registry handles container image storage, scanning, signature verification, and access control, with a vulnerability surface of 8.3M+ publicly disclosed CVEs. The registry layer misses source vulnerabilities and runtime context.
Layer 4: Runtime covers the container orchestration cluster, workload execution, network access, and filesystem, with a vulnerability surface including kernel exploits, privilege escalation, and lateral movement.
These layers operate under different teams, with different tooling, different timelines, and different assumptions about what happens upstream and downstream. A vulnerability discovered at Layer 1 may not be visible at Layer 3. A threat that matters at Layer 4 is invisible to a Layer 3 scanner. This fragmentation creates a false sense of security: you can point to scanning at Layer 3 and declare victory while Layer 1 contains exploitable code and Layer 4 runs with privileges it doesn't need.
Layer 1: Source Code — The Forgotten Security Boundary
Source code is where most of a container's attack surface lives, yet it's the layer enterprises scan the least.
Your application code is not the problem. The problem is that 95% of the code in your container ships with dependencies — libraries you didn't write, maintained by people you've never met, operating under funding models that often don't sustain the work.
The npm ecosystem contains 3.2 million packages. The Python Package Index contains 600,000. Maven Central hosts 1.8 million Java artifacts. You depend on maybe 200 of them directly. Each of those 200 depends on another 200. Transitive dependency chains go 10+ layers deep. At each step, a vulnerable library can exist undetected.
A vulnerability at Layer 1 means the vulnerable code is baked into your source repository, every built image contains it, every deployed container runs it, and you can't fix it without waiting for the maintainer or forking the code.
Source-level vulnerability scanning (Software Composition Analysis) exists, but it arrives late in development and operates with incomplete data. A developer discovers a new dependency has a CVE, they update the version number, and the scanner flags it. But the vulnerable code was already committed. The pull request was already merged. If the vulnerability affects code paths that run in production, it's potentially already running in staging or production clusters.
The invisibility extends deeper: most SCA tools don't detect vulnerabilities in transitive dependencies until after you've already pulled them into your build. You run npm install or pip install. Hundreds of sub-dependencies download from the internet. Only then does the scanner examine what you actually got. By that point, the code is in your repository and heading toward production.
Layer 2: Build — Where Secrets Leak and Provenance Breaks
The build layer transforms source code into a runnable artifact. This is where non-hermetic builds, leaked secrets, and unverified base images create attack vectors that scanning can't detect.
A non-hermetic build means the binary output depends on the state of the build machine, not just the source code. The same source checked out on two different machines produces different binaries. This makes verification impossible. If you rebuild your application next month, does the binary hash match? No. This breaks reproducibility and opens the door to subtle compromises that scanners won't catch because they're looking at the binary, not the path that created it.
Secrets leak at build time in multiple ways: credentials committed to source then deleted in a later commit but still present in history, environment variables exposed in Docker layer cache, build logs that contain secrets stored in CI/CD platforms with overly permissive access, and private keys used for signing stored unencrypted in build systems.
Base images represent another Layer 2 attack surface. You start with FROM ubuntu:latest or FROM python:3.11. You have no way to verify this image came from Canonical or the Python maintainers. Container registries have no mandatory signature verification. You can pull a base image, and if someone has compromised the registry or the network, you get compromised code. The xz/liblzma backdoor (CVE-2024-3094) entered systems via a compromised upstream release — but in a more distributed scenario, a base image could be compromised and deployed to thousands of clusters.
Non-hermetic builds also mean you can't patch libraries independently of the full rebuild. If you want to apply a security patch to OpenSSL without rebuilding your entire application, standard Docker builds don't support this. You have to rebuild the application layer, the dependency layer, everything. If that rebuild takes 6 hours and requires coordination across teams, patches get delayed.
The build layer is also where you typically apply the smallest security hardening. Most Dockerfiles run as root, install packages with no signature verification, and assume the build machine is trustworthy. A compromised build machine or a poisoned package repository can inject malicious code that ends up in every image you build.
Layer 3: Registry — The Visibility Trap
Most container security focuses here. Scanning images in a registry feels like control: you get a report, you see CVE counts, you enforce policies based on severity.
This is also where the illusion breaks apart.
Image scanning at the registry layer has fundamental blind spots:
Scanners disagree dramatically. You scan the same image with Trivy, Grype, and Snyk. Trivy reports 347 vulnerabilities. Grype reports 412. Snyk reports 289. The differences aren't minor; they reflect different vulnerability databases, different severity scoring, different detection heuristics. No enterprise can make policy decisions when tools disagree this wildly.
Scanners can't see transitive code reachability. An image contains 5,000 packages. A CVE exists in one of them. But does your application ever call the vulnerable function? The scanner can't tell. This is why vulnerability explosion at the registry layer is inevitable — every CVE in every package gets flagged, even if it's unreachable. VEX (Vulnerability Exploitability eXchange) attempts to address this with attestations, but requires manual work and doesn't eliminate the underlying vulnerability.
Scanners can't see runtime behavior. The registry sees a static image. It sees packages, versions, and known CVEs. It doesn't see what system calls your container makes, what network connections it opens, what files it reads, what operations would actually trigger a vulnerability, or whether the application is running as root (making kernel exploits catastrophic).
Stale images in registries are invisible. You have 1,000 tags of 200 different images in your registry. Most are old builds from months ago. New CVEs are published weekly. Your registry has no way to enforce policies on images not being deployed. They just sit there, getting more vulnerable as time passes.
Registry-layer scanning arrives too late. By the time you're scanning at Layer 3, code has been written, tested, merged, built, and pushed. If a critical vulnerability is discovered, you have three options: (1) block the image, (2) rebuild immediately, or (3) accept the risk. Blocking breaks deployments. Rebuilding takes hours. Accepting risk is the path most teams take.
Signature verification is not enforced. Images can claim they're signed, but registries don't mandate verification. You could pull a tampered image and never know. Container Content Trust exists but is rarely enforced, especially across heterogeneous environments with multiple registries.
The registry layer is also where access control often breaks down. Too many people have push permissions. Images are stored in registries with overly permissive policies. A compromised developer account or a leaked push token means malicious images can be published to production registries.
Layer 4: Runtime — The Exploitation Layer
Once a container is running, the game changes. A vulnerability that was theoretical at Layer 3 becomes operational at Layer 4.
Most containers run with unnecessary privileges. Root access means a kernel exploit becomes a cluster compromise. Writable filesystems mean an attacker can modify the application code while it runs, persist changes across restarts, or break container integrity. Shells (bash, sh) are included in images "just in case" — and in case of compromise, they enable interactive post-exploitation.
Runtime security requires understanding container execution: containers share the same kernel, so a kernel vulnerability affects every container on the host. System calls are the boundary between container and kernel, and unrestricted system calls like mount(), reboot(), and sysctl() allow privilege escalation and resource manipulation. Network namespaces are shared within a cluster, meaning east-west traffic between containers is unencrypted unless you deploy service mesh or network policies, allowing a compromised container to spy on all traffic to adjacent containers. Shared storage mounts mean a writable volume in one container can be written to by another, leaving container isolation incomplete.
A vulnerability discovered at Layer 4 means the code is running in production, exploitation is happening in real-time, lateral movement to other containers or the host is possible, and detection depends on behavioral monitoring rather than static analysis.
Most organizations have no behavioral monitoring at Layer 4. No one is watching for unusual system calls, suspicious file access, or unexpected network connections until an incident occurs. By then, the attacker has already moved laterally.
Why Fixing One Layer Doesn't Fix the Problem
Vulnerability remediation is often attempted at a single layer, which doesn't work because vulnerabilities don't live at a single layer.
If you focus only on Layer 3 (registry scanning), you solve the visibility problem at that moment, but you don't solve Layer 1 where vulnerable dependencies are still in your source code, you don't address Layer 2 where the build process is still non-hermetic with leaking secrets and unverified base images, and you can't prevent Layer 4 exploitation since once the image runs, only runtime controls matter. If you focus only on Layer 4 (runtime monitoring), you catch compromises after they happen, but the vulnerable code is still present, attackers have multiple entry vectors, and reactive detection is slower than proactive prevention.
If you focus only on Layer 1 (dependency scanning), you find vulnerabilities early but you still have to wait for maintainers to patch, you have no way to guarantee those patches make it through Layer 2 and Layer 3, and Layer 4 execution context still matters.
The false sense of security comes from conflating "we scan our images" with "our images are secure." Scanning is only the beginning. Scanning is visibility. Security requires remediation at every layer.
The Invisibility Problem: What Tools Can't See
Standard security tools operate with incomplete information:
Tool Type Visibility Blind Spots─────────────────────────────────────────────────────────────SAST Source code Can't see runtime behaviorSCA Dependencies Can't see if code is calledDAST Running app Can't see all code pathsImage scanning Packages Can't see transitive behaviorRuntime monitoring System calls Can't see application logicThis matrix shows why no single tool secures a container. Each tool answers one question. Security requires all the answers.
A SAST scanner finds a hardcoded credential in source code. Great. But what if the credential never makes it into the built image because it gets stripped during the build? Or what if it does make it through, but it's a dummy credential only used in tests? SAST can't answer these questions.
A runtime monitor detects a container making 100,000 system calls per second in a loop. Suspicious. But is it a DOS attack, or is the application behavior normal? The monitor can't tell without understanding the application logic.
The Attack Surface Multiplier
Each layer contributes to the total attack surface. Vulnerabilities at each layer can be chained together for maximum impact.
A Layer 1 vulnerability (vulnerable library in source) becomes exploitable at Layer 2 if the build process doesn't validate the package hash. It becomes visible at Layer 3 if scanning tools catch the vulnerable version. It becomes catastrophic at Layer 4 if the container runs as root and the vulnerability is a privilege escalation exploit.
An attacker doesn't need to find a single critical vulnerability. They need to find a chain: a vector into Layer 1 (supply chain compromise), a way through Layer 2 (bypassing build verification), a bypass for Layer 3 (circumventing scanning), and a path to impact at Layer 4 (privilege escalation or lateral movement).
Most security implementations focus on one link in this chain. Strong security requires hardening all of them.
What Comprehensive Security Requires
True container security is not a product or a single tool. It's a practice that spans all four layers:
At Layer 1: Know your dependencies by using SCA tools, maintaining an SBOM, and monitoring for new CVEs in dependencies you actually use. Understand transitive dependencies and their maintainers, and for critical libraries, consider maintaining patches or forks if the maintainer is unresponsive.
At Layer 2: Make builds reproducible, verify base images via signature checking, rotate secrets frequently and never store them in registries, scan build artifacts before they're pushed, and implement hermetic builds where the output depends only on source code, not build machine state.
At Layer 3: Scan images at multiple stages including after build, before deployment, and periodically in production. Enforce signature verification on all images, implement policy enforcement that blocks images with critical vulnerabilities, and maintain access control on registries by limiting who can push, pull, or modify images.
At Layer 4: Run containers with minimal privileges by removing root access and unnecessary capabilities, remove shells unless genuinely needed, implement network policies to restrict traffic between containers, deploy behavioral monitoring to detect anomalies, and implement filesystem immutability where possible.
Security across all four layers is complex. It requires investment in tooling, process changes, and culture shifts. But the alternative — defending only one or two layers and hoping the others don't matter — is what has led to nearly every major container security incident.
The container security problem is not that tools don't exist. Tools exist at every layer. The problem is that organizations treat layers as independent when they're fundamentally interconnected. A vulnerability at Layer 1 travels through all four layers, and only addressing it at one point creates a false sense of security while the actual risk remains.
