--- phase: 16-ci-dependency-audit-and-security-checks plan: "02" subsystem: ci-security tags: [dependency-audit, pnpm-audit, pnpm-outdated, allowlist, tdd] dependency_graph: requires: [] provides: [scripts/check-audit.mjs, scripts/audit-allowlist.json, scripts/check-outdated.mjs, scripts/outdated-pins.json] affects: [16-05-ci-security-job] tech_stack: added: [] patterns: [node-wrapper-script, tdd-red-green, audit-allowlist-pattern] key_files: created: - scripts/check-audit.mjs - scripts/audit-allowlist.json - scripts/check-outdated.mjs - scripts/outdated-pins.json - scripts/__tests__/check-audit.test.mjs modified: [] decisions: - "D-04/D-05: Audit wrapper uses committed allowlist (audit-allowlist.json) with reason+reviewer+expiry per GHSA; Option B over native pnpm.auditConfig.ignoreGhsas (no accountability metadata in native approach)" - "D-06: check-outdated.mjs always exits 0; tiered report never gates" - "Pitfall 1 honored: pnpm audit --json with NO --audit-level flag" - "TDD gate: test(16-02) RED commit precedes feat(16-02) GREEN commit" metrics: duration: 25 completed: "2026-06-13" tasks: 3 files: 5 --- # Phase 16 Plan 02: Dependency Audit Gate + Outdated Report Summary **One-liner:** pnpm audit blocking wrapper with committed GHSA allowlist (esbuild waiver pre-seeded) plus tiered outdated report — both as standalone Node.js scripts, TDD-verified. ## What Was Built ### Task 1 — Audit allowlist + pin reasons (chore, `0f101bd`) - `scripts/audit-allowlist.json`: committed GHSA waiver map. Seeded with `GHSA-gv7w-rqvm-qjhr` (esbuild High advisory, transitive dev-only via drizzle-kit/vitest/vite, not in production image). Each entry carries `reason`, `reviewer`, and `expires` fields for auditability. - `scripts/outdated-pins.json`: flat package→reason map for four intentional pins: eslint (ESLint 10 breaks eslint-plugin-react), @eslint/js (same), zod (v4 breaking API), @types/node (Node 22 LTS types). ### Task 2 — check-audit.mjs blocking wrapper, TDD (`7ac8b19` RED → `6eb5107` GREEN) - `scripts/check-audit.mjs`: exports two pure functions (`selectBlocking`, `partitionAdvisories`) for unit testing. Main body runs only when invoked directly (import.meta.url guard). Uses `pnpm audit --json` with no `--audit-level` (Pitfall 1 honored). Exits 1 on unwaived High/Critical; exits 0 with advisory report for moderate/low. - `scripts/__tests__/check-audit.test.mjs`: 5 cases via `node:test` + `node:assert` (no extra deps). Covers: unwaived High → blocking; waived High → not blocking; moderate/low only → not blocking; no advisories → not blocking; mixed → correct partition. - All 5 tests green. ### Task 3 — check-outdated.mjs tiered report (`baf2e3a`) - `scripts/check-outdated.mjs`: classifies outdated packages into four tiers (AUDIT-ADVISORY > MAJOR-BEHIND-INTENTIONAL > MAJOR-BEHIND-UNPINNED > ROUTINE-DRIFT). Cross-checks `pnpm audit --json` to surface pinned-but-vulnerable packages under AUDIT-ADVISORY. Reads `outdated-pins.json` to label intentional pins with their reason. Always `process.exit(0)` — never gates (D-06). - Live run output: eslint/@eslint/js/zod/@types/node correctly under INTENTIONAL, @vitejs/plugin-react/jsdom/typescript under UNPINNED, hono/mysql2/@types/react under ROUTINE-DRIFT. ## Verification Results - `node --test scripts/__tests__/check-audit.test.mjs` → 5/5 pass - `node scripts/check-outdated.mjs` → exit 0, tiered report printed - `node -e "require('./scripts/audit-allowlist.json')['GHSA-gv7w-rqvm-qjhr']"` → defined - `grep "execSync" scripts/check-audit.mjs` → `pnpm audit --json` (no `--audit-level`) - `grep -c "process.exit(0)" scripts/check-outdated.mjs` → 1 - `grep "process.exit(1)" scripts/check-outdated.mjs` → absent ## TDD Gate Compliance | Gate | Commit | Message | |------|--------|---------| | RED | 7ac8b19 | test(16-02): add failing tests for check-audit.mjs filter logic | | GREEN | 6eb5107 | feat(16-02): add check-audit.mjs blocking wrapper + unit tests | TDD gate sequence correct: test commit precedes implementation commit. ## Deviations from Plan None — plan executed exactly as written. ## Known Stubs None. All scripts are fully functional with live data. ## Threat Flags No new threat surface introduced. Files created are scripts (no network endpoints, no auth paths, no schema changes). ## Self-Check: PASSED - `scripts/check-audit.mjs` — exists ✓ - `scripts/audit-allowlist.json` — exists ✓ (GHSA-gv7w-rqvm-qjhr present) - `scripts/check-outdated.mjs` — exists ✓ - `scripts/outdated-pins.json` — exists ✓ - `scripts/__tests__/check-audit.test.mjs` — exists ✓ - Commits 0f101bd, 7ac8b19, 6eb5107, baf2e3a — all present in git log ✓