Skip to content

fix: prevent prototype pollution via __proto__ in FormData field names#4

Merged
milamer merged 4 commits into
mainfrom
fix/prototype-pollution-protection
May 8, 2026
Merged

fix: prevent prototype pollution via __proto__ in FormData field names#4
milamer merged 4 commits into
mainfrom
fix/prototype-pollution-protection

Conversation

@milamer
Copy link
Copy Markdown
Owner

@milamer milamer commented May 8, 2026

Summary

parseFormData walked bracket- and dot-notation field names into nested
objects without filtering reserved property keys. A single FormData field
whose name began with __proto__, or contained .__proto__. mid-path,
caused the parser to traverse and assign onto Object.prototype,
polluting the prototype chain of every plain object in the running
process.

import { parseFormData } from 'parse-nested-form-data';
const fd = new FormData();
fd.append('__proto__.polluted', 'yes');
parseFormData(fd);
console.log(({}).polluted); // -> 'yes'  ← before this PR

Fix

handlePathPart now throws a new ForbiddenKeyError (also exported)
when any path segment is __proto__, constructor, or prototype.
The check runs before object/array type branching, so it covers both
a.__proto__.x and any future code path that might route a forbidden
key through the array branch.

The array branch was not exploitable in practice today - the regex
restricts array-index segments to digit characters - but applying the
check uniformly removes the dependence on that invariant. Three new
tests assert that a[__proto__], a[constructor], and a[prototype]
throw and never pollute, so any future regex change that loosens the
input shape will surface the gap loudly rather than silently.

Tests

  • 6 new prototype-pollution tests in src/__tests__/index.ts
  • All 52 tests pass
  • 100% coverage maintained

Release impact

Conventional fix: commit → semantic-release will cut 1.0.1
(patch). Previously-accepted inputs with __proto__ / constructor /
prototype segments now throw ForbiddenKeyError - no legitimate use
of these as form field names is expected to be affected.

Disclosure

Reported responsibly by Mohamed Bassia
(@0xBassia) via private email. A GitHub
Security Advisory with CVE assignment will be published after 1.0.1
lands on npm so that downstream users are notified through Dependabot
and npm audit.

This PR also adds SECURITY.md documenting the disclosure channel for
future reports.

Christian Schurr added 4 commits May 8, 2026 22:00
`parseFormData` walked bracket and dot-notation field names into nested
objects without filtering reserved property keys. A single field whose
name began with `__proto__` (or contained `.__proto__.` mid-path) caused
the parser to traverse and assign onto `Object.prototype`, polluting the
prototype chain of every plain object in the running process.

`handlePathPart` now throws a new `ForbiddenKeyError` (also exported)
when an object-type path segment is `__proto__`, `constructor`, or
`prototype`. The array branch is unaffected today - the regex restricts
array-index segments to digits only - and three new tests pin that
invariant so a future regex change would surface the gap.

Reported responsibly by Mohamed Bassia (https://github.com/0xBassia).
The shipped artifact () uses
nullish coalescing (), which requires Node 14+. Node 12 has been
unable to import the published package since 1.0.0; the matrix entry
was misleading rather than load-bearing.

The CI lint step also fails on Node 12 because
 (transitively required via
) uses the  import scheme, which
was added in Node 14.18 and is unsupported on Node 12.

Node 12 has been EOL since April 2022.
@milamer milamer merged commit 527ad58 into main May 8, 2026
3 checks passed
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

🎉 This PR is included in version 1.0.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

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.

1 participant