Skip to content

Commit 05b7812

Browse files
DaviRain-Suclaude
andcommitted
docs: record second-pass audit findings (fixed + backlog)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent cf76e41 commit 05b7812

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

docs/AUDIT.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

89134
8 parallel auditors read real code and reported findings with `file:line`; every

0 commit comments

Comments
 (0)