fix(cli): stop rejecting generic files on upload, fix attachment markdown - #3641
fix(cli): stop rejecting generic files on upload, fix attachment markdown#3641kcao-gss wants to merge 1 commit into
Conversation
|
PR #3641 review — first pass done. Diagnosis and approach are right. One must-fix before merge, two nits, and the defense-in-depth decision you asked me for. Defense-in-depth trade: accept it. Do not port Desktop's deny-list into the CLI.Not on maintainability grounds alone — the deleted gate was not buying security in the first place, and neither would a CLI deny-list.
So the trade is real but the thing being traded away was ornamental. @chrollo your ruling stands — no re-route to Feitan needed. Must fix: the 50 → 100 MB cap change is wrong for images, and the comment asserting otherwise is factually false
The relay does not have that shape. Failure case: That is strictly worse than what shipped before, in the exact lines the PR touched, and it contradicts the invariant the new comment claims. Fix is small: /// Relay per-category caps — must match `buzz_media::config::MediaConfig`
/// defaults (`max_gif_bytes`, `max_image_bytes`, `max_video_bytes`,
/// `max_file_bytes`).
const MAX_GIF_BYTES: u64 = 10 * 1024 * 1024;
const MAX_IMAGE_BYTES: u64 = 50 * 1024 * 1024;
const MAX_VIDEO_BYTES: u64 = 500 * 1024 * 1024;
const MAX_FILE_BYTES: u64 = 100 * 1024 * 1024;
let max = if mime.starts_with("video/") {
MAX_VIDEO_BYTES
} else if mime == "image/gif" {
MAX_GIF_BYTES
} else if mime.starts_with("image/") {
MAX_IMAGE_BYTES
} else {
MAX_FILE_BYTES
};Raising the generic-file cap to 100 MB was correct and is the part worth keeping. Collapsing images into it was not. Nit 1: unescaped filename in the markdown link text
Nit 2: the 100 MB test is expensive for
|
…down buzz upload file client-side-gated every upload through a five-entry ALLOWED_MIMES allowlist (image/jpeg, image/png, image/gif, image/webp, video/mp4). Anything else — including application/octet-stream, which is what `infer` returns for un-sniffable JSON/plain-text content, and PDF, which infer detects correctly but which was never allowlisted — was rejected before the request ever left the process. The relay's generic-file upload path already accepts this content via a deny-list validator (buzz_media::validation), and Desktop's uploader mirrors that deny-list client-side. Rather than adding a second copy of that policy to the CLI (a fourth copy alongside relay/desktop/mobile), drop the CLI-side gate entirely — the relay is the sole enforcement point. Also: - Raise the non-video size cap from 50 MB to 100 MB to match the relay's generic-file default_max_file_bytes (images were previously capped below what the relay allows for non-video content). - Fix `buzz messages send --file` to render non-image/non-video attachments as a plain markdown link instead of ``, which was unreachable while the allowlist blocked those uploads but would have rendered as a broken image once the gate was removed. - Document a known gap: the /media/upload legacy fallback still hard-rejects non-image/video content server-side, so a generic file against an old relay lacking /upload will now fail there with a confusing DisallowedContentType instead of a clear version error. Follow-up, not fixed here. Verified live against the dev relay (upload of JSON/text/PDF all now succeed with the correct detected MIME; message with a JSON attachment renders as a link). Signed-off-by: Kyler Cao <kcao@gssmail.com>
e002f56 to
917d5fe
Compare
Root cause
buzz upload filegated every upload client-side through a five-entryALLOWED_MIMESallowlist (image/jpeg,image/png,image/gif,image/webp,video/mp4). Anything else — includingapplication/octet-stream(whatinferreturns for un-sniffable JSON/plain-text content) andapplication/pdf(detected correctly byinfer, but never allowlisted) — was rejected before the request left the process.The relay's generic-file
/uploadpath already accepts this content via a deny-list validator (buzz_media::validation), and Desktop's uploader (desktop/src-tauri/src/commands/media.rs) mirrors that deny-list client-side. The CLI's allowlist was the only client in this codebase enforcing a narrower policy than the relay actually allows.Repro (before this PR, against the live dev relay):
Fix
ALLOWED_MIMESallowlist gate entirely rather than porting Desktop's deny-list into the CLI. The relay/Desktop/mobile already each carry a copy of that policy; a fourth CLI-side copy would drift. The relay is the sole enforcement point — MIME detection is kept (it still feedsContent-Type, the Blossom signature, and the descriptor).MAX_FILE_BYTES) to match the relay'sdefault_max_file_bytes— the old cap under-allowed generic files relative to what the relay accepts.buzz messages send --file: it rendered every non-video attachment as, which was unreachable while the allowlist blocked non-image uploads but would post a broken inline image for JSON/text/PDF attachments once the gate was removed. Now branches three ways: image →, video →, everything else → a plain[filename](url)link./media/uploadlegacy fallback still hard-rejects non-image/video content server-side, so a generic file against an old relay lacking/uploadwill 404 there, fall back to/media/upload, and fail with a confusingDisallowedContentTypeinstead of a clear version error. Follow-up.Verification
cargo test -p buzz-cli: 262 passed, 0 failed.application/octet-stream,application/octet-stream,application/pdfrespectively).buzz messages send --file test.jsonposts successfully with the JSON attachment rendered as a plain link, not a broken image.Trade-off carried to review
This removes a defense-in-depth client-side gate. Upload safety now rests entirely on the relay's deny-list validator plus
serve_inlinenever inlining non-media content. Both exist today and are tested, but worth a conscious sign-off rather than silently accepting.Mobile (
mobile/lib/shared/relay/media_upload.dart:363) has the same class of allowlist gate — separate follow-up, not in scope here.