Phase 16: CI dependency audit & security checks #15

Merged
luckberg merged 40 commits from gsd/phase-16-ci-dependency-audit-and-security-checks into main 2026-06-13 10:01:01 -04:00
3 changed files with 104 additions and 15 deletions
Showing only changes of commit b67d757f87 - Show all commits
+3 -3
View File
@@ -328,7 +328,7 @@ Plans:
Plans:
**Wave 1**
- [ ] 16-01-PLAN.md — Image-hygiene runtime: bake NODE_ENV=production + boot-time refuse-to-boot guard (IMG-01)
- [x] 16-01-PLAN.md — Image-hygiene runtime: bake NODE_ENV=production + boot-time refuse-to-boot guard (IMG-01)
- [ ] 16-02-PLAN.md — pnpm audit gate + waiver allowlist + advisory-only tiered outdated report (DEP-01, DEP-02)
- [ ] 16-03-PLAN.md — Fold eslint-plugin-security into the lint gate as blocking errors + triage (SEC-02)
- [ ] 16-04-PLAN.md — gitleaks config + full-history baseline + .dockerignore (SEC-01, IMG-02)
@@ -359,7 +359,7 @@ Plans:
| 13. Real Lint Gate (ESLint) | v1.1 | 3/3 | Complete | 2026-06-12 |
| 14. Desktop E2E Coverage | v1.1 | 1/1 | Complete | 2026-06-12 |
| 15. Doc-Only CI Skip + MD Lint | v1.1 | 3/3 | Complete | 2026-06-12 |
| 16. CI Dep Audit, Sec & Img Hyg | v1.1 | 0/6 | Not started | - |
| 16. CI Dep Audit, Sec & Img Hyg | v1.1 | 1/6 | In Progress| |
## Backlog
@@ -367,7 +367,7 @@ Plans:
**Goal:** [Captured for future planning] Abstract the calendar backend behind a provider interface so Fastmail/CalDAV is one implementation among potentially many. Shipping with a single provider is fine, but the broker, sync, and event-expansion layers should be structured so additional providers (e.g. other CalDAV hosts, Google Calendar, generic ICS feeds) can be added without rework. Captures the "provider" seam as an explicit architectural concern.
**Requirements:** TBD
**Plans:** 2/2 plans complete
**Plans:** 1/6 plans executed
Plans:
+15 -12
View File
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
milestone: v1.1
milestone_name: Operability & Polish
status: executing
stopped_at: Phase 16 context gathered
last_updated: "2026-06-13T03:25:36.437Z"
last_activity: 2026-06-13 -- Phase 16 planning complete
stopped_at: Completed 16-01-PLAN.md
last_updated: "2026-06-13T09:15:26.256Z"
last_activity: 2026-06-13 -- Phase 16 execution started
progress:
total_phases: 19
completed_phases: 6
total_plans: 17
completed_plans: 17
total_plans: 23
completed_plans: 18
percent: 32
---
@@ -21,14 +21,14 @@ progress:
See: .planning/PROJECT.md (updated 2026-06-10)
**Core value:** One color-coded family calendar (shared + personal) and shared lists from a single low-friction PWA — cross-ecosystem, no app store
**Current focus:** Phase 09faster-write-back
**Current focus:** Phase 16ci-dependency-audit-and-security-checks
## Current Position
Phase: 13
Plan: Not started
Phase: 16 (ci-dependency-audit-and-security-checks) — EXECUTING
Plan: 2 of 6
Status: Ready to execute
Last activity: 2026-06-13 -- Phase 16 planning complete
Last activity: 2026-06-13 -- Phase 16 execution started
### Deferred Checkpoint — Phase 15 Plan 15-03 Task 2 (human-action)
@@ -103,6 +103,7 @@ _Updated after each plan completion_
| Phase 13-real-lint-gate-eslint P02 | 90 | 2 tasks | 31 files |
| Phase 13-real-lint-gate-eslint P03 | 10 | 3 tasks | 399 files |
| Phase 09-faster-write-back P01 | 341 | 3 tasks | 3 files |
| Phase 16 P01 | 188 | 3 tasks | 4 files |
## Accumulated Context
@@ -164,6 +165,8 @@ Recent decisions affecting current work:
- [Phase ?]: D-04-SCHEDULE-X-LOCATOR: Used .sx-react-calendar-wrapper CSS class to assert Schedule-X grid — no semantic role on outer wrapper div
- [Phase ?]: D-04-EMPTY-NETWORK-SIM: Lists empty state simulated via page.route to 200 empty array — preserves seeded DB for parallel workers (D-06 / T-07-11)
- [Phase ?]: D-13-08: Prettier reformat committed as isolated mechanical diff; CI Format check step added to fast-checks job
- [Phase ?]: D-07 (16-01): ENV NODE_ENV=production baked into production Dockerfile stage — engages devBypass.ts hard guard so DEV_AUTH_BYPASS never injects in production
- [Phase ?]: D-08 (16-01): assertNotDevBypassInProduction() boot guard — first statement in isMainModule(), exits non-zero when NODE_ENV=production AND DEV_AUTH_BYPASS=true; unit-tested
### Roadmap Evolution
@@ -228,9 +231,9 @@ Recent decisions affecting current work:
## Session Continuity
Last session: 2026-06-13T02:50:57.477Z
Stopped at: Phase 16 context gathered
Resume file: .planning/phases/16-ci-dependency-audit-and-security-checks/16-CONTEXT.md
Last session: 2026-06-13T09:15:26.245Z
Stopped at: Completed 16-01-PLAN.md
Resume file: None
## Operator Next Steps
@@ -0,0 +1,86 @@
---
phase: 16-ci-dependency-audit-and-security-checks
plan: "01"
subsystem: api-security
tags: [security, boot-guard, docker, tdd]
dependency_graph:
requires: []
provides: [assertNotDevBypassInProduction, bootGuards.ts, ENV NODE_ENV=production]
affects: [apps/api/src/index.ts, apps/api/Dockerfile]
tech_stack:
added: []
patterns: [TDD RED/GREEN, process.exit spy, boot-time guard]
key_files:
created:
- apps/api/src/lib/bootGuards.ts
- apps/api/tests/lib/bootGuards.test.ts
modified:
- apps/api/src/index.ts
- apps/api/Dockerfile
decisions:
- "D-07: ENV NODE_ENV=production baked into production Dockerfile stage — engages devBypass.ts hard guard at image build time, not at runtime"
- "D-08: assertNotDevBypassInProduction() placed as first statement in isMainModule() — boot-time refuse-to-boot guard converts silent misconfig into loud exit(1)"
- "Guard evaluated at call time (not import time) — allows unit tests to set env vars before calling without module cache manipulation"
metrics:
duration_seconds: 188
completed_date: "2026-06-13"
tasks_completed: 3
files_changed: 4
---
# Phase 16 Plan 01: Boot-time Dev-Bypass Guard Summary
**One-liner:** Boot-time refuse-to-boot guard (`assertNotDevBypassInProduction`) plus `ENV NODE_ENV=production` baked into the production Dockerfile stage, turning a silent auth-bypass misconfiguration into an immediate non-zero exit.
## What Was Built
### Task 1 — RED (test commit 8414e89)
Created `apps/api/tests/lib/bootGuards.test.ts` with 3 test cases:
1. `NODE_ENV=production` + `DEV_AUTH_BYPASS=true``process.exit(1)` is called (spy throws to make it observable)
2. `NODE_ENV=development` + `DEV_AUTH_BYPASS=true` → no `process.exit`
3. `NODE_ENV=production` + `DEV_AUTH_BYPASS` unset → no `process.exit`
Suite failed with `Cannot find module '../../src/lib/bootGuards.js'` — RED state confirmed.
### Task 2 — GREEN (feat commit c2ffd1c)
- Created `apps/api/src/lib/bootGuards.ts` exporting `assertNotDevBypassInProduction(): void`
- JSDoc documents D-08, call-time env evaluation, and required placement rule
- Added import to `apps/api/src/index.ts`
- Added call as the **first** statement in `isMainModule()` block (before VAPID config, workers, serve())
- 3/3 unit tests pass, `pnpm typecheck` green
### Task 3 — Dockerfile ENV (chore commit 5b4f32a)
- Added `ENV NODE_ENV=production` to the `production` stage in `apps/api/Dockerfile`
- Placed between `WORKDIR /app/apps/api` and `COPY --from=pwa-builder` (exactly as specified)
- Comment references D-07
- Exactly 1 occurrence; no other stage is affected
## Deviations from Plan
None — plan executed exactly as written.
## TDD Gate Compliance
- RED gate commit: `8414e89``test(16-01): add failing tests for boot-time dev-bypass guard`
- GREEN gate commit: `c2ffd1c``feat(16-01): add boot-time refuse-to-boot guard for dev-bypass in production`
- REFACTOR: not needed — implementation was clean on first pass
## Threat Surface Scan
No new network endpoints, auth paths, file access patterns, or schema changes introduced. The boot guard adds a startup-time process.exit — no new externally-reachable surface.
## Known Stubs
None.
## Self-Check: PASSED
- `apps/api/src/lib/bootGuards.ts` — FOUND
- `apps/api/tests/lib/bootGuards.test.ts` — FOUND
- `apps/api/src/index.ts` modified — assertNotDevBypassInProduction() called at line 115
- `apps/api/Dockerfile``ENV NODE_ENV=production` present in production stage
Commits:
- `8414e89` — test(16-01): add failing tests for boot-time dev-bypass guard
- `c2ffd1c` — feat(16-01): add boot-time refuse-to-boot guard for dev-bypass in production
- `5b4f32a` — chore(16-01): bake ENV NODE_ENV=production into production Dockerfile stage