Skip to content

Vulnerability Scanning

Overview

Every image released from this repository is scanned by Trivy as part of the release pipeline. Scan results are:

  1. Uploaded to the GitHub Security tab in SARIF format (visible under Security → Code scanning)
  2. Printed as a table in the workflow build log
  3. Available for download as a workflow artifact

What Trivy scans

Scan type What it finds
os CVEs in OS packages (dpkg, apk, rpm)
library CVEs in language packages (npm, pip, cargo, composer, gem, go modules)
Misconfigurations Dockerfile best practice violations (in source scan mode)
Secrets Accidentally committed credentials (in source scan mode)

The release pipeline scans in image mode, covering os and library types.

Severity levels

Severity Description Default behavior
CRITICAL CVSS 9.0–10.0. Actively exploited or trivially exploitable Reported, build does not fail
HIGH CVSS 7.0–8.9 Reported
MEDIUM CVSS 4.0–6.9 Reported
LOW CVSS 0.1–3.9 Not reported by default

The default severity filter is CRITICAL,HIGH,MEDIUM. Adjust via the trivy-severity input to the cosign-sign-attest action.

Why the build doesn't fail on findings

The release pipeline sets exit-code: 0 for Trivy, meaning CVE findings do not block the release. This is intentional:

  • Signing and publishing the image creates traceability — it does not imply the image is CVE-free.
  • Many CVEs in base OS packages have no available fix yet (ignore-unfixed: true filters these out).
  • Blocking releases on unfixable CVEs creates pressure to suppress reporting rather than fix issues.
  • The GitHub Security tab provides a centralized, actionable view for triage.

For images used in critical production contexts, you can set exit-code: 1 in the action input.

Viewing findings

GitHub Security tab

  1. Go to the repository on GitHub
  2. Click SecurityCode scanning
  3. Filter by tool: Trivy
  4. Each finding shows the affected package, CVE ID, severity, and a direct link to the NVD advisory

Workflow log

The table-format Trivy run in the Sign & Attest job prints a human-readable summary to the build log:

1
2
3
4
5
+--------------+------------------+----------+-------------------+
| Library      | Vulnerability    | Severity | Installed Version |
+--------------+------------------+----------+-------------------+
| openssl      | CVE-2024-XXXXX   | HIGH     | 3.0.11-1          |
+--------------+------------------+----------+-------------------+

Remediation workflow

  1. Triage: Review findings in the GitHub Security tab. Identify which findings are in packages that your application actually uses.
  2. Update base image: Most CVEs in OS packages are fixed by updating to the latest base image. Update the ARG RUNNER_VERSION or FROM line in the relevant Dockerfile and create a new release.
  3. Track via Renovate: Renovate is configured to automatically open PRs for base image updates. Enabling docker: true in your Renovate config ensures base image CVEs are patched quickly.
  4. Dismiss false positives: In the GitHub Security tab, you can dismiss findings with a justification (not affected, tolerated risk, etc.).

Running Trivy locally

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# Install Trivy
brew install trivy  # macOS
# or: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

# Scan an image
trivy image \
  --severity CRITICAL,HIGH \
  --ignore-unfixed \
  ghcr.io/webgrip/github-runner:latest

# Output as JSON for programmatic processing
trivy image \
  --format json \
  --output results.json \
  ghcr.io/webgrip/github-runner:latest

# Scan the Dockerfile for misconfigurations
trivy config ops/docker/github-runner/Dockerfile

Integration with the homelab cluster

The homelab cluster runs Trivy Operator, which continuously scans running workloads and produces VulnerabilityReport and ExposedSecretReport CRDs. These feed into Grafana dashboards in the security namespace.

1
2
3
4
5
# View vulnerability reports in the cluster
kubectl get vulnerabilityreports -A

# Inspect a specific report
kubectl describe vulnerabilityreport <name> -n <namespace>