From dea6261deb4ac81887bee1e7ccd0a4e10f603f75 Mon Sep 17 00:00:00 2001 From: araharah-executor Date: Sun, 25 Oct 2026 04:41:12 +0800 Subject: [PATCH] =?UTF-8?q?docs(07-03):=20complete=20the=20consolidated=20?= =?UTF-8?q?privacy-audit=20plan=20=E2=80=94=20SUMMARY?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 12-vector TDD suite, 4 source gates + bundle gate + 2 live probes, the 0/1/2 exit contract, and docs/PRIVACY-AUDIT.md signed-record scaffold - PRIVACY AUDIT CLEAN — 5 gate(s) passed, 0 violations on the audited tree - baselines intact: bot 125/125, web 165/165 --- .../07-03-SUMMARY.md | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 .planning/phases/07-hardening-private-launch/07-03-SUMMARY.md diff --git a/.planning/phases/07-hardening-private-launch/07-03-SUMMARY.md b/.planning/phases/07-hardening-private-launch/07-03-SUMMARY.md new file mode 100644 index 0000000..5301e13 --- /dev/null +++ b/.planning/phases/07-hardening-private-launch/07-03-SUMMARY.md @@ -0,0 +1,149 @@ +--- +phase: 07-hardening-private-launch +plan: 03 +subsystem: testing +tags: [privacy-audit, grep-gates, comment-stripping, kv-ttl, zero-telemetry, allowlist, wrangler, cloudflare-kv, curl-probe] + +# Dependency graph +requires: + - phase: 03-mini-app-shell-geolocation + provides: the memory-only client law + the canonical comment-stripper (web/test/no-storage.test.mjs, D-07 origin) + - phase: 02-bot-skeleton-initdata-security + provides: the PG1/PG2 TTL-only KV write law (bot/test/privacy.test.mjs) and the D-14 category-only log vocabulary (bot/src/webhook.ts) + - phase: 05/06 routing + guidance + provides: the route-privacy RP1/RP2 allowlist style and the TTL key families the audit probes (bot/src/kv/*) +provides: + - "The ONE consolidated audit CLI: npm run privacy-audit (scripts/privacy-audit.mjs) — 5 static gates + 2 --live probes, exit codes 0/1/2 (never a silent pass)" + - "scripts/lib/source-gates.mjs — storage-api / kv-ttl / log-vocabulary / runtime-hosts gates extended repo-wide over ALL five src trees" + - "scripts/lib/bundle-gates.mjs — PRODUCTION web/dist telemetry scan (analytics/beacon tokens, positive host allowlist, deep-link crypto, storage tokens)" + - "scripts/lib/cloudflare-probes.mjs — live kv-expiry (list_complete/cursor/expiration parsing) + live asset-headers probes, degrading to notRun without credentials" + - "docs/PRIVACY-AUDIT.md — the signed written audit record citing the ACTUAL run evidence (D-01), with a human signature block for the launch gate" +affects: [07-04 (budget-audit shares the _headers/live-probe surface), 07-05 (limitations doc), 07-06 (launch gate — the privacy-audit-signed checklist leg), any fork onboarding] + +# Tech tracking +tech-stack: + added: [] # zero new dependencies — node:fs/node:child_process/curl/npx wrangler only + patterns: + - "Gate-as-function returning {id, pass, evidence, violations} (07-RESEARCH Pattern 4 shape)" + - "Canonical comment-stripped source introspection with documented prefer-fail adjudications (theme-audit 'Scope adjudications' precedent)" + - "Positive host allowlist with the inventory PRINTED, never assumed (A4)" + - "notRun as the exit-2 class: a skipped probe is never a pass (T-07-16)" + - "PRIVACY_AUDIT_ROOT env override — cwd-independent gates, fork/test injectability" + +key-files: + created: + - scripts/privacy-audit.mjs + - scripts/lib/source-gates.mjs + - scripts/lib/bundle-gates.mjs + - scripts/lib/cloudflare-probes.mjs + - scripts/test/privacy-audit.test.mjs + - docs/PRIVACY-AUDIT.md + modified: + - package.json (the root "privacy-audit" script) + +key-decisions: + - "Bundle host scan adjudication: MapLibre's own static-text URLs (maplibre.org, wiki.openstreetmap.org, github.com diagnostics, maplibre.invalid) + our ODbL license strings (opendatacommons.org) form a DOCUMENTED static-text class alongside the 2-host fetch allowlist — each host enumerated with its reason; any other host fails" + - "W3C namespace identifiers (svg/xhtml) excepted as spec constants, never fetched — documented in both gate files" + - "Line-numbered gates scan raw lines with only the // tail removed (honest line numbers); block-comment prose mentioning a scanned token fails prefer-fail" + - "Asset-headers probe reads HTTP status BEFORE interpreting headers — a stale-deploy 404 is deployment drift (notRun, exit-2 class), never a cache violation" + - "The audit suite is a hand-rolled 12-vector TDD file (scripts/test/privacy-audit.test.mjs) with fixture-root injection, proving each gate can FAIL — a gate that cannot fail is decoration" + +patterns-established: + - "PRIVACY AUDIT CLEAN/FAILED/NOT RUN — the consolidated audit's summary vocabulary + 0/1/2 exit contract" + - "Gates are cwd-INDEPENDENT (module-URL root) so a stray subdirectory run cannot false-pass" + +requirements-completed: [PX-04] # PX-04 claimed here: SC3 is a Phase-7 success criterion under the PX-04 phase; PRIV-02 + GEO-03 are re-verified end-to-end (D-01), not re-claimed + +# Metrics +duration: 9min +completed: 2026-10-24 +--- + +# Phase 7 Plan 03: Consolidated privacy audit + signed record Summary + +**One consolidated `npm run privacy-audit` CLI — 5 static gates over all five src trees + the production bundle, 2 credential-gated live probes, honest 0/1/2 exit codes — plus `docs/PRIVACY-AUDIT.md`, the signed audit record citing the real run's evidence (D-01/D-02).** + +## Performance + +- **Duration:** 9 min (20:32Z → 20:41Z) +- **Started:** 2026-10-24T20:32:07Z +- **Completed:** 2026-10-24T20:41:00Z +- **Tasks:** 3 (TDD RED→GREEN) +- **Files modified:** 7 (6 created, 1 modified) + +## Accomplishments + +- The repo's gate lineage consolidated, not duplicated: the phase-3 memory-only law, the PG1 same-line TTL law, the D-14 category-only log law, and the bundle laws are now enforced REPO-WIDE by one tool — the existing unit-level suites stay as the pins +- All 5 static gates PASS on the audited tree (commit `9c55e9a`): 72 files scanned, 4 KV writes all TTL-named, 28 log call sites category-verified, host inventories printed (source + bundle), ZERO analytics/beacon/crypto/storage tokens in `web/dist` +- `docs/PRIVACY-AUDIT.md` carries the ACTUAL evidence strings from the run, the four KV-family storage inventory, the log vocabulary, the runtime-network inventory with D-02's zero-telemetry posture, and the blank human signature block (D-09's launch gate signs it) +- Live probes verified honest-by-construction: without credentials both return notRun and the CLI exits 2 — never a silent pass (T-07-16) + +## task Commits + +Each task was committed atomically (TDD: RED gate commit → GREEN feat commits): + +1. **task 1: the source-gates library** — `87fcb28` (test: RED — 12-vector suite) + `430993b` (feat) +2. **task 2: bundle-gates + cloudflare-probes** — `b959e23` (feat) +3. **task 3: the CLI + npm script + signed record** — `9c55e9a` (feat) + `8229e36` (docs) + +## Files Created/Modified + +- `scripts/lib/source-gates.mjs` — runStorageGate / runKvTtlGate / runLogVocabularyGate / runRuntimeHostGate over web/bot/routing/search/theme src +- `scripts/lib/bundle-gates.mjs` — runBundleTelemetryGate (PRODUCTION `web/dist` ONLY; `dist-harness` forbidden) +- `scripts/lib/cloudflare-probes.mjs` — runKvExpiryProbe / runAssetHeaderProbe / probeUnavailable (namespace id read from `bot/wrangler.jsonc` at runtime) +- `scripts/privacy-audit.mjs` — the orchestrator: Pattern-4 ordering, per-gate evidence lines, CLEAN/FAILED/NOT RUN summaries, process.exit 0/1/2 +- `scripts/test/privacy-audit.test.mjs` — the 12-vector TDD suite (SA1-5, BG1-2, CP1-2, AU1-3) +- `package.json` — root `"privacy-audit": "node scripts/privacy-audit.mjs"` +- `docs/PRIVACY-AUDIT.md` — the signed audit record (D-01) + +## Decisions Made + +- **Bundle static-text adjudication:** the plan's 2-host allowlist fails on MapLibre's own embedded attribution/diagnostic URLs in the shipped bundle (they are library constants and our dataset's ODbL metadata — never fetched). Followed the repo's own "Scope adjudications (documented, deliberate)" precedent (theme/scripts/audit.mjs): an explicitly enumerated static-text class, each host with its reason, full inventory printed as evidence. Any host outside both lists fails. +- **Header probe honesty:** the first live run exposed the probe interpreting a 404 as "no cache-control" — the probe now reads HTTP status first and treats origin/build mismatch as deployment drift (exit-2 class). The immutable `/assets/*` policy ASSERTION belongs to 07-04's budget-audit (its `_headers` file); this probe quotes, never interprets. +- **Test injectability:** gates accept an opts-only roots/dist override (production callers pass nothing), letting the suite prove each gate FAILS on synthetic violations in temp fixtures. +- **cwd-independent root** with the `PRIVACY_AUDIT_ROOT` override — a stray subdirectory run must not scan the wrong tree and false-pass. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Asset-header probe misread a stale-deploy 404 as a cache violation** +- **Found during:** task 3 (first `--live` run) +- **Issue:** the probe fetched the local dist's hashed asset from the deployed origin without checking HTTP status; a 404 printed "no cache-control header observed" and FAILED the gate with `undefined violation(s)` (also a `${violations.length}` bug on a number) +- **Fix:** status line read before header interpretation; non-2xx = notRun (deployment drift) with observed headers carried in evidence; the count bug fixed +- **Files modified:** scripts/lib/cloudflare-probes.mjs +- **Verification:** `npm run privacy-audit -- --live` now exits 2 with `PRIVACY AUDIT NOT RUN — deployment drift …`; CP2 vector pins the shape +- **Committed in:** b959e23/9c55e9a follow-ups (folded into the task-2/3 commits) + +--- + +**Total deviations:** 1 auto-fixed (1 bug) +**Impact on plan:** The fix makes the live probe's honesty contract hold end-to-end. The live legs were recorded NOT RUN in docs/PRIVACY-AUDIT.md — honest, exit-2 class, re-run at the launch gate. + +## Issues Encountered + +- The deployed Worker (`araharah-bot.workers.dev`) serves a bundle older than the current `web/dist` (its hashed asset 404s) — correctly reported as deployment drift by the probe; a redeploy (07-04/07-06) plus shipped `_headers` will turn the live legs green. + +## Known Stubs + +None — every gate/probe is fully wired; no placeholder data flows anywhere. + +## User Setup Required + +None — no external service configuration required. (The `--live` probes need `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` in the environment; absent credentials degrade to exit 2 by design, never a pass.) + +## Next Phase Readiness + +- 07-04 (budget-audit) re-runs the live header probe against a deployed build carrying its `_headers` policy — the two plans share the live-probe surface +- 07-06 (launch gate) signs `docs/PRIVACY-AUDIT.md`'s signature block as the privacy-audit checklist leg; the `--live` re-run protocol is documented in the record +- Baselines intact: bot 125/125, web 165/165; `npm run privacy-audit` exits 0 (PRIVACY AUDIT CLEAN — 5 gate(s) passed, 0 violations) + +--- +*Phase: 07-hardening-private-launch* +*Completed: 2026-10-24* + +## Self-Check: PASSED + +All 7 created files verified on disk; all 5 task commits verified in git log +(`87fcb28` test-RED, `430993b`, `b959e23`, `9c55e9a`, `8229e36`). STATE.md and +ROADMAP.md untouched (orchestrator-owned).