Repository navigation
fix(core): honor quoted-strings when parsing Content-Disposition filename - #118
Conversation
…name getFilenameFromContentDisposition matched `filename*=` / `filename=` with quote-unaware regexes, so a `;filename*=...` inside the quoted legacy filename was taken as the real parameter. A File named "invoice;filename*=utf-8''invoice.exe;.pdf" arrived as "invoice.exe" on fetch, node, peer and aws-lambda receivers. Parse parameters with a quote-aware tokenizer (first occurrence wins, unterminated quoted-strings run to the end), and drop ';' and '=' from the generated fallback filename so receivers still on the old parser cannot be misled either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0119s8HvkwUjgBK9EGP6gRWs
Replace matchAll with a sticky regex exec loop and let a leading [\s;]* absorb separator runs in one match. Typical headers parse in about half the time, and ';'-flooded headers go from ~2ms to ~16us. Also trim the comments and drop test cases that duplicated existing ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0119s8HvkwUjgBK9EGP6gRWs
Move the remaining inline regex literals in core utils into named module-level constants, matching the rest of the repo. The global ones are only used with replace(), which resets lastIndex, and the others are stateless, so sharing them is safe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0119s8HvkwUjgBK9EGP6gRWs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0119s8HvkwUjgBK9EGP6gRWs
@standard-server/aws-lambda
@standard-server/core
@standard-server/fastify
@standard-server/fetch
@standard-server/node
@standard-server/peer
@standard-server/shared
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
ℹ️ No critical issues — the parser rewrite is sound and well tested. One deliberate output change is worth confirming.
Reviewed changes — replaced the ad-hoc regex extraction in getFilenameFromContentDisposition with a single sticky-regex parameter scanner; initial review of all 4 commits.
- New
getContentDispositionParam()scanner (packages/core/src/utils.ts:30): extracts a named parameter while respecting quoted-string boundaries and escaped pairs, so a;/filename*=embedded inside a quoted value can no longer be read as a real parameter. ThelastIndexloop always advances (proved by fuzzing), so the non-null assertion is safe. getFilenameFromContentDisposition()rewrite (:46): prefersfilename*(charset/language handling unchanged) and otherwise falls back to the plainfilename, both via the scanner. Closes the old injection wherefilename="report;filename*=utf-8''evil.exe"returnedevil.exe.generateContentDisposition()fallback escaping change (:4,:19): the ASCIIfilename="..."fallback now also replaces;and=with_;filename*still preserves them.- Tests (
utils.test.ts): injection, unterminated-quote, whitespace/case, quoted-filename*, and generator round-trip cases.
Verification: affected suites pass (400 tests across core/fetch/node/fastify/aws-lambda) and core tsc -b is clean. I fuzzed the scanner with 200k random strings (no hang, no zero-width match, no exec null), ran 20k exact round-trips (all clean except the documented lone-surrogate normalization), and confirmed no ReDoS on 200k-char adversarial inputs (all <7ms).
DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏
…ements Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0119s8HvkwUjgBK9EGP6gRWs

getFilenameFromContentDispositionnow reads parameters with a quote-aware scanner. It used to findfilename*=andfilename=with regexes that ignored quoted-strings, so a;filename*=...inside the quoted fallback written bygenerateContentDispositionwas taken as the real parameter. AFilenamedinvoice;filename*=utf-8''invoice.exe;.pdfsent throughtoFetchBodyarrived asinvoice.exeon the fetch, node, peer and aws-lambda receivers, andreport;filename*=utf-8''evil.exearrived asevil.exe".Fixes
;,=,'or*now round-trip exactly throughgenerateContentDispositionandgetFilenameFromContentDisposition.generateContentDispositionreplaces;and=with_in thefilename="..."fallback, so receivers still on the released parser can't be misled either. The exact name still travels infilename*.Notes for reviewers
filename*, which already see_for non-ASCII characters. The core README now says so.filename*="utf-8''..."is accepted, whitespace around=is allowed, and an unquotedfilename=value keeps any"it contains. An unterminated quoted-string now runs to the end of the header, so nothing after it is read as a parameter.execloop and stays linear on hostile input: a 16 KB header parses in under 1 ms. All regexes are hoisted to module scope.Testing
;and parameters inside quoted values, unterminated quoted-strings, quotedfilename*, whitespace and case, plus a round-trip test over tricky filenames.🤖 Generated with Claude Code
https://claude.ai/code/session_0119s8HvkwUjgBK9EGP6gRWs