Scope: This document covers the Docker base images used by the SecuScan backend (
python:3.11-slim-bookworm) and frontend (nginx:1.27-alpine). It defines when and how those images must be updated to keep the container supply chain safe.
Base images inherit all OS-level packages from the upstream distribution. New CVEs are published daily, and a pinned base image that was clean at build time can become vulnerable within weeks. Failing to update means:
- Trivy scans will start failing the CRITICAL policy gate in CI.
- SecuScan containers may be deployed with known exploitable vulnerabilities.
| Responsibility | Owner |
|---|---|
| Keeping base images current and approving update PRs | Project maintainers (reachable via §6 Contacts) |
| Automated vulnerability detection — no human trigger needed | CI (the Trivy Vulnerability Scan workflow; see Cadence below) |
| Proposing an update | Any contributor may open a PR (§3); a maintainer must review and merge it |
There is currently no
CODEOWNERSfile, so review and approval are handled by the maintainer role above rather than an auto-assigned reviewer. Route security-sensitive updates through the private advisory path in §6 Contacts.
| Trigger | Who | Action |
|---|---|---|
Weekly cron — Mondays 06:00 UTC (0 6 * * 1) |
CI (automated) | The Trivy Vulnerability Scan workflow builds and scans both images. Its "Fail on CRITICAL vulnerabilities" step fails the run on any new CRITICAL and uploads SARIF to the GitHub Security tab. |
On change — push/PR to main touching backend/Dockerfile, frontend/Dockerfile, backend/requirements*.txt, or frontend/package*.json |
CI (automated) | The same Trivy Vulnerability Scan runs, so a base-image or dependency change is checked before merge. Can also be triggered on demand (workflow_dispatch). |
| New upstream minor/patch release | Maintainer | Update the FROM line within 5 business days of release. |
| Zero-day or CRITICAL CVE advisory | Maintainer / any contributor | Update within 24 hours of public disclosure. |
| Quarterly | Maintainer | Full review of all pinned versions (OS packages, base tag, and digest). |
# Backend
docker pull python:3.11-slim-bookworm
docker inspect python:3.11-slim-bookworm --format '{{index .RepoDigests 0}}'
# Frontend
docker pull nginx:1.27-alpine
docker inspect nginx:1.27-alpine --format '{{index .RepoDigests 0}}'Change the FROM line in:
backend/Dockerfilefrontend/DockerfileExample (pinning by digest for full reproducibility):
FROM python:3.11-slim-bookworm@sha256:<new-digest> AS baseNote: Tag-only pins (
python:3.11-slim-bookworm) are acceptable for development velocity; digest pins are required for any release/production build. The CI workflow accepts tag pins and will still catch new CVEs via the weekly cron.
# Build and scan backend
docker build -t secuscan-backend:local ./backend
trivy image --severity CRITICAL,HIGH --ignore-unfixed secuscan-backend:local
# Build and scan frontend
docker build -t secuscan-frontend:local ./frontend
trivy image --severity CRITICAL,HIGH --ignore-unfixed secuscan-frontend:localInstall Trivy locally: https://aquasecurity.github.io/trivy/latest/getting-started/installation/
The PR title should follow: chore(docker): update base images YYYY-MM-DD
Include in the PR description:
- Old tag/digest → new tag/digest
- Link to upstream changelog or CVE advisory (if emergency update)
- Trivy output showing zero CRITICALs before and after
If a CVE is:
- Unfixed upstream (no patched version available), and
- Not exploitable in the SecuScan threat model (e.g., vulnerability is in
a library component that SecuScan never calls)
…it may be suppressed with a Trivy
.trivyignoreentry. The entry must:
- Reference the CVE ID and a comment explaining why it is not exploitable.
- Include an
expiresdate no more than 90 days out. - Be approved by a maintainer in a PR review.
Example
.trivyignore:
# CVE-2024-XXXXX: affects libssl's QUIC path; SecuScan does not use QUIC.
# Re-evaluate when python:3.11-slim-bookworm ships OpenSSL ≥3.x.
# Expires: 2026-08-01
CVE-2024-XXXXX
Both Dockerfiles must run application processes as a non-root user.
The CI hardening check (the hardening-check job in
.github/workflows/docker-hardening.yml)
enforces this automatically and will fail if id -u inside the container returns 0.
- Backend: user
secuscan(UID 1001) - Frontend: user
nginx(UID 101, built intonginx:*-alpine) If a new base image changes the default UID, update the Dockerfile and this document accordingly.
| Role | Contact |
|---|---|
| Security issues | Open a private advisory via GitHub Security tab |
| General update PRs | Open a standard pull request and tag a maintainer |