Skip to content

Conversation

@techouse
Copy link
Contributor

@techouse techouse commented Aug 26, 2025

Problem

qs splits bracket groups with a regex that forbids [] inside a group:

var child = /(\[[^[][^\]]*])/g; // original: /(\[[^[\]]*])/g

This causes keys like search[withbracket[]] to split into "[withbracket]", then "[]", leaving the inner [] detached from the literal segment. Downstream, it can no longer be treated as a single literal key.

Repro

// expected:
qs.parse('search[withbracket[]]=foobar');
// => { search: { 'withbracket[]': 'foobar' } }

// actual (today):
// inner [] is split out, so the key can’t be treated as one literal segment

Fix approach

Backport splitKeyIntoSegments from qs_dart and replace the regex child matcher with a balanced bracket splitter that treats [] inside a bracket group as literal content (only outer [] act as array push). This preserves withbracket[] as a single segment.

Notes

  • Does not change existing semantics for [] at the outermost level (array push).
  • Honors existing options: allowDots, depth, strictDepth, allowPrototypes, etc.

Test snippet

t.test('parses keys with literal [] inside a bracket group (#493)', function (st) {
    // A bracket pair inside a bracket group should be treated literally as part of the key
    st.deepEqual(
        qs.parse('search[withbracket[]]=foobar'),
        { search: { 'withbracket[]': 'foobar' } },
        'treats inner [] literally when inside a bracket group'
    );

    // Single-level variant
    st.deepEqual(
        qs.parse('a[b[]]=c'),
        { a: { 'b[]': 'c' } },
        'keeps "b[]" as a literal key'
    );

    // Nested with an array push on the outer level
    st.deepEqual(
        qs.parse('list[][x[]]=y'),
        { list: [{ 'x[]': 'y' }] },
        'preserves inner [] while still treating outer [] as array push'
    );

    st.end();
});

Fixes #493

@techouse techouse changed the title Fix incorrect parsing of nested params with closing square bracket ] in property name Fix parsing of keys containing literal [] inside bracket groups Aug 27, 2025
@techouse techouse changed the title Fix parsing of keys containing literal [] inside bracket groups Fix parsing of keys containing literal [] inside bracket groups Aug 27, 2025
@techouse
Copy link
Contributor Author

techouse commented Aug 28, 2025

@ljharb, it seems some of the tests fail because the runners are unable to download a specific Node version, i.e.

curl: (56) OpenSSL SSL_read: Connection reset by peer, errno 104
download from https://nodejs.org/dist/v5.3.0/node-v5.3.0-linux-x64.tar.xz failed
grep: /home/runner/.nvm/.cache/bin/node-v5.3.0-linux-x64/node-v5.3.0-linux-x64.tar.xz: No such file or directory
curl: (92) HTTP/2 stream 0 was not closed cleanly: INTERNAL_ERROR (err 2)
download from https://nodejs.org/dist/v13.13.0/node-v13.13.0-linux-x64.tar.xz failed
grep: /home/runner/.nvm/.cache/bin/node-v13.13.0-linux-x64/node-v13.13.0-linux-x64.tar.xz: No such file or directory

@techouse
Copy link
Contributor Author

@ljharb, have you had a chance to look at this yet? 😊

@ljharb ljharb requested a review from Copilot November 4, 2025 06:50
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements bracket balancing in the query string parser to correctly handle literal [] characters inside bracket groups. Previously, bracket pairs within a bracket group could cause parsing issues. The new implementation uses a level counter to properly balance opening and closing brackets, treating inner bracket pairs as literal parts of keys.

Key Changes

  • Refactored parseKeys function to extract bracket parsing logic into a new splitKeyIntoSegments helper function
  • Implemented bracket-balancing algorithm that uses a level counter to track nested brackets
  • Added test cases validating that [] inside bracket groups are treated as literal key characters

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
lib/parse.js Replaced regex-based bracket parsing with bracket-balancing algorithm that correctly handles nested [] within bracket groups
test/parse.js Added test case covering three scenarios: basic nested brackets, single-level variant, and array push with nested brackets

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Owner

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty hard to review. Could you perhaps make a refactoring commit that makes the majority of the changes, and then a fix commit with the test and the thing that makes the test pass?

@techouse techouse force-pushed the fix/qs-493-parse-nested-brackets branch from 243b1f5 to bb1b7c3 Compare November 4, 2025 08:41
@techouse
Copy link
Contributor Author

techouse commented Nov 4, 2025

This is pretty hard to review. Could you perhaps make a refactoring commit that makes the majority of the changes, and then a fix commit with the test and the thing that makes the test pass?

@ljharb done. 😇

I have split the parse key processing into a refactor-only commit followed by the nested-bracket fix plus its regression tests so it now has the structure you have asked for.

@techouse techouse requested a review from Copilot November 4, 2025 08:45
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect parsing of nested params with closing square bracket ] in property name

2 participants