Skip to content

fix: decode WHATWG-escaped characters in file originalname - #1421

Merged
UlisesGascon merged 2 commits into
expressjs:mainfrom
webdevelopersrinu:fix/originalname-decoding
Aug 25, 2026
Merged

UlisesGascon merged 2 commits into
expressjs:mainfrom
webdevelopersrinu:fix/originalname-decoding

Conversation

@webdevelopersrinu

Copy link
Copy Markdown
Contributor

What & why

Fixes #836.

Browsers escape the bytes 0x0A (LF), 0x0D (CR) and 0x22 (") as
%0A, %0D and %22 when serialising a filename in a
multipart/form-data body, per the WHATWG HTML spec. Busboy leaves
them escaped, so a file uploaded as file".ext was exposed on
req.file.originalname as file%22.ext.

Change

  • Decode exactly those three sequences when building the file object in
    lib/make-middleware.js.
  • Only these three are decoded on purpose — a bare % is never escaped
    by the spec, so a full decodeURIComponent would corrupt legitimate names
    like 50%off.pdf. A dedicated test guards this.

Reproduction (before)

Uploading a file named file".ext →

req.file.originalname === 'file%22.ext' // ❌ before
req.file.originalname === 'file".ext' // ✅ after

Tests

Adds test/filename-decoding.js covering:

  • escaped double quote (%22)
  • escaped CR + LF (%0D, %0A)
  • an unescaped filename (unchanged)
  • preservation of a literal percent sign (50%off.ext)

All pass; standard lint is clean. Targeting the 2.x line as discussed on
the issue.

Browsers escape the bytes 0x0A (LF), 0x0D (CR) and 0x22 (") as %0A, %0D
and %22 when serialising filenames in a multipart/form-data body, per the
WHATWG HTML spec. Busboy leaves them escaped, so a file named `file".ext`
was exposed as `file%22.ext` on `req.file.originalname`.

Reverse exactly those three sequences when building the file object. Only
these are decoded on purpose: a bare `%` is never escaped by the spec, so
a full decodeURIComponent would corrupt legitimate names like `50%.pdf`.

Adds regression tests covering the escaped quote, CR/LF, an unescaped
name, and preservation of a literal percent sign.

Fixes expressjs#836
@UlisesGascon UlisesGascon self-assigned this Aug 25, 2026
@UlisesGascon UlisesGascon changed the title fix: decode WHATWG-escaped characters in file originalname (#836) fix: decode WHATWG-escaped characters in file originalname Aug 25, 2026
@UlisesGascon
UlisesGascon merged commit 5d39be7 into expressjs:main Aug 25, 2026
28 of 40 checks passed
@UlisesGascon UlisesGascon mentioned this pull request Aug 25, 2026
YasharF added a commit to sahat/hackathon-starter that referenced this pull request Aug 28, 2026
config/morgan.js: colors moved out of the token and into the format string, where morgan doesn't touch them

Dependency Release Notes:

Multer v2.3.0
- Fix [CVE-2026-77078](https://www.cve.org/CVERecord?id=CVE-2026-77078) ([GHSA-wc9g-mqfw-jrwm](GHSA-wc9g-mqfw-jrwm))
- Fix [CVE-2026-77037](https://www.cve.org/CVERecord?id=CVE-2026-77037) ([GHSA-qfvm-cv95-jqjf](GHSA-qfvm-cv95-jqjf))
- Fix [CVE-2026-77063](https://www.cve.org/CVERecord?id=CVE-2026-77063) ([GHSA-qvfw-j98x-7q72](GHSA-qvfw-j98x-7q72))
- Fix [CVE-2026-82333](https://www.cve.org/CVERecord?id=CVE-2026-82333) ([GHSA-535w-7cp7-47q4](GHSA-535w-7cp7-47q4))
- Add `MulterError` codes `INVALID_FIELD_NAME` and `STREAM_DESTROYED`
- Add opt-in `limits.fieldArrayIndexLimit` to bound numeric array indexes in field names ([#1438](expressjs/multer#1438))
- Accept files whose size is exactly `limits.fileSize` ([#1407](expressjs/multer#1407))
- Preserve the caller's async context (`AsyncLocalStorage`) when calling `next()` ([#1124](expressjs/multer#1124))
- Decode WHATWG-escaped characters (`%0A`, `%0D`, `%22`) in `file.originalname` ([#1421](expressjs/multer#1421))
- Do not crash when `fileFilter` invokes its callback more than once ([#1427](expressjs/multer#1427))
- Use a fallback message for `MulterError` codes without a mapping ([#1448](expressjs/multer#1448))
- Docs: clarify `preservePath` and `parts`, use `crypto.randomBytes` in the `DiskStorage` example ([#1414](expressjs/multer#1414), [#1430](expressjs/multer#1430), [#1436](expressjs/multer#1436))
- Docs: add Indonesian, Japanese and Tamil translations and refresh all translations from the current README ([#1431](expressjs/multer#1431), [#1354](expressjs/multer#1354), [#1462](expressjs/multer#1462))
- Internal: run the test suite on Windows ([#1334](expressjs/multer#1334))

Morgan 1.12.0
- Security fix for [CVE-2026-15603](https://www.cve.org/CVERecord?id=CVE-2026-15603)([GHSA-jxfw-x594-9x9m](https://github.com/expressjs/morgan/security/advisories/GHSA-jxfw-x594-9x9m))
- Allow format functions to return objects for streams in `objectMode`
- Respect the `NO_COLOR` environment variable in the `dev` format
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

originalname is not properly decoded

2 participants