4.7 KiB
4.7 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics, requirements
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | requirements | |||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 16-ci-dependency-audit-and-security-checks | 06 | ci |
|
|
|
|
|
|
|
Phase 16 Plan 06: Image Hygiene CI Assertions Summary
One-liner: Publish-time CI assertions that block docker push when .dockerignore is incomplete, --target production is dropped, or the production image tolerates DEV_AUTH_BYPASS=true (D-10 / IMG-03).
What Was Built
Two CI assertion steps added to .gitea/workflows/publish.yml, inserted strictly between the Build production image step and the Push image step:
Step 1 — "Image hygiene — static assertions" (set -euo pipefail):
- Fails if
.dockerignoreis absent - Loops over 7 forbidden patterns (
.env,node_modules,apps/api/scripts,.git,.planning,apps/api/tests,apps/pwa/e2e) and fails if any is missing from.dockerignore - Fails if
--target productionis no longer grep-able inpublish.ymlitself - Catches config drift that would ship secrets or dev files (T-16-19)
Step 2 — "Image hygiene — boot-smoke (must refuse dev-bypass in production)" (set -euo pipefail):
- Runs the freshly-built
sha_tagimage withNODE_ENV=production DEV_AUTH_BYPASS=true timeout 15caps the run (T-16-21: prevents the job hanging if the guard does not fire)- EXIT==0 → image started → guard NOT working → FAIL
- EXIT==124 → timeout → guard not firing → FAIL
- Any other non-zero exit → image refused boot → PASS
- Proves
assertNotDevBypassInProduction()(16-01 / D-08) fires in the actual shipped image (T-16-18)
The step order in the publish job is now:
- Checkout
- Compute image tags
- Docker login
- Build production image
- Image hygiene — static assertions ← new
- Image hygiene — boot-smoke ← new
- Push image
- Docker logout (if: always())
Task Commits
| Task | Name | Commit | Files |
|---|---|---|---|
| 1 | Split publish build and push into separate steps | bc00f3e |
.gitea/workflows/publish.yml |
| 2 | Add static image-hygiene assertion + boot-smoke before push | 27046db |
.gitea/workflows/publish.yml |
Deviations from Plan
None — plan executed exactly as written.
Verification
- YAML parses cleanly (python3 yaml.safe_load confirmed)
- Step ordering verified: assertions at indices 4-5, push at index 6 (between Build at 3 and Push at 6)
- All 7 forbidden .dockerignore patterns confirmed present in .dockerignore
--target productiongrep confirmed to match within publish.yml- Dockerfile
ENV NODE_ENV=productionconfirmed in production stage (line 45) — precondition for the smoke to be meaningful bootGuards.tsprocess.exit(1)confirmed fires whenNODE_ENV=production AND DEV_AUTH_BYPASS=true— that is what the smoke exercises
Threat Coverage
| Threat ID | Description | Mitigated By |
|---|---|---|
| T-16-18 | Regressed image that tolerates DEV_AUTH_BYPASS=true published | Boot-smoke step: fails push if image starts under forbidden combo |
| T-16-19 | Future change removes .dockerignore or drops --target production | Static assertions step: fails push on any missing pattern or pin |
| T-16-20 | Assertions run after push (bad image already published) | Ordering: both steps are before Push image step |
| T-16-21 | Boot-smoke hangs if guard does not fire | timeout 15; EXIT==124 treated as failure |
Known Stubs
None.
Self-Check
Files modified:
.gitea/workflows/publish.yml— modified (confirmed by git log)
Commits:
bc00f3e— ci(16-06): split publish build and push into separate steps27046db— ci(16-06): add static image-hygiene assertion + boot-smoke before push