Worker anomaly-resolve endpoint accepted any string for direct Bearer callers — TT F5 (PR #565)
Felix Bristol Stokes Croft 36-yo 9-yr independent API security 4-yr Monzo platform security + 5-yr solo UK SaaS audits + Cloudflare Worker security reviews side habit reading deepest open-source repo layers past Next.js routes into Worker handlers less rigorous review. Q1 2026 thMenu affiliate anomaly resolution superadmin Next.js + Cloudflare Worker /api/affiliate/anomaly-resolve called from Next.js + internal scripts/crons API_SECRET_KEY Bearer. Next.js validates upstream worker validate or trust blindly? cloudflare/src/handlers/affiliate-anomaly-resolve.ts const body = await req.json(); UPDATE affiliate_anomalies SET resolved=1, resolution=?, resolved_by=?, resolved_at=? WHERE id=?. 4 problems (1) resolution ANY string no validation expected dismissed/suspended/banned state machine attacker resolution=free_pro_tier_forever <script>; (2) resolved_by ANY string expected system or UUID attacker resolved_by=ghost_admin blurring audit trail; (3) no row-existence check + no race-guard UPDATE runs blindly anomaly_id doesn't exist 0 rows update 200 OK race condition concurrent resolve second overwrites first attacker overwrite historical resolve re-write history; (4) no audit log structured logging direct-worker callers cron script future MCP server attacker-with-API-key not in audit trail. Threat model Next.js Zod enum + auth + CSRF safe but API_SECRET_KEY Bearer leak attacker direct worker arbitrary resolution + arbitrary resolved_by + history overwrite + audit blur defense-in-depth gap. Writeup CVSS 5.8 MEDIUM privilege escalation via secret leak + audit trail compromise. Engineering 3 wrong theories (1) API_SECRET_KEY already secret leak hypothetical defense-in-depth can't assume zero leak PR #585 XX F2 backup encryption + PR #560 SS F1 BACKUP_SKIP_DATA layered-defense pattern; (2) Next.js validates upstream worker can silently trust worker directly callable public endpoint cron + scripts + future MCP trust model can't assume upstream validation already done; (3) resolved_by free-form text gives ops flexibility audit log integrity requires canonical system or UUID arbitrary blurs audit trail degrades SOC 2 CC7.1 evidence integrity. PR #565 batch TT F5 4-layer hardening Layer 1 ANOMALY_RESOLUTIONS allowlist Set dismissed/suspended/banned 422 invalid_resolution future addition Set drift impossible. Layer 2 RESOLVED_BY_PATTERN regex ^(system|UUID v4)$ 422 invalid_resolved_by ghost_admin reject. Layer 3 race-guard UPDATE AND resolved=0 + meta.changes=0 404 not_found_or_already_resolved atomic race-guard concurrent re-resolve fails 404 generic message covers both anomaly doesn't exist and already resolved. Layer 4 structured console.log audit trail event + anomaly_id + resolution + resolved_by + resolved_at + caller_ip Logpush + Sentry pickup SOC 2 audit evidence + suspicious-pattern detection rapid re-resolves ghost_admin attempts. Production audit 90-day 247 total calls all legitimate Next.js or system cron 0 leak-exploit patterns defense-in-depth secret-leak scenario remained hypothetical never realized. Hardening shipped anyway 4-layer fix Felix verification access lab bypass arbitrary resolution 422 + arbitrary resolved_by 422 + re-resolve 404 expected. Felix €1400 Wise CVSS 5.8 + Hall of Fame + advisory board LinkedIn 4.0k UK DevSecOps community reference. Selcuk Istanbul Kadikoy Moda 34-yo 8-yr ex-Hepsiburada platform security parallel discovery thMenu fork audit €1200 bounty Turkish security community blog 1.9k. Pattern Worker handlers must not trust upstream Next.js routes each layer validates independently allowlist set + regex pattern + race-guard + structured audit log quartet every state-changing worker endpoint defense-in-depth closes gap secret-leak scenarios. Sibling sweep /api/affiliate/anomaly-resolve TT F5 + /api/affiliate/postback-secret/rotate PR #609 CCC-B dual-secret + OCC + /api/admin/erase-user PR #611 DDD F1 audit log + structured trail + /api/cron/inventory-predict PR #606 CCC F1 claim-rollback + /api/cron/feedback-sentiment PR #639 IV F4 poison-pill loop budget + /api/superadmin/affiliate/[id]/status PR #660 X F4 OCC race-guard. Implementation state-changing worker handler identify + allowlist set canonical state values + regex pattern actor identifiers system/UUID + race-guarded UPDATE AND state_field = expected + meta.changes 404 not_found_or_state_mismatch + structured console.log audit trail event + entity + actor + IP + timestamp + PR template checkbox + quarterly worker-handler grep audit. PR #565 reference.
thMenu Team
thmenu.com
Found this helpful? Share it.
Related articles
Why Digital Menus Increase Restaurant Revenue by Up to 30%
Studies show restaurants using digital QR menus see measurable increases in aver…
When a Customer Downgrades, What Happens to Old Features? — The Silent Feature-Drift Problem in SaaS
Most SaaS apps run a single line of code when a customer downgrades — but old fe…
JWT alg-confusion attack — why Supabase's HS256 → RS256/JWKS migration breaks legacy verifiers
Verifiers that never decode the JWT header are wide open to `alg=none` and alg-c…