fix: cap the multipart metadata part instead of buffering it unbounded

The `metadata` JSON part in the manga-create and chapter-upload handlers was
read with `Field::bytes()`, buffering the whole part before any check — a client
could send a multi-hundred-MB metadata blob (up to the 200 MiB request limit) as
one allocation. Image parts were already streamed with a cap; metadata was the
last unbounded read.

Cap it at a shared upload::MAX_METADATA_BYTES (64 KiB) via the read_capped
streaming loop (413 on overflow), and remove the now-unused read_field_bytes
helper so the unbounded path can't return.

Tests: manga + chapter oversized-metadata parts are rejected with 413.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-13 20:10:05 +02:00
parent 253d46c7e5
commit 1b7b8a3038
8 changed files with 71 additions and 12 deletions

View File

@@ -37,6 +37,15 @@ pub struct StagedImage {
/// staged page out of this prefix.
pub const STAGING_PREFIX: &str = "staging";
/// Upper bound on a multipart `metadata` JSON part, enforced as bytes arrive
/// (via [`read_capped`]). Manga/chapter metadata is title + a few short lists +
/// a description — kilobytes at most. Without this, `metadata` was the one
/// remaining part read with the unbounded `Field::bytes()`, letting a client
/// buffer up to the whole 200 MiB request body in memory as a single JSON blob
/// before any validation ran. 64 KiB is generous headroom over any legitimate
/// payload while keeping the worst case tiny.
pub const MAX_METADATA_BYTES: usize = 64 * 1024;
/// Read one multipart image part and write it straight to a staging key,
/// returning only its metadata. The per-file byte cap is enforced as bytes
/// arrive, so an oversized part is rejected (413) without being fully