Reviewed-on: #14
CVE Differential
A self-contained Gitea and GitHub Actions composite action that extracts multi-platform SBOM attestations from container images, runs vulnerability scans with Trivy, and produces a set-based CVE delta report between a head image and a target baseline image.
✨ Features
- Dynamic Multi-Platform Support: Automatically discovers and scans all architectures present in container images, or iterates over an explicit list of target architectures.
- Fast Attestation Extraction: Uses
docker buildx imagetools inspectto extract platform-specific SBOMs directly from registry manifest indices in seconds without downloading layer blobs. - Graceful Fallbacks: If SBOM attestations are missing for any platform, automatically falls back to generating SBOMs directly with Trivy per architecture.
- Set-Based Differential Analysis: Categorizes vulnerabilities into:
- 🔴 Introduced Vulnerabilities (newly added in this branch / commit)
- 🟢 Fixed Vulnerabilities (present in target baseline, resolved in this branch)
- ⚪ Unchanged Vulnerabilities (pre-existing in target baseline)
- Secure by Default: Verified Trivy binary downloads against static SHA256 checksum lockfiles and single-step unified caching (binary + vulnerability database).
📥 Inputs
| Input | Description | Required | Default |
|---|---|---|---|
head-image |
Head container image reference to scan (e.g. registry.example.com/repo:git-sha) |
Yes | — |
target-image |
Target baseline container image reference to compare against (e.g. registry.example.com/repo:latest) |
Yes | — |
target-branch |
Target branch name for report headings | No | main |
architectures |
Comma-separated architectures/platforms to scan (e.g. amd64,arm64). If omitted, scans all architectures present in the image. |
No | (all) |
output-delta |
Output path for the CVE delta Markdown report | No | cve-delta.md |
output-summary |
Output path for the full vulnerability Markdown summary | No | cve-summary.md |
write-summary |
Whether to append the generated reports to the job summary ($GITHUB_STEP_SUMMARY) |
No | true |
use-inbuild-sbom |
Whether to inspect and use built-in image SBOM attestations. If false, generates SBOMs directly with Trivy | No | false |
📤 Outputs
| Output | Description |
|---|---|
delta-report |
Path to the generated CVE delta Markdown report |
summary-report |
Path to the full vulnerability Markdown summary report |
🔍 Built-in SBOM Attestations vs. Trivy Direct Scanning (Syft vs. Trivy)
This action supports two modes for obtaining SBOMs:
1. Direct Image Scanning with Trivy (use-inbuild-sbom: false — Default & Recommended)
By default, the action uses trivy image --format spdx-json to generate the SBOM directly from the container images:
- Full Filesystem Fidelity: Inspects the entire flattened root filesystem, reading
/etc/os-releaseand native package databases (e.g./var/lib/dpkg/status, RPM databases, apk). - Accurate OS & Language Package Detection: Successfully catalogs both OS packages (Debian, Alpine, RHEL, etc.) and application dependencies (Python, Node.js, Go, etc.).
- Symmetric, Apples-to-Apples Comparisons: Both
headandtargetimages are analyzed using the identical Trivy engine and vulnerability database, preventing skewed diffs.
2. Built-in Attestation Extraction (use-inbuild-sbom: true)
When enabled, the action extracts SBOM attestations attached to the container image using docker buildx imagetools inspect:
- Speed: Extracts the SBOM JSON metadata directly from the registry manifest index in seconds without pulling image layer blobs.
- Underlying Generator (Syft): Docker Buildx (
sbom: true) uses BuildKit's embedded Syft scanner (moby/buildkit-syft-scanner). - Known Limitations with Trivy:
- Testing/Rolling Distributions: Embedded Syft versions may not recognize newer or unreleased distributions (such as Debian 13 "Trixie"), recording the OS as
family="none"and skipping OS package vulnerability matching. - Multi-Stage Builds: Analyzing layer diffs rather than the flattened container rootfs can cause package database paths to be omitted or detached from the OS manifest.
- Schema/PURL Discrepancies: Syft and Trivy express certain package qualifiers and namespaces differently in SPDX. When Trivy scans a Syft SPDX file that lacks explicit distro qualifiers, Trivy will silently skip OS vulnerability checks.
- Differential Skew: If a
headimage uses an incomplete Syft SBOM (detecting only language packages) whiletargetis scanned directly with Trivy (detecting 400+ OS packages), the differential report will erroneously show hundreds of "resolved" CVEs.
- Testing/Rolling Distributions: Embedded Syft versions may not recognize newer or unreleased distributions (such as Debian 13 "Trixie"), recording the OS as
Tip
If your builds run on stable base images where BuildKit's Syft scanner accurately captures all OS packages, you can set
use-inbuild-sbom: 'true'to speed up CI runs. Otherwise, leave it asfalseto ensure consistent and comprehensive scans.
🚀 Usage Example
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- name: Build and Push Head Container Image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: registry.example.com/my-app:git-${{ github.sha }}
- name: Compare Vulnerabilities (Head vs Target)
uses: https://git.horstenkamp.eu/Philipp/cve-differential.git@main
with:
head-image: registry.example.com/my-app:git-${{ github.sha }}
target-image: registry.example.com/my-app:latest
target-branch: ${{ github.base_ref || 'main' }}
📄 License
MIT