Bundle A: Container & supply-chain hardening — 3 findings closed; All High closed

Closes H-001 + M-012 + M-014 from comprehensive-audit-2026-04-25.

H-001 (CWE-829) — Container base images SHA-pinned
  Pre-bundle: 5 FROM lines pulled by tag only — registry-side tag
  swap could silently change the build.
  Post-bundle: every FROM pinned to immutable digest fetched live
  from Docker Hub at audit time:
    node:20-alpine@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293
    golang:1.25-alpine@sha256:5caaf1cca9dc351e13deafbc3879fd4754801acba8653fa9540cea125d01a71f (x2)
    alpine:3.19@sha256:6baf43584bcb78f2e5847d1de515f23499913ac9f12bdf834811a3145eb11ca1 (x2)
  Dockerfile header comment documents the operator bump procedure
  (quarterly cadence; docker manifest inspect or Hub Registry API).
  CI step Forbidden bare FROM regression guard (H-001) fails build
  if any new FROM lacks @sha256.

M-012 (CWE-250) — Verified-already-clean + USER guard
  Recon found both Dockerfile:75 and Dockerfile.agent:59 already
  carry USER certctl directives; pre-USER RUN calls are build-setup
  steps that legitimately need root, each happening before the
  USER drop.
  CI step Forbidden missing USER regression guard (M-012) greps
  every Dockerfile* for the LAST USER directive; fails build if
  missing OR equals root/0. Future Dockerfile additions must
  preserve the privilege drop.

M-014 — npm ci explicit retry helper
  Pre-bundle Dockerfile:25:
    RUN npm ci --include=dev || npm ci --include=dev && \
        tsc --version && npm run build
  Broken bash precedence: A || (B && C && D) means tsc+build only
  ran on success path of the second npm ci. A transient registry
  blip silently skipped the production step — build would succeed
  with no node_modules + no tsc verification.
  Post-bundle: deterministic 3-attempt retry loop with 5s backoff
  plus explicit [ -d node_modules ] post-check that fails loudly
  if directory wasn't created. Silent failure is now impossible.

Audit deliverables:
  audit-report.md: H-001/M-012/M-014 flipped [x] with closure
    notes; score 49/55 closed (High 9/9 = 100%; Medium 24/27;
    Low 19/19 with L-004 deferred). All High audit findings now
    closed for the first time.
  findings.yaml: 3 status flips
  CHANGELOG.md: Bundle A section

Verification:
  Self-test of both new CI guards locally — PASS for current state
  (every FROM has @sha256; every Dockerfile drops to non-root).
This commit is contained in:
shankar0123
2026-04-27 01:28:38 +00:00
parent 87086fbe33
commit 12003f5ca5
4 changed files with 119 additions and 6 deletions
+48
View File
@@ -166,6 +166,54 @@ jobs:
exit 1
fi
- name: Forbidden bare FROM regression guard (H-001)
# Bundle A / Audit H-001 (CWE-829): every FROM line in every
# Dockerfile in the repo MUST carry an @sha256:... digest pin in
# addition to the human-readable tag. A registry-side tag swap
# cannot then change what we pull. This step grep-fails the
# build if any new FROM lands without the @sha256 suffix.
run: |
set -e
# Match any "FROM image[:tag]" that does NOT contain @sha256.
# Strip comments and blank lines defensively.
BAD=$(find . -name 'Dockerfile*' -not -path './web/node_modules/*' \
-exec grep -HnE '^FROM\s+[^@#]+(\s+AS\s+\S+)?\s*$' {} \; || true)
if [ -n "$BAD" ]; then
echo "::error::Dockerfile has bare FROM (no @sha256 digest pin):"
echo "$BAD"
echo ""
echo "Pin every FROM to an immutable digest. See the bump"
echo "procedure in Dockerfile's header comment (Bundle A / H-001)."
exit 1
fi
- name: Forbidden missing USER regression guard (M-012)
# Bundle A / Audit M-012 (CWE-250): every Dockerfile in the repo
# MUST end with a `USER <non-root>` directive before the
# ENTRYPOINT/CMD so the container never runs as uid=0. This step
# grep-fails the build if any Dockerfile is missing such a USER.
# `USER root` and `USER 0` are explicitly rejected.
run: |
set -e
BAD=""
for df in $(find . -name 'Dockerfile*' -not -path './web/node_modules/*'); do
# Find the LAST USER directive in the file.
last_user=$(grep -E '^USER\s+\S+' "$df" | tail -1 | awk '{print $2}')
if [ -z "$last_user" ]; then
BAD="$BAD\n$df: no USER directive at all"
continue
fi
if [ "$last_user" = "root" ] || [ "$last_user" = "0" ]; then
BAD="$BAD\n$df: terminal USER is $last_user (must drop privileges)"
continue
fi
done
if [ -n "$BAD" ]; then
echo "::error::Dockerfile USER-drop regression:"
echo -e "$BAD"
exit 1
fi
- name: Forbidden README JWT advertising regression guard (H-009)
# H-009 closed by Bundle D as verified-already-clean: at audit time
# the README does NOT advertise JWT support (certctl does not ship