← CommitteeR4R3R2Score

Round 5 · bedrock: tenant isolation + sign-code + live-score + JSON DoS + injury sub + mass action

30 bedrock-level tests on the remaining product-security classes.

After 4 rounds + ~165 tests + 13 product bugs fixed, round 5 hits the last bedrock classes: multi-tenant data isolation across listing endpoints, scoresheet co-sign code brute-force resistance, live-scoring access control, JSON-payload DoS, injury substitution rule path, and admin mass-action authorization.

30
✓ OK
0
⚠ Friction
0
✗ Bug

Tenant isolation

VQuestionResultObserved
Public stats — hkpl vs uat returns DIFFERENT data (tenant routing works)✓ Different per hosthkpl=200 uat=200 hkpl_size=56 uat_size=56
Fetch uat team stats via hkpl host✓ 404 (tenant-scoped)http=404
Fetch hkpl team stats via uat host✓ 404 (tenant-scoped)http=404
/public/teams under hkpl host — only hkpl-tenant ids✓ Pure hkplcount=126 sample_all_hkpl=true leaked_uat_id=false
/public/teams under uat host — only uat-tenant ids✓ Pure uatcount=141 sample_all_uat=true leaked_hkpl_id=false
/public/results under uat host — sample IDs all uat-tenant✓ Pure uatsample_ids=5 all_uat=true
uat captain cookie reused on hkpl host — captain API access✓ Blockedhttp=401
SUPER_ADMIN sees all tenants via /super/tenants✓ 200 (by design)http=200 count=6

Sign code

VQuestionResultObserved
Submit a real scoresheet to create the per-game match codes✓ Submittedhttp=200
Per-game code entropy (3 random bytes → 6 hex chars)✓ 16^6 = 16.7M combinations × 5 gamescrypto.randomBytes(3).toString('hex').toUpperCase()
Wrong 6-char code (AAAAAA) rejected✓ Blockedhttp=403 {"error":"invalid_match_code"}
Brute-force code flood (20 wrong codes) hits 429✓ Rate-limitedcodes=403,429 429=16
Empty body — match_code missing✓ 400http=400 {"error":"match_code_required"}
Non-captain (PLAYER) tries to sign✓ Blockedhttp=403

Live score

VQuestionResultObserved
Judge starts live scoring✓ 200http=200 {"ok":true,"match_id":4202,"live":{"state":"in_progress","teamA":0,"teamB":0,"current_game":1,"games":[{"a":0,"b":0}],"format":"BO5-21-SUDDEN-DEATH-AT-20","max_games":5,"score_targ
Judge logs a point on team A✓ 200http=200 {"ok":true,"live":{"games":[{"a":1,"b":0}],"state":"in_progress","teamA":1,"teamB":0,"events":[{"t":"s","by":"ee960932-ea93-4b3b-8a6a-beac6b7a7c70","ts":1782301319222,"format":"BO5
Judge undo last point✓ 200http=200
Random PLAYER tries to log a live point✓ Blocked (403)http=403
Spectator tries to log a live point✓ Blockedhttp=403
Malformed point payload (team="INVALID")✓ Rejectedhttp=400 {"error":"team_required_A_or_B"}

JSON DoS

VQuestionResultObserved
11MB body — rejected or dropped, server stays up✓ http=0, health 200 afterpayload_resp=0 health_after=200
100k-element array — server stays up✓ health 200 afterpayload_resp=0 health_after=200
Prototype pollution attempt via __proto__ / constructor.prototype✓ Object.prototype intactpollute_attempt_http=404 polluted=false
2000-deep nested object — server doesn't crash✓ Returned 404http=404

Injury sub

VQuestionResultObserved
Judge files an injury sub-request✓ Filedhttp=200 {"ok":true,"kind":"queued_for_review","request_id":"cmqs06rqy000ro0u1b67uuhez","message":"Substitution request filed. A division rep will review and approve."}
Random PLAYER tries to file a sub-request✓ Blockedhttp=403
Sub-request with missing fields✓ 400http=400 {"error":"invalid_body","detail":[{"code":"invalid_type","expected":"string","received":"undefined","path":["original_dupr"],"message":"Required"},{"c

Mass action

VQuestionResultObserved
Captain tries to broadcast a notification✓ Blocked (403)http=403
Admin sends broadcast to PLAYER role✓ 200http=200 {"ok":true,"recipients":1730,"target_role":"PLAYER"}
Admin broadcast with empty body✓ Rejectedhttp=400 {"error":"title_body_target_role_required"}