diff options
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 14 |
1 files changed, 9 insertions, 5 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 38977af..f1473f0 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -1755,10 +1755,14 @@ card text lives in a bounded in-memory TTL cache; the image rides the same blake3-keyed store as any other thumbnail. **The new surface is SSRF**, because the URL is a member's choice and it triggers an outbound request from the operator's machine: http(s) only, no credentials, a port allowlist, every resolved address -must be globally routable, redirects followed by hand so each hop is re-checked, -and the address the connection landed on re-checked before the body is read (the -request itself has been sent by then, so this refuses the answer, not the -request). The body is read as a stream and stops at its cap — 512 KiB of page, +must be globally routable, and redirects followed by hand so each hop is +re-checked. **The socket is opened to the address that was checked**: the name is +resolved once, off the event loop, every answer is checked, and the connection +goes to that IP literal while TLS verifies the certificate for the name. Checking +one resolution and letting the HTTP client make another would let a name answer +clean and then with a LAN address, and the request would leave before anything +looked; resolving on the event loop would stall every group on a slow name. No +proxy from the environment is used, since a proxy resolves the name itself. The body is read as a stream and stops at its cap — 512 KiB of page, 2 MiB of image — counted after decompression, so a small compressed response is bounded like any other; an image declared larger than its cap is not read, and the whole fetch has a 15-second deadline. Previews are rate-limited per @@ -3456,7 +3460,7 @@ be understood, not so the incident can be retold. | **M2a** | `sender_id` comes from the authenticated session (**NS6**). Now guaranteed by there being **one** chat implementation: QUIC does not carry chat at all (§5.1) | | **M2b** | Chat broadcast is per group (**H1**), on the one transport that carries chat | | **M2c** | No transport runs a synchronous media process on the event loop, and every one is capped | -| **M3** *(third review)* | Link-preview SSRF is gated: rate limit, port allowlist, globally-routable check, per-hop re-check, connect-address re-check, size guard (§6.5) | +| **M3** *(third review)* | Link-preview SSRF is gated: rate limit, port allowlist, globally-routable check, per-hop re-check, connection pinned to the checked address, size guard (§6.5) | | **M4** *(third review)* | Federation binds a pushed row's source to the signer, checks the token audience, caps the push, rejects replays, and scopes revocation to the peer's own entries (§7.6) | | **M5** *(third review)* | A CSP and security headers apply to the hub-served application, verified against the running app — a mis-tuned CSP shows as a blank page | | **M6** *(third review)* | **Withdrawn.** It misread the node registering a hub membership during the CLI invite flow — which is deliberate — as authorization drift | |