Skip to content

0010 - Scan API Container Images in Pull Request CI

Status: Accepted Date: 2026-08-10 Author: Manuel Nucci

Context

FPC packages the API as a multi-stage container. Its final image contains Alpine packages, the Node.js runtime, compiled application code, Prisma artifacts, and production dependencies. Manifest checks and source review do not prove that this assembled artifact is free of known operating-system or application-library vulnerabilities, or that it contains no detectable secrets.

The GitHub Actions Docker job builds the final image as fpc-api:ci and runs it for a health check when API, Shared, Docker, workflow, or root dependency inputs change. The repository already uses a full-SHA-pinned Trivy action for Terraform configuration scanning. Scanning that local image in the same job covers the CI artifact without pushing a CI-only image or adding another security vendor.

The workflow runs for pull requests to dev and main, and for pushes to those branches. It does not run this Docker job for arbitrary feature-branch pushes. The supporting comparison is recorded in docs/research/container-image-scanning-options.md.

Decision

Use the existing Docker job as the mandatory container-image gate for pull requests and protected-branch pushes that build the API image.

  • After building fpc-api:ci and before starting test containers, run the pinned Trivy action against that local image.
  • Remediate the initial HIGH/CRITICAL baseline through narrowly scoped existing-dependency overrides before enabling the blocking gate. Do not exclude those findings.
  • Scan for vulnerabilities and secrets. Fail the job for HIGH or CRITICAL findings; do not ignore unfixed findings or introduce a blanket exception. Any future exception must be narrow, justified, and time-bounded.
  • Keep the scan under the current change classifier, so it runs when inputs capable of changing the API image or its workflow change.
  • Do not add a container build or image scan to the pre-commit hook. Developers may run the equivalent scan locally for earlier feedback, but that is optional and does not replace CI.
  • Configure Dependabot's docker ecosystem for apps/api to propose weekly base-image version updates to dev. These updates complement the image scan; Dependabot does not inspect packages inside the assembled image.
  • Keep Dependabot alerts and Dependabot security-update pull requests separate from Docker version updates. GitHub bases security-update pull requests on the repository default branch (main); target-branch: dev applies only to scheduled version updates. This ADR does not add a Dependabot security-update configuration.
  • Keep the blocking result in the GitHub Actions job log. SARIF upload to GitHub Code Scanning is reporting only and remains outside this decision until the repository entitlement and desired alert workflow are confirmed.

Defer the following until post-MVP hardening because they add recurring registry or CI cost:

  • Google Artifact Analysis or another registry-side vulnerability-scanning service.
  • Scheduled or continuous rescanning of unchanged images after new advisories are published.

The workflow configuration enforces scans on its eligible CI runs; it does not by itself prove that GitHub has executed a particular run or that a passing scan establishes the absence of vulnerabilities. Until recurring scanning is introduced, an image is reassessed only when an eligible CI run rebuilds it.

Consequences

Positive

  • Eligible pull requests and pushes fail before progressing when the exact fpc-api:ci image has a detected HIGH or CRITICAL vulnerability or a detectable secret.
  • The implementation reuses the existing image build, scanner vendor, action-pinning policy, and change detection.
  • Dependabot proposes base-image version updates weekly against the integration branch without changing the default-branch behavior of security updates.
  • No paid registry-scanning account, long-lived token, or CI-only image push is required before post-MVP hardening.

Negative

  • An issue introduced on a feature branch is not checked by this hosted Docker job until a pull request is opened.
  • A vulnerability disclosed after the last eligible CI run can remain undetected until the image is rebuilt or recurring scanning is added.
  • Image builds and advisory-database refreshes add time and an external-data dependency to the Docker job.
  • Severity-based gates can produce findings with no immediate upstream fix or no known exploit path in FPC's runtime context.

Mitigations

  • Open a draft pull request when hosted feedback is needed early, and use the optional local scan for sensitive work before that point.
  • Keep the Trivy action pinned to an audited full commit SHA and update it deliberately.
  • Remediate the initial baseline before enabling the gate, then record only narrow, justified, expiring exceptions; do not use blanket ignores or silently exclude unfixed findings.
  • Reconsider registry-side scanning and scheduled rescans as a named post-MVP hardening decision rather than treating them as already provided by this CI gate.