47 Known CVEs Just Deployed to Production
Why Container Image Scanning Isn't Optional Anymore
A developer pulls a base image from Docker Hub, builds their app on top, and ships it. Nobody checks what's inside that base layer. Turns out it's running an OpenSSL version with 3 critical vulnerabilities. This happens more than you think. 87% of container images in production carry high-severity CVEs. Here's how to fix it.
How It Happens
A developer pulls node:20 from Docker Hub. Builds the app on top. Pushes to the registry. CI runs unit tests, linting, maybe even integration tests. Everything passes. Ship it.
Nobody checked what's inside that base layer. That node:20 image is built on Debian Bookworm, ships with 239 packages your app never calls, and carries an OpenSSL version with 3 critical vulnerabilities. A libcurl with 2 more. A zlib with a known buffer overflow. Your application code is clean. Your container is not.
87%
of production images have high-severity CVEs
239
avg vulnerabilities in Debian-based images
120+
days since most Docker Hub images were updated
This isn't a hypothetical. This is Monday morning for most teams shipping containers.
Why “Just Update” Doesn't Work
The intuitive fix is to run apt-get update && apt-get upgrade in your Dockerfile. But Chainguard's research shows that updating OS packages in official Docker Hub images only reduces vulnerability counts by less than 6%. Before updates: 239 vulnerabilities. After updates: 225 vulnerabilities.
Why? Because 98% of vulnerabilities in Debian-based Docker images come from OS packages that are part of the distribution itself. They're not bugs in your code—they're bugs in the 200+ packages your runtime never calls but that ship in the base layer anyway. Updating within the same distribution just gives you the same bloated package set with minor patch bumps.
“You can't patch your way out of a bloated base image. The packages aren't broken—they shouldn't be there in the first place.”
The 4-Step Shift-Left Playbook
Scan in CI/CD, Not in Production
Catch CVEs Before They Ship
Add Trivy or Grype as a pipeline step. Fail the build on critical and high-severity CVEs. This takes 30 seconds to set up and saves you 3 AM incident calls. If an image has a known critical vulnerability, it doesn't get pushed to the registry. Period.
GitHub Actions — Trivy
- name: Scan image for CVEs
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.IMAGE }}
format: 'table'
exit-code: '1'
severity: 'CRITICAL,HIGH'
ignore-unfixed: trueGitLab CI — Grype
scan_image:
stage: security
image: anchore/grype:latest
script:
- grype $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--fail-on critical
--only-fixed
allow_failure: falseTrivy
All-in-one scanner: vulnerabilities, misconfigurations, secrets, IaC, and license compliance. Generates SBOMs. Maintained by Aqua Security. VS Code extension available.
Grype
Focused vulnerability scanner with excellent accuracy and minimal false positives. Smaller database updates. Pairs with Syft for SBOM generation. Maintained by Anchore.
Also consider: Docker Scout (built-in to Docker Desktop, generates SBOMs automatically) and Kubescape (CNCF incubating project, Kubernetes-native scanning with admission controller support).
Pin Your Base Images by Digest
Immutable References, Not Mutable Tags
Never use latest. Never use bare tags like node:20. Pin to a specific SHA256 digest. Tags are mutable—someone can push a different image to the same tag at any time. A digest is a cryptographic hash of the image content. If a single byte changes, the digest changes. This is your defense against supply chain attacks where malicious images get pushed to the same tag.
Bad — Mutable tag
FROM node:20-alpine
Good — Tag + digest for readability and security
FROM node:20-alpine@sha256:2f3b7a...c9d4e1
Get the Digest
docker buildx imagetools inspect \
node:20-alpine --format \
'{{ .Manifest.Digest }}'Enforce in Kubernetes
Use Kyverno or OPA Gatekeeper admission controllers to reject pods using mutable tags. Block :latest and require digest references.
Use Distroless or Minimal Base Images
Fewer Packages = Fewer CVEs
The most effective way to reduce CVEs isn't scanning—it's eliminating the packages that carry them. A standard Debian-based image ships with ~300 packages. A distroless image ships with your binary and its runtime dependencies. Nothing else. No shell, no package manager, no curl, no wget, no attack surface.
Alpine
~5 MB
musl-based, minimal, widely supported. Good first step.
Distroless
~2 MB
Google's distroless. No shell, no pkg manager. Production hardened.
Chainguard
~0 CVEs
Wolfi-based, rebuilt nightly. 97.6% CVE reduction vs Debian.
Docker Hardened
95%
fewer vulns. 1,000+ images, now free and open source (Dec 2025).
Multi-stage build — Build fat, ship thin
# Stage 1: Build with full toolchain FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --production COPY . . RUN npm run build # Stage 2: Ship with minimal runtime FROM gcr.io/distroless/nodejs20-debian12 COPY --from=builder /app/dist /app COPY --from=builder /app/node_modules /app/node_modules WORKDIR /app CMD ["server.js"]
Tradeoff: Distroless images have no shell. You can't exec into them for debugging. Use ephemeral debug containers in Kubernetes (kubectl debug) instead.
Automate Periodic Re-Scans
Images Drift. Scan Continuously.
An image that was clean last month might not be today. New CVEs are disclosed daily. If your image was built 90 days ago, it's likely accumulated new vulnerabilities since then. Schedule weekly scans of all running images and fail deployments that reference stale, unscanned images.
Scheduled CI Scans
# GitHub Actions cron
on:
schedule:
- cron: '0 6 * * 1' # Weekly Monday 6AM
workflow_dispatch: {}
jobs:
rescan:
runs-on: ubuntu-latest
steps:
- name: Scan running images
run: |
for img in $(kubectl get pods -A \
-o jsonpath='{..image}' | tr ' ' '\n' \
| sort -u); do
trivy image "$img" --severity CRITICAL,HIGH
doneRun a cron job in CI that scans every unique image running in your clusters.
Kubernetes-Native Scanning
Deploy Kubescape as an in-cluster operator. It continuously scans running workloads against CIS benchmarks, NSA-CISA guidelines, and MITRE ATT&CK frameworks.
Pair it with an admission controller that blocks pod creation if the image hasn't been scanned or fails policy checks. This is your last line of defense before deployment.
Remediation SLA benchmark: Chainguard reports average remediation of under 20 hours for critical CVEs, with 97.6% fixed within 2 days. Set your own SLAs: 72 hours for critical, 7 days for high, 30 days for medium.
Build an Approved Base Images Registry
Pre-Scanned. Pre-Hardened. Regularly Updated.
This is the highest-leverage action you can take. Instead of letting every developer pick their own base image from Docker Hub, maintain a curated internal registry of approved base images. Your developers get fast, reliable builds. Your security team gets peace of mind. Everybody wins.
Curate Your Golden Images
Select base images for each language runtime your org uses: Node.js, Python, Java, Go, .NET. Start with Alpine or distroless variants. Strip unnecessary packages. Add only what your workloads require.
Automate Nightly Rebuilds
Set up a CI pipeline that rebuilds every golden image nightly from pinned upstream digests. Scan each rebuild with Trivy. Only promote images that pass with zero critical CVEs to the "approved" tag.
Sign and Attest
Sign every approved image with Cosign (Sigstore). Attach an SBOM attestation. This creates a verifiable chain of trust from source to deployment that admission controllers can validate.
Enforce via Admission Control
Configure Kyverno or OPA Gatekeeper to only allow images from your internal registry with valid signatures. Reject anything from Docker Hub, ECR Public, or unsigned registries in production namespaces.
Publish an Internal Catalog
Create a simple internal page listing your approved images, their digests, last scan date, and CVE count. Make it easy for developers to find the right base image without hunting through Docker Hub.
The Supply Chain Threat Is Real
Public Registries Are Silent Risk Multipliers
Since early 2025, attacks on build systems, unverified registry pulls, and misconfigured Kubernetes deployments have significantly increased. Public container registries (Docker Hub, ECR Public, GHCR) now host crypto mining binaries, malware, typo-squatted images, and exposed secrets—often pulled automatically by CI/CD pipelines without any human review.
Supply chain attacks are projected to cost businesses $60 billion globally in 2025—triple the impact from 2021. The container image is the most under-audited artifact in most CI/CD pipelines.
Attack Vectors
Defenses
Scanner Comparison
| Tool | Type | Scans | SBOM | Best For |
|---|---|---|---|---|
| Trivy | Open source | Images, IaC, FS, repos, secrets | Yes (SPDX) | All-in-one CI/CD scanning |
| Grype | Open source | Images, FS, SBOMs | Via Syft | Accurate, minimal false positives |
| Docker Scout | Free tier | Images via Docker Desktop/Hub | Yes | Docker-native workflows |
| Kubescape | Open source (CNCF) | Images, K8s configs, runtime | Via Grype | Kubernetes-native security |
| Snyk Container | Commercial | Images, code, IaC | Yes | Enterprise integrations, fix PRs |
What NOT to Do
Don't Scan Only at Build Time
New CVEs are disclosed daily. An image that was clean at build time can have critical vulnerabilities discovered a week later. You need continuous re-scanning of running images, not just build-time gates.
Don't Ignore "Unfixed" CVEs
Some scanners report CVEs with no available fix. Don't just suppress them and move on. Track them, set SLAs, and use them as leverage to move to base images where fixes ship faster (Alpine, Chainguard).
Don't Treat Scanning as a Gate Without Action
A scan that generates a 200-line report nobody reads is security theater. Set clear severity thresholds, auto-fail builds on critical CVEs, and route findings to owners with remediation deadlines.
Don't Use "latest" in Production
The :latest tag is a mutable pointer. It can change under you between builds, between environments, between deploys. In 2026, using :latest in production is negligence, not convenience.
Don't Skip the SBOM
Without a Software Bill of Materials, you can't answer "are we affected?" when the next Log4Shell drops. Generate SBOMs for every image, store them alongside the image, and make them queryable.
Your Action Plan
Implement This Week
Security isn't a gate—it's a guardrail. Build it into the road, not at the end of it. Here's your concrete implementation plan:
This Week's Checklist
Add Trivy or Grype to one CI pipeline. Set it to fail on CRITICAL severity. Takes 5 minutes.
Audit your Dockerfiles for :latest or bare tags. Replace with tag@sha256:digest references.
Pick one high-traffic service and switch its base image from Debian to Alpine or distroless. Measure the CVE delta.
Set up a weekly cron scan of all images running in your staging cluster.
Generate an SBOM for your most critical production image using Syft or Trivy.
Document your team's approved base images in a shared wiki/README. List the image, digest, last scan date, and CVE count.
Add a Kyverno or OPA Gatekeeper policy to block :latest tags in your staging namespace.
Schedule a 30-minute team meeting to review scan results and set remediation SLAs.
Key Takeaways
87% of container images in production carry high-severity CVEs. Most come from bloated base image packages your app never uses.
Updating OS packages in Debian-based images only reduces CVE count by ~6%. You need a fundamentally different base image, not a patch.
Scan in CI/CD, not in production. Fail builds on critical CVEs. Trivy and Grype both integrate in under 5 minutes.
Pin base images by SHA256 digest, not mutable tags. Never use :latest in production. Enforce via admission controllers.
Use distroless, Alpine, Chainguard, or Docker Hardened Images. Fewer packages = fewer CVEs = smaller attack surface.
Automate weekly re-scans of running images. New CVEs are disclosed daily; build-time scans alone are not enough.
Build an approved base images registry: pre-scanned, signed with Cosign, rebuilt nightly, enforced via admission policy.
Generate SBOMs for every image. When the next Log4Shell hits, you need to answer "are we affected?" in minutes, not days.
Supply chain attacks cost $60B globally in 2025. Public registries are silent risk multipliers. Sign, verify, and restrict.
Security Isn't a Gate. It's a Guardrail.
Build it into the road, not at the end of it. Every unscanned image in production is a ticking clock. Start with one pipeline, one scanner, one policy. Then scale.
Related Posts
Ingress NGINX Is Dying Next Month. Here's Your No-Panic Plan.
The most popular Kubernetes ingress controller — used by roughly half of all cloud-native environments — gets archived in March 2026. No more security patches. No more bug fixes. Maintained by a tiny team of volunteers, it collapsed under burnout. Here's your no-panic migration plan.
Kubernetes for AI/ML Workloads: The New Infrastructure Standard
Kubernetes has evolved from a container orchestration platform to THE de facto substrate for AI/ML workloads in 2025. With over 90% of teams planning to increase their AI workloads on K8s, it has become the central component powering enterprise AI infrastructure.
Claude Code Hit $2.5B. Amazon Engineers Can't Use It. Welcome to AI Agent Lock-In.
Claude Code just hit a $2.5 billion run-rate — doubled since January 1st. Yet 1,500 Amazon engineers are fighting for permission to use it, steered toward AWS Kiro instead. This is vendor lock-in repackaged for the AI agent era. Platform-native vs platform-agnostic is the new architectural fault line.