chore(deps): update dependency qs to v6.16.0 [security] - #596
Open
renovate[bot] wants to merge 1 commit into
Open
chore(deps): update dependency qs to v6.16.0 [security]#596renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
renovate
Bot
force-pushed
the
renovate/npm-qs-vulnerability
branch
from
September 3, 2026 10:45
0886a41 to
5bea62a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
6.15.3→6.16.0qs: Denial of Service via Attacker Controlled isBuffer
CVE-2026-82417 / GHSA-4mjr-xmp4-gh2g
More information
Details
Summary
qs.stringify()callsutils.isBuffer()on every value it serializes, andutils.isBuffer()invokesobj.constructor.isBuffer(obj)without checking that it is callable. A value whose ownconstructor.isBufferis a non-function makesqscall a non-callable and throwTypeError. Such a value is produced byqs.parseitself from an untrusted query string whenplainObjects: trueorallowPrototypes: trueis set, so a pure-qsparse→stringifyround-trip — noJSON.parse— turns an unauthenticated query string into an uncaught throw.An attacker-controlled
parseinput reaches the host application's availability asset — viaqs's own recommendedplainObjectsmitigation — and triggers an uncaught exception during aparse→stringifyround-trip.Details
utils.isBufferruns atlib/stringify.js:127for every serialized value:utils.isBuffer(lib/utils.js:327-333) invokesobj.constructor.isBufferwithout verifying it is callable:constructorandisBufferare ordinary keys.qs.parsewithplainObjects: trueorallowPrototypes: truekeeps them as own properties, so the parsed value carries a non-functionconstructor.isBuffer;stringifythen calls a non-callable and throwsTypeError. By contrastutils.isRegExpuses a brand check (Object.prototype.toString); the missing guard here is an internal inconsistency, not a platform limitation.Trust Boundary Note
qs.stringifyalone treats its input as caller-constructed, so serializing a hostile object could be argued outside its contract. This report does not depend on that framing: the malicious shape is produced byqs.parse, whose input is untrusted by design.qs.parsenormally strips aconstructorkey via its prototype guard, but with the documented optionsplainObjects: trueorallowPrototypes: truethe key survives and lands as an own property. Feeding the parsed object back intoqs.stringify— the standard round-trip in gateways and request-forwarders — then hits the unchecked call.PoC
poc02c_isBuffer_qs_only_roundtrip.js— pure-qschain, noJSON.parse; an untrusted query string alone reaches the throw:poc02_isBuffer.js— the minimal defect:poc02b_isBuffer_async_crash.js— worker death in an async sink:Execution Steps
Reproduction Evidence
poc02c_isBuffer_qs_only_roundtrip.js:poc02_isBuffer.js:poc02b_isBuffer_async_crash.js:The pure-
qsround-trip shows the malicious shape originates fromqs.parseof an untrusted query string, with noJSON.parse. The synchronoustry/catchin the async case does not catch the throw; the process exits with code 1, denying service to all requests on that worker.Impact
An unauthenticated request degrades any endpoint that re-serializes deserialized client data with
qs.stringify. The primary impact is a per-request failure: the handler throws and the framework returns HTTP 500. Where the call sits in an unguarded async continuation, the throw escapes and the worker process exits, denying service to all requests it was handling, which means a higher impact that depends on the application's error handling, not onqs.Recommended Fix
Replace the duck-type with a brand check mirroring
utils.isRegExp:If duck-typing must remain, require
typeof obj.constructor.isBuffer === 'function'before invoking and wrap the call intry/catch.Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
qs array-limit bypass via bracket-key comma parsing
CVE-2026-82562 / GHSA-x5fp-wj9c-mxmx
More information
Details
Summary
qsv6.15.3allows bracket-key input to bypassarrayLimitandthrowOnLimitExceededwhencomma: true. The inputa[]=1,2,3,4succeeds witharrayLimit: 3, while the equivalent plain-key input is rejected.Affected version tested:
Details
parseArrayValue()enforces the comma limit only for flat values. Thea[]form is marked non-flat, so its comma-separated value is wrapped after parsing and the inner array is not checked. A single parameter can therefore materialize arbitrarily large arrays.PoC
On
v6.15.3, the first input parses successfully and the second creates an array with 1,000,001 elements. The equivalenta=1,2,3,4input throwsRangeErroras expected.Impact
An attacker who can supply a query string or form body can bypass configured array limits and force excessive memory allocation, causing denial of service. The limit must be applied after comma splitting and before the resulting array is wrapped.
Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
ljharb/qs (qs)
v6.16.0Compare Source
stringify: add adepthoption to bound recursion depth (defaultInfinity)parse: enforcearrayLimiton comma groups under[]=whenthrowOnLimitExceededis setparse: flatten a collection appended to an overflowed array (#571)utils:isBuffer: do not invoke a non-callableconstructor.isBufferstringify: do not letallowEmptyArraysskip cycle detection (or drop own keys) on an empty array with own propertiesstringify: encode dots in a top-level key with a primitive value when encodeDotInKeys is set (#562)stringifydeep-nesting DoS is caller-boundedarrayLimitis a representation threshold, not an element-count capparse: remove a test that pinned[]=comma groups escapingarrayLimitstringify: pin currentencodeDotInKeysseparator-dot behavior@ljharb/eslint-config,eslinteslint,evalmdConfiguration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.