fix(server): inert proxy responses, API-call header, image-only uploads; origin check by host

From the final review: proxied archived HTML could run on our origin and read a
remembered token; /api/upload accepted any Wayback URL and GIF-prefixed bytes; the
origin check broke behind a TLS proxy; one unreadable snapshot could fail a run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-15 01:49:52 +02:00
1 parent 5084f8c557
commit baa3476b73
16 files changed
+183 -26

No files matched your search

+2
View File
@@ -58,6 +58,8 @@ Put it behind a reverse proxy that terminates TLS and **appends** the client add
no file is deleted before its 30 days.
- Nothing is uploaded or submitted without an explicit user action; saves use `save_mode: "review"`.
- `/api/fetch` only fetches allowlisted hosts; `/api/upload` only takes a source URL, never bytes.
- Proxied bodies are inert (`application/octet-stream`, sandbox CSP, attachment), and `/api/*` fetch/upload need
the `x-mbseed: 1` header; `/api/upload` only rehosts from source image hosts or their Wayback copies.
## Known rough edges
See the open questions in spec §7 and [`TODO.md`](TODO.md).
+18
View File
@@ -16,6 +16,24 @@ the repo every session — measurably slower and more tokens).
**Consequences:** Rule changes happen in one file; pointer files must stay thin. Plans are working
artefacts: kept after merge they go stale and contradict the code, so the spec is the record.
## 2026-10-15 — Harden the server proxy and uploads (final review)
**Status:** accepted (amends "Implementation details: server API" below)
**Context:** The whole-branch review found that `/api/fetch` passed the upstream content type through. Anyone can
archive an HTML page on Wayback and link a user to our `/api/fetch?url=…` for it: the script would run on our origin
and could read a MangaBaka token saved with "remember on this device". `/api/upload` also accepted any Wayback URL
and any bytes starting with `GIF`, so our domain could be used as a 30-day file drop and the store filled.
**Decision:**
- Proxied bodies are always `application/octet-stream` with `nosniff`, `Content-Security-Policy: sandbox;
default-src 'none'` and `Content-Disposition: attachment`. Nothing in our clients reads the content type.
- `/api/fetch` and `/api/upload` require the header `x-mbseed: 1`, which our page and userscript send and a link or
form cannot.
- `/api/upload` rehosts only from a source's image hosts (`Source.imageHosts`) or Wayback's copy of one, and only
images 1–10 000 px a side.
- The origin check compares hosts, so `https://host` passes behind a TLS proxy that Bun sees as `http://host`.
**Alternatives:** A session cookie or the password gate (deferred, spec §4.11); serving the page and the proxy on
different origins (more deployment work).
**Consequences:** A new source must list its image hosts. Clients other than ours must send `x-mbseed: 1`.
## 2026-10-14 — Implementation details: server API, Webtoons fetch forms
**Status:** accepted
**Context:** Decisions made while implementing plan 001 that the spec leaves open.
+4
View File
@@ -18,6 +18,10 @@ Design: [specs/001-webtoons-covers.md](specs/001-webtoons-covers.md).
submitted with its hash on MangaBaka. If it doesn't, "already on MangaBaka" never matches.
- [ ] Userscript: fill MangaBaka's cover form for users without a token (spec §4.9) — needs the logged-in
form's markup and a check that it accepts programmatic input (spec §7.5). The panel offers copy-and-paste instead.
- [ ] Small fixes from the final review (deferred as minor): a malformed `Location` on a proxied redirect gives 500
instead of 502; cap the `/api/upload` JSON body size (`maxRequestBodySize`); after Preview, re-run `draw()`
so the button respects an unconfirmed link mismatch and the counts refresh after a partial upload;
label the "direct" host's URL as "Source URL", not "Rehosted".
- [ ] Mobile layout (the user deferred it, 2026-10-15): the page stacks under 1000px but was only checked at 760px.
## Verify with real services (once possible)