Manual Test Runbook — I1: Container Image Scanning
Owner: Sagar | Time: ~3 min (Part A offline) · +10 min (Part B live scan) | Cloud: none · $0
Promotes I1 (
.github/workflows/image-scan.yml) from 🟦 Code Complete → 🟩 Shipped. Reusableworkflow_callimage scan (Trivy). Distinct from C2's build-time grype: I1 scans an arbitrary image ref (base image, vendor image, periodic re-scan). Part A is offline (YAML lint). Part B is a live scan viaworkflow_dispatch.
Prerequisites
- Local tooling:
python3(YAML lint), optionallydocker+trivyfor a local dry-run - (Part B only)
ghauthenticated; the workflow present on the default branch - Working directory: repo root
Steps
Part A — workflow lint (offline, ~3 min)
bash python3 -c "import yaml; yaml.safe_load(open('.github/workflows/image-scan.yml')); print('parses OK')" # Optional, if installed: actionlint .github/workflows/image-scan.yml
Expected: parses OK. Confirm workflow_call inputs (image,
severity_cutoff default CRITICAL,HIGH, ignore_unfixed,
fail_on_findings, upload_sarif) and optional registry_username /
registry_password secrets.
- (Optional) local equivalent of the gate, no CI needed:
Expected: a known-old base image (alpine:3.14) fails (exit 1); a current
alpine:latest passes (exit 0). Proves the cutoff/exit-code wiring.
Part B — live dispatch scan (~10 min, $0)
- Run the scan against a deliberately-old image:
Confirm the trivy (image) job fails on High/Critical findings and the
results appear under Security → Code scanning (category trivy-image).
-
Re-run with a clean current image (
-f image=alpine:latest) and confirm green. -
(Optional) Wire a real caller: a downstream repo's workflow
uses:this file withimage: <acr>.azurecr.io/app@sha256:...and registry secrets; confirm the optional registry-login step runs only when a username is supplied.
Sign-Off
| Field | Value |
|---|---|
| Part A (lint) | ☐ PASS |
| Part B (live scan) | ☐ PASS / ☐ skipped |
| Tester | |
| Date | |
| Result | ☐ PASS |