fix(file-s3): preserve directory portion of filename in object key#16010
fix(file-s3): preserve directory portion of filename in object key#16010Tusharkhadde wants to merge 18 commits into
Conversation
🦋 Changeset detectedLatest commit: f76e6ec The changes in this PR will be included in the next version bump. This PR includes changesets to release 79 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Fixes the handling of filenames in the file-s3 module by ensuring the directory portion is preserved in the object key.
|
Thanks for the contribution! A few items need to be addressed before this can move forward: Correct, minimal fix that preserves the directory segment of a filename in the S3 object key, matching linked issue #16008; changeset is present and well-formatted, and the returned key keeps delete/download roundtrips working. Two gaps before approval: the PR description omits the template's Testing section, and no test was added even though this provider already has unit tests for key/URL generation.
Triggered by: new PR opened |
|
Thanks for the contribution! We need more information before reviewing this further. Re-review: the prior request for a unit test is resolved — the diff now adds dir-preservation and no-dir tests for both upload() and getUploadStream(). The code fix is correct and minimal, matching linked issue #16008. One item from the previous review remains: the PR description still omits the template's Testing section. Non-blocking nit: two duplicate changeset files bump @medusajs/file-s3 for the same fix; consider keeping one. Triggered by: new commit pushed |
|
Thanks for the contribution! We need more information before reviewing this further. Re-review: the code fix is correct and minimal, preserving the directory segment of the filename in the S3 object key for both upload() and getUploadStream(), matching linked issue #16008. The prior request for unit tests is resolved (dir-preservation and no-dir cases added), and the duplicate changeset nit is resolved (one changeset remains). One item still outstanding: the PR description does not include the template's Testing section. No blocking bugs, security, or performance issues found. Triggered by: new commit pushed |
|
Thanks for the contribution! Initial automated review looks good. Correct, minimal fix preserving the filename's directory segment in the S3 object key for both upload() and getUploadStream(), matching linked issue #16008. All prior review items are resolved: unit tests for dir-preservation and no-dir cases were added, the duplicate changeset was removed, and the PR description now includes the Testing section. No bugs, security, or performance concerns found; S3 keys are a flat literal namespace so no path-traversal risk. Triggered by: PR description updated |
shahednasser
left a comment
There was a problem hiding this comment.
Thank you for your contribution @Tusharkhadde . There are a couple of issues to point out with this approach:
- This approach doesn't handle cases where the path starts with
/like/vendor/logo.png. The path would bepublic//vendor/logo-{suffix}.png - The approach doesn't handle cases where the path starts with
.like./vendor/logo.png. the path would include a/./segment - Similar to the above, the directory path isn't sanitized, which could allow for path traversal like
../secret/logo.png. It must be sanitized to ensure there are no path traversals and remove those
… prefixes, and path traversals
|
Thanks for the detailed review @shahednasser ! I've addressed the path sanitization concerns by handling leading slashes, relative path segments ( |
|
@medusajs-bot review |
|
@medusajs-bot review |
|
@medusajs-bot review |
|
A suggestion for a small additional change: A pure traversal filename like '../..' will reslt in '' after sanitization. This will leave just the prefix as the filekey. Not sure what the implications of that are. Could it overwrite? In any case, probably best to outright reject if filename is '' after sanitization. |
|
@medusajs-bot review |
|
Thanks for the contribution! A few items need to be addressed before this can move forward: The PR proposes to preserve the directory segment of file.filename when constructing the S3 object key. The feature intent is sound, but the implementation passes parsedFilename.dir (user-controlled) into the S3 Key without sanitizing '..' or leading '/' segments, introducing a path traversal vulnerability. An attacker with upload access could escape the configured prefix and write objects to unintended S3 paths. A changeset for the file-s3 package should also be included if not already present in the diff.
Triggered by: manual review request via comment |
|
Thanks for the feedback, @pevey! I've made the requested changes to handle the edge cases you mentioned. If a user-provided filename becomes empty after sanitization (for example, I added this validation to all three upload methods:
I also added unit tests for these invalid filename cases across all three methods, and everything is passing locally. Please let me know if you notice anything else that should be improved. Thanks again for the review! |
Fixes #16008
Summary
The S3 file provider was ignoring the directory portion of the supplied filename when constructing the object key. It only used the parsed
nameandext, dropping any path structure (e.g.vendor_123/logo.pngbecamelogo-<ulid>.png).This change updates the
uploadandgetUploadStreammethods to preserve the directory segment when generating the object key, resulting in keys likevendor_123/logo-<ulid>.png.Changes
parsedFilename.dir(with a trailing slash) when constructing the S3 object key.uploadandgetUploadStream.packages/modules/providers/file-s3/src/services/s3-file.tsBehavior
Before
After
Testing
packages/modules/providers/file-s3/src/services/__tests__/s3-file-url-encoding.spec.ts:upload()withfilename: "vendor_123/logo.png"→ key matches^public\/vendor_123\/logo-[^/]+\.png$.upload()withfilename: "logo.png"(no directory) → key matches^public\/logo-[^/]+\.png$(unchanged).getUploadStream()withfilename: "vendor_123/logo.png"→ key matches^public\/vendor_123\/logo-[^/]+\.png$.yarn workspace @medusajs/file-s3 jest src/services/__tests__/s3-file-url-encoding.spec.ts— all 7 tests pass.Closes #16008