fix(upload): derive R2 key extension from content type, not filename - #77
Merged
royalpinto007 merged 2 commits intoAug 8, 2026
Merged
Conversation
getPresignedUploadUrl trusted the extension off the user-supplied filename even though contentType is already validated against ALLOWED_TYPES in the presign route. A hostile filename could put an arbitrary extension on a public object key. Map the validated content type to an extension instead, and keep the original filename only as sanitized object metadata.
|
Nice work @waterlemonnn, this is merged. Genuinely helpful change for agentpostmortem. If this project helps you out, a star helps others find it too. Happy to see more from you whenever you have the time. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #44
getPresignedUploadUrlwas building the object key fromfilename.split(".").pop(), so the extension came straight from user input even thoughcontentTypeis already validated againstALLOWED_TYPESin the presign route. A request withcontentType: "image/png"andfilename: "x.html"produced a key ending in.html.Extension is now derived from a
contentType -> extensionmap, falling back tobinfor anything unrecognized. The original filename is no longer used for the key at all — kept only as sanitized metadata on the object (Metadata["original-filename"]) in case it's useful later.Tests in
lib/r2/upload.test.tscover each allowed content type, an unrecognized type, a hostile filename that shouldn't be able to influence the key, and the metadata sanitization.npx vitest run(105/105),npx tsc --noEmit, andnpx eslintall clean.