Security Scan an Image
Run vulnerability scans on Docker images, interpret severity levels, apply remediation in running containers, and export Dockerfile fixes.
Prerequisites
- Docker is running
- At least one image exists locally
- Pro subscription required
- Trivy is installed (
brew install trivy) — if not installed locally, Zenithal uses the Trivy bundled inside the Lima VM
Scenario 1: Scan an Image for Vulnerabilities
- Click Image Library in the sidebar
- Hover over an image row (e.g.,
node:20-alpine) and click the shield icon ("Security Scan") - The scan begins — Trivy analyzes image layers for known vulnerabilities (CVEs)
- A progress indicator shows the scan running
- When complete, the results view opens with a severity breakdown
What You'll See
- Summary header — Overall risk level, vulnerability counts by severity, detected OS, scanner name, and scan duration
- Vulnerability list (left panel) — Sorted by severity, each showing CVE ID, CVSS score, package name, installed version, and fixed version
- Detail panel (right panel) — Full CVE description, affected package details, references, published date, and remediation options
- Rescan button — Re-run the scan after making changes (uses cached Trivy DB for consistent results)
- Save Dockerfile Fix (All) button — Export a Dockerfile that upgrades all packages
Reading Scan Results
Critical (Red)
Actively exploited vulnerabilities or those with public exploits. Update immediately — these pose an immediate risk.
Example: CVE-2024-3094 — XZ Utils Backdoor (CVSS 10.0)
High (Orange)
Serious vulnerabilities that could be exploited with some effort. Update in the next maintenance window.
Example: CVE-2024-21626 — Container escape via runc
Medium (Yellow)
Vulnerabilities requiring specific conditions to exploit. Plan to address in upcoming updates.
Example: CVE-2023-44487 — HTTP/2 Rapid Reset attack
Low (Blue)
Minor vulnerabilities with limited impact. Address as part of regular maintenance.
Example: CVE-2023-29491 — ncurses memory corruption
Result Details
Each vulnerability entry shows:
- CVE ID — the Common Vulnerabilities and Exposures identifier
- CVSS Score — numeric severity rating (0–10) with color-coded gauge
- Package — the affected OS or application package
- Installed Version — the currently installed version
- Fixed Version — the version that resolves the issue (if available)
- Severity — Critical, High, Medium, or Low
- Overview — brief summary of the vulnerability
- References — links to security advisories (NVD, vendor pages)
- Published Date — when the CVE was disclosed
Scenario 2: Run a Fix in a Container
For quick testing, you can run remediation commands directly in a running container:
- Scan an image and select a vulnerability with a fix available
- In the Remediation section, you'll see two options:
- Safe (green badge) — Upgrades all packages. Always works, recommended approach
- Specific (orange badge) — Upgrades only the affected package. More targeted but may fail if package names differ between distros
- If the image has running containers, select one from the container picker
- Click the terminal icon next to a command — Terminal.app opens with
docker execrunning the fix - The command runs as root (package managers require it) and drops you into a shell when done
Cross-OS Adaptation
Zenithal automatically adapts commands when the scan detects a different OS than what's running in the container:
- If Trivy reports Debian but the container runs Alpine,
apt-getcommands are converted toapk - Package names are also translated (e.g.,
xz-utils→xz,libssl-dev→openssl-dev) - A warning toast appears if there's an OS mismatch
Important: Container changes are ephemeral — they're lost when the container stops. For permanent fixes, use the Dockerfile export (Scenario 3).
Scenario 3: Export a Dockerfile Fix (Single CVE)
- Select a vulnerability with a fix available
- Click Save Dockerfile Fix in the remediation section
- Choose a save location — the file is named
Dockerfile.fix-<cve-id> - The generated Dockerfile:
- Starts with
FROM <scanned-image>(valid, buildable Dockerfile) - Detects the image's actual OS (reads
/etc/os-release) to use the correct package manager - Includes an "upgrade all" command (recommended) and a commented "upgrade specific package" alternative
- Translates package names if the image OS differs from what Trivy reported
- Starts with
Example Output (Alpine)
# Fix for CVE-2024-3094: Update xz-utils
# Severity: CRITICAL
# Required version: >= 5.6.1
#
# Note: This upgrades system packages in the image. Vulnerabilities baked
# into the base image layers (e.g. compiled binaries, language runtimes)
# may persist. If the issue remains after building, update the base image
# in your original Dockerfile (e.g. FROM node:22-alpine).
FROM my-app:latest
# Upgrade all packages (recommended):
RUN apk update && apk upgrade --no-cache
# Or upgrade a specific package:
# RUN apk add --upgrade --no-cache xzScenario 4: Export a Dockerfile Fix (All Vulnerabilities)
- After a scan completes, click Save Dockerfile Fix (All) in the summary header
- Choose a save location — the file is named
Dockerfile.security-fix - The generated Dockerfile lists all CVEs as comments and includes a single upgrade-all command
Example Output
# Security Fix Dockerfile — my-app:latest
# Generated by Zenithal Security Scanner
# Fixes 7 vulnerabilities (2 critical, 2 high, 2 medium, 1 low)
#
# Note: This upgrades system packages in the image. Vulnerabilities baked
# into the base image layers (e.g. compiled binaries, language runtimes)
# may persist. If issues remain after building, update the base image
# in your original Dockerfile (e.g. FROM node:22-alpine).
#
# CRITICAL:
# CVE-2024-3094 — xz-utils 5.6.0 → 5.6.1
# CVE-2024-21626 — runc 1.1.9 → 1.1.12
# HIGH:
# CVE-2023-44487 — golang.org/x/net 0.14.0 → 0.17.0
# CVE-2023-39325 — golang.org/x/net 0.14.0 → 0.17.0
FROM my-app:latest
RUN apk update && apk upgrade --no-cacheScenario 5: Build the Fixed Image
- Export a Dockerfile fix (Scenario 3 or 4)
- Go to Image Library → click Build (hammer icon)
- In the Configuration tab:
- Set Context Path to the directory containing the exported Dockerfile
- Set Dockerfile Path to the exported file
- Enter an Image Name (e.g.,
my-app-fixed) and Tag (e.g.,latest)
- Click Build Image
- After the build completes, scan the new image to verify fixes were applied
See the Build a Docker Image tutorial for a detailed walkthrough of the image build process.
Understanding Base Image Layer Vulnerabilities
When Zenithal detects that the image's actual OS differs from what the vulnerability scanner reports, an orange warning banner appears:
"Vulnerabilities detected in base image layers (Debian). This image runs Alpine. Package upgrades may not resolve these issues — update the FROM image in your original Dockerfile."
Why This Happens
Many Docker images use multi-stage builds. For example, node:20-alpine is an Alpine image, but the Node.js binary inside it was compiled on Debian. Trivy scans all image layers and finds Debian package metadata baked into the compiled binary's layers.
Running apk upgrade (Alpine's package manager) upgrades Alpine packages, but cannot fix vulnerabilities in Debian artifacts embedded in the Node.js binary. These are two different package ecosystems.
What To Do
| Vulnerability Type | Can upgrade fix it? | Real Fix |
|---|---|---|
| OS system packages (same OS) | Yes | RUN apk upgrade / apt-get upgrade |
| Base image layer packages (different OS) | No | Update FROM image to a newer version |
| Language runtime (Go, Node.js) | No | Update runtime version (e.g., FROM node:22-alpine) |
| App dependencies (npm, pip, go modules) | No | Update in package.json, requirements.txt, go.mod |
| Container runtime (runc, containerd) | No | Update Docker/Lima on the host |
Per-Vulnerability Warning
Each vulnerability in the remediation panel also shows the warning individually:
Base Image Vulnerability — This vulnerability is in the base image layers (Debian), not in the image's own packages (Alpine). Package upgrades won't fix it — update the FROM image in your original Dockerfile instead.
Tips
- Alpine-based images generally have fewer vulnerabilities due to their smaller package set
- New CVEs are published daily — rescan after pulling updated images
- After the first scan, Zenithal uses
--skip-db-updatefor consistent results within a session - Vulnerabilities are deduplicated by CVE ID + package name to avoid repeated entries
- The Image Build view auto-sanitizes image names (strips spaces and invalid characters)
- Use multi-stage builds to minimize packages in your final image, reducing the attack surface
- Docker Hub official images are regularly updated with security patches — prefer them over community images