·Tech retro·2 min·Review needed
Adversarial Security Review: 11 Confirmed Flaws and What We Fixed Now
After the build, we ran a multi-agent adversarial review (security/auth/data/capture/PWA/spec) and confirmed 11 flaws. Fixed immediately the ones that break the demo/spec; documented the 2 production-scale items.
AI version
Once the app was working at e1a924e, we ran an adversarial review across 6 dimensions (security/auth/data/capture/PWA/spec) and adopted each finding only after independent verification. 11 confirmed flaws. All verified by reading the code directly.
Fixed immediately (all ed3600c)
| Flaw | Verification | Fix |
|---|---|---|
| dev-login auth bypass in production | Route has no NODE_ENV guard → an email alone issues a session for someone else's box | Production notFound() guard |
| Upload MIME unvalidated → stored XSS | The contributor's file.type is stored and reflected as-is; svg/html possible | MIME whitelist (isAllowedImageMime/Audio) + X-Content-Type-Options: nosniff on media responses |
| SW caches private box HTML | The navigate branch ran caches.put on every response → leak on logout / shared device | network-only + offline fallback, cache v1→v2 to clear out the old |
| Media route ignores Range (iOS playback broken) | Advertises Accept-Ranges but always returns 200 | Parse Range → 206 + Content-Range |
| No CSRF protection | Cookie is the sole authority for state changes, no Origin check | Enforce same-origin via proxy.ts (Next 16, formerly middleware) |
| No per-token rate limit + unbounded body buffering | Public endpoints | Per-token throttle + early cutoff on body Content-Length |
| Partial env regenerates session secret | All-or-nothing, so giving just SESSION_SECRET was ignored | Apply each key individually |
| AudioRecorder reports every failure as 'mic denied' | The recordFailed copy was dead code (violates §18) | Distinguish by err.name + unmount-safe |
Verification method (run directly): evil Origin POST → 403, same-origin → 200; text/html as photo → 400; curl -H "Range: bytes=0-99" → 206/content-range; unauthenticated media → 401.
Not fixed now, documented instead (production-scale only, no demo impact)
- Persist notification dedupe: currently in-memory (fine for a single process). With multiple instances/restarts, duplicate sends are possible → needs a persistent
(reminderId, date, HH:MM)marker. PGlite today is single-instance, so no impact on the demo. - Trusted proxy IP: the rate-limit key is based on the raw
X-Forwarded-For(spoofable) → should use the hosting trusted IP. Partial defense is already in place via per-token throttle.
Notes
- The review is structured as find → independent verification → adopt. We separated the verification step to filter out "plausible but wrong" findings.
- One honest limitation: the fixes above were verified by running the dev server, but we did not reproduce the actual attack scenarios (e.g., a real cross-site form) — only confirmed standard defenses like Origin checks and nosniff at the code level.
Review needed
No human review on this entry yet.