Skip to content

Adopt Trivy for Container Image Vulnerability Scanning in CI/CD

Status

Proposed — Trivy will be introduced as the standard container image and filesystem vulnerability scanner, integrated into the CI/CD pipeline for all platform-built images.


1. Context

The platform produces several container images as deployable artefacts, including the hybridops-tech frontend (Node.js/Astro SSR) and the entitlements-api backend (Node.js/Express). As these images are deployed into Kubernetes and interact with external services (Stripe, Keycloak, PostgreSQL), CVEs in OS packages, runtime dependencies, or base images represent a concrete risk surface.

Currently no image scanning is performed before push to the container registry. There is no automated mechanism to detect when a base image (node:22-slim, node:22-alpine) receives a known CVE, or when a dependency introduced in package.json carries a vulnerability.

The platform requires a scanning approach that: - Integrates into GitHub Actions without requiring an external SaaS account or paid tier. - Covers both OS packages (Debian/Alpine) and language-level dependencies (node_modules). - Produces structured output usable in CI gates and developer feedback loops. - Scales cost-effectively as the number of images grows.


2. Decision

Adopt Trivy (by Aqua Security, open source) as the standard tool for container image vulnerability scanning. Trivy will be integrated as a GitHub Actions step that runs against each built image before it is pushed to the container registry.

The initial integration scope: - Scan target: built container image (post docker build, pre docker push) - Scanner: vuln (OS packages + language dependencies) - Fail threshold: CRITICAL severity by default; HIGH configurable per image - Output format: table for PR annotations, sarif for GitHub Security tab upload - Scope: all images built by CI — hybridops-tech and entitlements-api as first targets

Trivy's database is updated on each scan run from Aqua's public vulnerability DB (no account required).


3. Rationale

Why Trivy over alternatives:

  • Snyk provides stronger developer-IDE integration and auto-fix PRs, but requires an account and has dependency on SaaS availability. It is better suited to dependency-level scanning earlier in the development cycle, not as a gate on the final image. It may be added later for package.json scanning at the branch level.
  • Wiz is an agentless cloud posture tool suited to organisations running multiple cloud accounts with a dedicated security team. It is out of scope at the current platform scale.
  • Grype (Anchore) is a comparable open-source alternative. Trivy is preferred here because it covers both OS and language ecosystems in a single invocation and has a well-maintained GitHub Actions integration.
  • Docker Scout is available natively in Docker Hub workflows but less portable to self-hosted or GHCR-based pipelines.

Trivy satisfies the core requirement: zero-cost, zero-account, single-tool coverage of OS + language CVEs, running in CI at image build time.


4. Consequences

4.1 Positive consequences

  • CVEs in base images and node_modules are surfaced automatically before any image reaches the registry.
  • SARIF upload to the GitHub Security tab provides a persistent, searchable record of vulnerability state per branch.
  • CI gate on CRITICAL severity prevents shipping known-critical images accidentally.
  • No external account or SaaS dependency — Trivy operates entirely within the GitHub Actions runner.
  • Consistent scan output format enables future tooling (dashboards, trend analysis) without vendor lock-in.

4.2 Negative consequences / risks

  • Trivy does not verify image signatures or perform SBOM attestation. Supply chain provenance is out of scope for this ADR.
  • Trivy's vulnerability database has a propagation lag — a newly published CVE may not appear in the database for several hours. This is an acceptable trade-off for a free, pull-based model.
  • False positives are possible, particularly for OS packages where the vendor has backported a patch without bumping the version string. These will require manual suppression via a .trivyignore file.
  • A pipeline scanning step adds build time (~30–60 seconds per image). This is acceptable given the security benefit.
  • Trivy does not replace JWKS signature validation, mTLS, or runtime policy enforcement — it is a build-time control only.

5. Alternatives considered

  • Snyk — rejected as primary image scanner due to SaaS account requirement and per-image rate limits on the free tier. Retained as a candidate for dependency scanning at the source level (a separate future ADR).
  • Wiz — rejected as out of scope; aimed at cloud posture across multi-account environments with dedicated security teams.
  • Grype — valid alternative. Not chosen over Trivy because Trivy covers more ecosystems in one tool and has broader community adoption in GitHub Actions contexts.
  • Docker Scout — rejected due to tighter coupling with Docker Hub and less flexible CI integration for GHCR-based workflows.

6. Implementation notes

The Trivy scan step will be added to the GitHub Actions workflow for each image build. Example step pattern:

- name: Scan image for vulnerabilities
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ghcr.io/hybridops-studio/hybridops-tech:${{ github.sha }}
    format: sarif
    output: trivy-results.sarif
    severity: CRITICAL,HIGH
    exit-code: "1"

- name: Upload Trivy scan results to GitHub Security tab
  uses: github/codeql-action/upload-sarif@v3
  if: always()
  with:
    sarif_file: trivy-results.sarif

A .trivyignore file at the repository root will be used to record accepted false positives with justification comments.

Images affected by this ADR: - hybridops.tech/deploy/Dockerfilenode:22-slim base - control/backend/entitlements-api/Dockerfilenode:22-alpine base

Scan step is added after docker build and before docker push in all relevant workflows.


7. Operational impact and validation

  • A failing Trivy scan blocks the push step; the PR cannot merge until either the vulnerability is resolved or an explicit suppression is added to .trivyignore with justification.
  • SARIF results in the GitHub Security tab provide an audit trail of what was scanned and when.
  • Periodic base image updates (node:22-slim, node:22-alpine patch releases) should be tracked as part of routine dependency maintenance, prompted by Trivy alerts appearing on the main branch.

8. References


Maintainer: HybridOps.Studio License: MIT-0 for code, CC-BY-4.0 for documentation unless otherwise stated.