Commit Graph
1 Commits
Author SHA1 Message Date
Marc-André Bombeck bd64b78e5d feat(documents): upload policy layer and binary content codec
The policy module (src/lib/documents/upload-policy.ts) is the one source of
truth for what the vault accepts and how it serves: Class A (PDF/JPEG/PNG/
WebP/GIF) renders inline; Class B (Office OOXML+CFB, text/CSV/Markdown/RTF,
TIFF, HEIC/HEIF, XML/JSON) is stored verbatim and leaves the origin only as
an opaque attachment download; executables, HTML/XHTML/SVG, and generic
archives are denied. Detection is magic-bytes first — the wire Content-Type
is never trusted; containers (ZIP → OOXML, CFB → legacy Office) and
extension-only text formats gate on the filename plus a no-NUL heuristic.
resolveDocumentLimits() reads the admin-tunable per-file cap (clamped to
the hard 100 MiB ceiling — single-shot GCM and bounded in-memory reads are
load-bearing past that) and the quota (user override ?? instance default).

crypto.ts gains the binary2 codec: encryptBytes/decryptBytes do AES-256-GCM
over raw bytes with a versioned key-id header, skipping the base64 detour
(−33 % at rest, one fewer copy). Same fail-closed discipline as the string
codec. The store dispatches per row on the explicit contentCodec column:
new uploads write binary2, pre-existing base64v1 rows keep decoding
byte-identically, and an unknown codec throws instead of guessing.

Exhaustive unit coverage: every accept class from real magic-byte fixtures,
every deny case (incl. the PDF-with-HTML-head, SVG-as-image, and
ZIP-renamed-docx polyglots), header-matrix source of truth, limit clamping,
codec round-trips both ways, crossed-codec and unknown-codec fail-closed.

Tasks: W1-T3, W1-T4.
2026-07-07 11:04:51 +02:00