@@ -84,6 +84,51 @@ Verifier **refuted** one finding: the knowledge-graph pipeline is not
8484"misconfigured/inert" — it is a deliberate, consistently flag-gated
8585(` GRAPH_ENABLED ` ) pre-launch feature.
8686
87+ ## Second pass — fixed (commit cf76e41, deployed + live-verified)
88+
89+ A deeper second audit (re-review of recent changes + under-covered subsystems:
90+ chains/Sui-verify, WebAuthn, the agent/MCP trust boundary, the import pipeline,
91+ reader/frontend, cross-cutting validation). New findings fixed:
92+
93+ - ** HIGH** — ` /api/mcp/call ` was unauthenticated AND unrate-limited while reaching
94+ billable Workers AI (` get_echoes ` ) + D1 writes; the first-pass AI cost fix missed
95+ it. Now per-IP rate-limited (verified: 25-burst → 24×429).
96+ - ** HIGH** — cross-publisher book takeover: ` ON CONFLICT(id) ` let any CLI-token
97+ holder overwrite another publisher's book. Added ` assertBookWritable() ` at every
98+ ingest entry point, threading the actor (` functions/lib/catalog.ts ` , ` books.ts ` ).
99+ - ** HIGH** — account-switch leaked the previous user's highlights/notes/shared-cards/
100+ reading-place (` liber.hl.* ` /` liber.nt.* ` /` liber.shared ` /` liber.place ` never cleared);
101+ the shelf-only fix missed them (` product-app.jsx ` ).
102+ - ** HIGH** — global translation-cache poisoning via ` PUT /ai/translations/:cacheKey ` ;
103+ now admin-gated (verified: non-admin → 403).
104+ - ** MED** (3 were regressions from my own recent batches) — SSRF redirect-follow
105+ bypass in ` /books/ingest ` (now manual redirect + per-hop re-validation + 12MB cap);
106+ ` runPlatformJob ` re-ran ` 'done' ` jobs on queue redelivery (now excluded);
107+ ` /billing/admin/activate ` non-constant-time token compare (now ` hasAdminToken ` );
108+ passkey ` userVerification ` mismatch (now ` requireUserVerification:false ` ).
109+
110+ Confirmed SOUND by the verifier (no change needed): zh-convert placeholder, upvote
111+ no-double-count, platform stale-reclaim serialization, listBooks sort injection-safe,
112+ no XSS surface (zero ` dangerouslySetInnerHTML ` ), path-traversal not possible (keys
113+ via ` safeId ` ).
114+
115+ ## Second pass — remaining backlog
116+
117+ - ** MED** — passkey: non-atomic registration (orphan user on cred-insert failure);
118+ no ` excludeCredentials ` + localStorage-only heuristic can fork a second account.
119+ - ** MED** — import: ` deleteStaleChapters ` orphans R2 chapter blobs + ` blobs ` rows;
120+ ` safeId ` strips CJK so Chinese-only titles without an id get a random id (breaks
121+ idempotent re-publish); chapter text silently truncates to ` text_preview ` (5000
122+ chars) on R2 miss.
123+ - ** MED** — ` /groups/:id ` builds every group then keeps one (N×~ 6 queries); malformed
124+ JSON body → 500 instead of 400 on most write routes (a few fixed: mcp/ai/billing).
125+ - ** MED** — Sui adapter can't verify zkLogin signatures (doc claims it; fails closed).
126+ - ** LOW** — read_passage/search N+1 + leading-wildcard LIKE; vote-count merges scan
127+ the whole votes table (no ` target_id IN (...) ` ); ` searchDynamic ` fabricates sentence
128+ sids; highlight color stored unvalidated; unbounded social post body sizes;
129+ ` /vote ` accepts arbitrary ` target_type ` ; login address strict-compare vs
130+ ` sameSuiAddress ` ; dead ` chainById ` .
131+
87132## Methodology
88133
891348 parallel auditors read real code and reported findings with ` file:line ` ; every
0 commit comments