fix(common): escape special chars in multipart form filenames#1037
fix(common): escape special chars in multipart form filenames#1037Wang-Yeah623 wants to merge 3 commits into
Conversation
MultipartWriter.CreateFormFile concatenated the fieldname and filename
into the Content-Disposition header without escaping, so a filename
containing a double-quote, backslash, CR, or LF produced a malformed
header. For example, uploading `report "draft" v2.pdf` via
`task +upload-attachment` made the server see `filename="report "`
(truncated at the first internal quote) and drop the rest.
Drop the custom override and let CreateFormFile be promoted from the
embedded *multipart.Writer, which applies the stdlib's quoteEscaper
(backslash and double-quote get a backslash prefix; CR and LF get
percent-encoded). The Content-Type ("application/octet-stream") and
the wrapper API are unchanged, so the existing `task +upload-attachment`
call site is unaffected -- filenames with special characters just now
round-trip correctly.
Add helpers_test.go covering plain, quoted, backslashed, mixed, and
unicode filenames. The test asserts both the on-wire encoding and a
round-trip through mime.ParseMediaType (bypassing Part.FileName, whose
filepath.Base is platform-dependent for backslash on Windows).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe custom CreateFormFile method was removed from MultipartWriter; the type now relies on the embedded *multipart.Writer's promoted CreateFormFile. Documentation was updated and two tests were added to validate filename escaping and part Content-Type. ChangesMultipart Writer Refactoring
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
shortcuts/common/helpers_test.go (1)
30-40: ⚡ Quick winAdd CR/LF filename cases to lock down the full escaping regression surface.
Line 30-40 covers quotes/backslashes/unicode, but not
\r/\n, which are part of the malformed-header class this PR addresses. Adding explicit CR/LF cases would make the regression suite complete.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shortcuts/common/helpers_test.go` around lines 30 - 40, Add explicit CR/LF test cases to the existing cases table in helpers_test.go: extend the cases slice (the same variable shown) with entries for CR, LF, and CRLF filenames such as {"with CR", "file\rname.pdf", "file\\rname.pdf"}, {"with LF", "file\nname.pdf", "file\\nname.pdf"}, and {"with CRLF", "file\r\nname.pdf", "file\\r\\nname.pdf"} so the test asserts the header-escaping logic handles carriage return and newline correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@shortcuts/common/helpers_test.go`:
- Around line 30-40: Add explicit CR/LF test cases to the existing cases table
in helpers_test.go: extend the cases slice (the same variable shown) with
entries for CR, LF, and CRLF filenames such as {"with CR", "file\rname.pdf",
"file\\rname.pdf"}, {"with LF", "file\nname.pdf", "file\\nname.pdf"}, and {"with
CRLF", "file\r\nname.pdf", "file\\r\\nname.pdf"} so the test asserts the
header-escaping logic handles carriage return and newline correctly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: fd29b4f4-1c05-4ca3-990f-9cb56934959c
📒 Files selected for processing (2)
shortcuts/common/helpers.goshortcuts/common/helpers_test.go
Per code-review feedback, extend the helpers_test.go cases table with
CR, LF, and CRLF filenames so the test exercises both legs of the
stdlib's quoteEscaper:
- backslash and double-quote use backslash escaping (quoted-pair);
these round-trip exactly through mime.ParseMediaType.
- CR and LF use percent encoding to prevent header injection; the
MIME parser does not decode percent escapes, so the read-side
filename param contains literal "%0D"/"%0A".
The cases table grows a wantParsed column so each case can declare its
expected post-parse value (same as filename for backslash-escaped chars,
percent-encoded for CR/LF).
Two follow-up tweaks suggested by a re-read of the PR:
- helpers.go: stop naming the stdlib's internal `quoteEscaper` in the
doc comment. Describe the observable behaviour ("escapes special
characters") instead, so the comment stays valid if the stdlib ever
renames or reimplements its escaping.
- helpers_test.go: rename the vague `with both` case to
`backslash and quote`; split the table-driven cases into three
visually-separated groups (happy path / backslash escaping /
percent encoding) so it is obvious why two cases have a different
wantParsed than filename.
No behaviour change; tests still pass 8/8.
Summary
MultipartWriter.CreateFormFileconcatenated the fieldname and filename into theContent-Dispositionheader without escaping, so any filename containing",\, CR, or LF produced a malformed header. The only consumer today istask +upload-attachment, which receives the filename fromfilepath.Baseon the user's path — so e.g. uploadingreport "draft" v2.pdfon macOS/Linux makes the server seefilename="report "and drop the rest.Changes
CreateFormFileoverride inshortcuts/common/helpers.go. The wrapper still embeds*multipart.Writer, somw.CreateFormFilenow resolves to the stdlib version, which appliesquoteEscaper(backslash →\\, double-quote →\", CR →%0D, LF →%0A). Behavior is otherwise identical (sameapplication/octet-streamContent-Type, same signature).shortcuts/common/helpers_test.gocovering plain, quoted, backslashed, mixed-special, and unicode filenames. The tests both inspect the on-wire bytes for the escaped form and round-trip the header throughmime.ParseMediaTypeto confirm the server-side filename matches the input.Test Plan
go test ./shortcuts/common/ -run TestMultipartWriter— passes (5/5 sub-cases + Content-Type check)main:with_double_quoteandwith_bothblow up withmime: invalid media parameter;with_backslashfails the on-wire byte assertiongo test ./shortcuts/task/— passes (only consumer ofMultipartWriter)go vet ./...,gofmt -l,go build ./...— all cleanlark-cli task +upload-attachmentwith a filename containing"(covered by unit tests; live run needs bot creds)Related Issues
Summary by CodeRabbit
Refactor
Tests