---
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 ✓
