Skip to content

fix(formDataToJSON): only split keys on bracket and dot notation#11006

Merged
jasonsaayman merged 6 commits into
axios:v1.xfrom
MahinAnowar:fix/formdatatojson-literal-keys
Jun 25, 2026
Merged

fix(formDataToJSON): only split keys on bracket and dot notation#11006
jasonsaayman merged 6 commits into
axios:v1.xfrom
MahinAnowar:fix/formdatatojson-literal-keys

Conversation

@MahinAnowar

@MahinAnowar MahinAnowar commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Closes #5402.

formDataToJSON / formToJSON split a field name like user-name into { user: { name: ... } } instead of keeping the literal key. The same happened for spaces, +, *, &, etc.

The cause is the tokenizer in parsePropPath:

const pattern = /\w+|\[(\w*)]/g;

It matches runs of word characters and bracket groups, silently dropping any non-word separator. So user-name matches user and name and the - is discarded, producing nested objects.

Fix

Match each path segment as any run of characters except ., [ and ]:

const pattern = /[^.[\]]+|\[([^\]]*)]/g;

Bracket notation (foo[bar][baz], foo[], foo[0]) and dot notation (foo.bar) still split exactly as before — only previously-dropped separators (-, space, +, *, &) are now kept as literal key characters. The existing []-empty handling and the depth guard are unchanged, and the prototype-pollution protection (which relies on dot-splitting of __proto__.x / constructor.prototype.y) is preserved.

Tests

Added two cases to tests/unit/helpers/formDataToJSON.test.js: one asserting user-name, first name, a+b stay literal keys, and one asserting bracket/dot notation still nests (foo[bar-baz]{ foo: { 'bar-baz': ... } }). Verified both fail on the old regex and pass with the fix; the full formDataToJSON unit suite (13 tests) is green and ESLint is clean.

🏄


Summary by cubic

Fixes formDataToJSON/formToJSON so only dot and bracket notation split keys; hyphens, spaces, and symbols stay literal. Also makes path parsing linear and treats . and [ inside brackets as separators. Closes #5402.

Description

  • Replaced path tokenizer with /[^.[\]]+|\[([^.[\]]*)]/g.
  • Only . and [] split keys; -, spaces, +, *, &, etc., stay literal.
  • Inside brackets, . and [ separate segments (e.g., foo[bar.baz]{ foo: { bar: { baz } } }).
  • Fail-fast on unmatched [ for linear-time parsing on malformed input.
  • Depth guard and prototype-pollution protections unchanged.

Docs

  • Update /docs/ to state that only . and [] split, with examples for literal keys (e.g., user-name) and nested cases (user.name, user[name]).

Testing

  • Added tests for literal keys (user-name, first name, a+b), dot/bracket nesting with literals, separators inside brackets, and linear-time parsing on long malformed input.
  • All existing tests pass. No additional tests needed.

Semantic version impact

  • Patch: bug fix to key parsing. Output may differ if code relied on the old incorrect splitting.

Written for commit c3b3461. Summary will update on new commits.

Review in cubic

parsePropPath tokenized field names with /\w+|\[(\w*)]/g, which silently
dropped any non-word separator. A key like `user-name` was split into
`{user:{name}}` instead of staying a literal key; the same happened for
spaces, `+`, `*` and `&`. Match each segment as any run of characters
except `.`, `[` and `]` so only bracket and dot notation split.
Comment thread lib/helpers/formDataToJSON.js Outdated
@jasonsaayman jasonsaayman added status::changes-requested A reviewer requested changes to the PR commit::fix The PR is related to a bugfix labels Jun 18, 2026
Address review feedback on the property-path tokenizer:

- Exclude `[` and `.` from the bracket capture (`\[([^.[\]]*)]`) so the match
  fails fast at the next `[` instead of being able to rescan to the end of the
  string from every unmatched `[`. Parsing is now linear in the name length
  (the previous pattern was quadratic on long malformed bracket input).
- Bracket contents now use the same character class as dot-notation segments
  (excluding `.`, `[` and `]`), so the grammar matches the documented comment.
  `.`, `[` and `]` keep their existing meaning, e.g. `foo[bar.baz]` still nests
  as `['foo','bar','baz']`; only previously-dropped separators stay literal.

Add regression tests for a long malformed bracket name (linear-time parse) and
for `.`/`[` appearing inside bracket groups.
…literal-keys

# Conflicts:
#	PRE_RELEASE_CHANGELOG.md
@MahinAnowar

Copy link
Copy Markdown
Contributor Author

Thanks for the review and the assign, @jasonsaayman — both points addressed.

Linearity: the bracket group now excludes [ (and .): /[^.[\]]+|\[([^.[\]]*)]/g. Since the capture stops at the next [, it fails fast instead of being able to rescan to the end of the string from every unmatched [; there are no nested quantifiers and the scans are bounded by the next [/], so parsing is linear in the name length. Quick local timing of the old vs new pattern on 'a' + '['.repeat(N):

N old new
2k 1.7ms 0.02ms
8k 23ms 0.02ms
16k 90ms 0.03ms

(old ~4× per doubling = O(n²); new flat). Added a regression test that parses 'a' + '['.repeat(100000).

Grammar: bracket contents now use the same character class as dot-notation segments (excluding ., [, ]), so the comment and the grammar agree — a key never contains ., [ or ] whether written in dot notation or inside brackets. ., [ and ] keep their original meaning (foo[bar.baz] still nests as ['foo','bar','baz'], matching the pre-PR \w+ behavior); only the previously-dropped separators (-, space, +, …) now stay literal. Added coverage for foo[bar.baz] and foo[a[b].

Also merged the latest v1.x to clear the changelog conflict. 🏄

@jasonsaayman jasonsaayman removed the status::changes-requested A reviewer requested changes to the PR label Jun 25, 2026
…teral-keys

# Conflicts:
#	PRE_RELEASE_DOCS.md
@jasonsaayman
jasonsaayman merged commit 44de1e4 into axios:v1.x Jun 25, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commit::fix The PR is related to a bugfix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

formToJSON should not separate properties with minus character

2 participants