loopbackio / loopbackio/loopback-next
Query parameter array parsing broken for >20 items after qs upgrade (CVE fix side effect)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 5.1k
- Forks
- 1.1k
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 27
Description
### Describe the bug
**Current Behavior**
After the qs upgrade in commit https://github.com/loopbackio/loopback-next/commit/1eedfd5057c05ac683a9c9b71c3086b61267ab81 (to address https://github.com/advisories/GHSA-6rw7-vpxm-498p), query parameter arrays with more than 20 items are being converted to objects with numeric keys instead of arrays. This causes LoopBack's parameter validation to fail.
**Expected Behavior**
Query parameters in the format ?ids=1&ids=2&ids=3...&ids=25 should be parsed as an array regardless of the number of items (within reasonable limits).
**Steps to Reproduce**
Create an endpoint with an array parameter:
```
typescript@get('/test')
async test(
@param.array('ids', 'query', { type: 'string' })
ids: string[]
): Promise {
return { ids };
}
```
2. Make a request with 21+ repeated query parameters:
`GET /test?ids=1&ids=2&ids=3...&ids=21&ids=22`
Observe the validation error:
```
json{
"error": {
"statusCode": 400,
"name": "BadRequestError",
"message": "Invalid data [{\"0\":\"1\",\"1\":\"2\",...}] for parameter \"ids\".",
"code": "INVALID_PARAMETER_VALUE"
}
}
```
**Root Cause**
The qs library by default limits array indices to 20 (https://github.com/ljharb/qs#parsing-arrays). When more than 20 items are provided, it converts the array to an object with numeric keys to prevent DoS attacks with extremely large indices like a[999999999].
This is documented behavior in the qs library, and there is an ongoing discussion about this limitation, since it was introduced in a patch version instead of a breaking change version: https://github.com/ljharb/qs/issues/537
**Impact**
This breaks existing APIs that accept more than 20 array items via query parameters.
**Proposed Solution**
Configure qs with a higher arrayLimit option. The default of 20 is too restrictive.
I encourage the LoopBack team to contribute to the discussion in the qs issue thread (https://github.com/ljharb/qs/issues/537) to help get this resolved at the library level, and in the meantime provide a way for LoopBack applications to configure this limit.
**Suggested approaches for LoopBack:**
Make arrayLimit configurable via RestServer options so applications can set their own limits
Increase the default arrayLimit to a more reasonable value (e.g., 100 or 1000)
Document the workaround for applications that need to handle this
----
LoopBack version: 4.x (any version after the qs upgrade)
Node.js version: [applicable to all]
Operating system: [applicable to all]
**Additional Context**
Related CVE fix: https://github.com/advisories/GHSA-6rw7-vpxm-498p
qs documentation: https://github.com/ljharb/qs#parsing-arrays
Ongoing discussion in qs repo: https://github.com/ljharb/qs/issues/537
### Logs
```shell
```
### Additional information
_No response_
### Reproduction
https://github.com/ljharb/qs/issues/537
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing query parsing through the REST server options and the qs arrayLimit behavior described in the issue. Compare the configurable-limit and higher-default approaches, then verify that repeated query parameters beyond 20 remain arrays and pass parameter validation without removing the DoS protection.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100