[pull] main from TryGhost:main#1168
Merged
Merged
Conversation
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [qs](https://redirect.github.com/ljharb/qs) | [`6.14.2` → `6.15.2`](https://renovatebot.com/diffs/npm/qs/6.14.2/6.15.2) |  |  | --- ### qs has a remotely triggerable DoS: qs.stringify crashes with TypeError on null/undefined entries in comma-format arrays when encodeValuesOnly is set [CVE-2026-8723](https://nvd.nist.gov/vuln/detail/CVE-2026-8723) / [GHSA-q8mj-m7cp-5q26](https://redirect.github.com/advisories/GHSA-q8mj-m7cp-5q26) <details> <summary>More information</summary> #### Details ##### Summary `qs.stringify` throws `TypeError` when called with `arrayFormat: 'comma'` and `encodeValuesOnly: true` on an array containing `null` or `undefined`. The throw is synchronous and not handled by any of qs's null-related options (`skipNulls`, `strictNullHandling`). ##### Details In the comma + `encodeValuesOnly` branch, `lib/stringify.js:145` mapped the array through the raw encoder before joining: ```js obj = utils.maybeMap(obj, encoder); ``` `utils.encode` (`lib/utils.js:195`) reads `str.length` with no null guard, so a `null` or `undefined` element throws `TypeError`. `skipNulls` and `strictNullHandling` are both checked in the per-element loop below this line and never get a chance to run. Same class of bug as the filter-array path fixed in 0c180a4. The vulnerable shape of the comma + `encodeValuesOnly` branch was introduced in 4c4b23d ("encode comma values more consistently", PR #​463, 2023-01-19), first released in v6.11.1. ##### PoC ```js const qs = require('qs'); qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }); // TypeError: Cannot read properties of null (reading 'length') // at encode (lib/utils.js:195:13) // at Object.maybeMap (lib/utils.js:322:37) // at stringify (lib/stringify.js:145:25) ``` ##### Fix `lib/stringify.js:145`, applied in 21f80b3 on `main`: ```diff - obj = utils.maybeMap(obj, encoder); + obj = utils.maybeMap(obj, function (v) { + return v == null ? v : encoder(v); + }); ``` `null` and `undefined` now pass through `maybeMap` unchanged and reach the `join(',')` step as-is. For `{ a: [null, 'b'] }` this produces `a=,b`, matching the non-`encodeValuesOnly` comma path (which already joins before encoding and produces `a=%2Cb` for the same input). Single-element `[null]` arrays still collapse via the existing `obj.join(',') || null` and remain subject to `skipNulls` / `strictNullHandling` in the main loop. ##### Affected versions `>=6.11.1 <=6.15.1` The vulnerable code shape was introduced in 4c4b23d and first shipped in v6.11.1. Earlier versions — including all of 6.7.x, 6.8.x, 6.9.x, 6.10.x, and 6.11.0 — implemented the comma + `encodeValuesOnly` path differently (joining before encoding) and are not affected. Empirically verified across released versions. ##### Impact Application code that calls `qs.stringify` with both `arrayFormat: 'comma'` and `encodeValuesOnly: true` (both non-default) on input that may contain a `null` or `undefined` array element will throw synchronously instead of producing a query string. In a typical Node.js HTTP framework (Express, Fastify, Koa, hapi) the sync throw is caught by the framework's error boundary and the affected request returns a 500; the worker process does not exit and subsequent requests are unaffected. The "kills the worker process" framing applies only to call sites outside a request-handler error boundary (background jobs, startup paths, stream pipelines) or to deployments with framework error handling explicitly disabled. The vulnerable input is a `null` or `undefined` entry inside an array; this is reachable from JSON request bodies or from application code constructing arrays from user input, but not from standard HTML form submissions (which produce strings or omitted fields, not literal `null`). #### Severity - CVSS Score: 6.3 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N` #### References - [https://github.com/ljharb/qs/security/advisories/GHSA-q8mj-m7cp-5q26](https://redirect.github.com/ljharb/qs/security/advisories/GHSA-q8mj-m7cp-5q26) - [https://nvd.nist.gov/vuln/detail/CVE-2026-8723](https://nvd.nist.gov/vuln/detail/CVE-2026-8723) - [https://github.com/ljharb/qs/commit/21f80b33e5c8b3f7eba1034fff0da4a4a37a1d41](https://redirect.github.com/ljharb/qs/commit/21f80b33e5c8b3f7eba1034fff0da4a4a37a1d41) - [https://github.com/advisories/GHSA-q8mj-m7cp-5q26](https://redirect.github.com/advisories/GHSA-q8mj-m7cp-5q26) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-q8mj-m7cp-5q26) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Release Notes <details> <summary>ljharb/qs (qs)</summary> ### [`v6.15.2`](https://redirect.github.com/ljharb/qs/blob/HEAD/CHANGELOG.md#6152) [Compare Source](https://redirect.github.com/ljharb/qs/compare/v6.15.1...v6.15.2) - \[Fix] `stringify`: skip null/undefined entries in `arrayFormat: 'comma'` + `encodeValuesOnly` instead of crashing in `encoder` - \[Fix] `stringify`: use configured `delimiter` after `charsetSentinel` ([#​555](https://redirect.github.com/ljharb/qs/issues/555)) - \[Fix] `stringify`: apply `formatter` to encoded key under `strictNullHandling` ([#​554](https://redirect.github.com/ljharb/qs/issues/554)) - \[Fix] `stringify`: skip null/undefined filter-array entries instead of crashing in `encoder` ([#​551](https://redirect.github.com/ljharb/qs/issues/551)) - \[Fix] `parse`: handle nested bracket groups and add regression tests ([#​530](https://redirect.github.com/ljharb/qs/issues/530)) - \[readme] fix grammar ([#​550](https://redirect.github.com/ljharb/qs/issues/550)) - \[Dev Deps] update `@ljharb/eslint-config` - \[Tests] add regression tests for keys containing percent-encoded bracket text ### [`v6.15.1`](https://redirect.github.com/ljharb/qs/blob/HEAD/CHANGELOG.md#6151) [Compare Source](https://redirect.github.com/ljharb/qs/compare/v6.15.0...v6.15.1) - \[Fix] `parse`: `parameterLimit: Infinity` with `throwOnLimitExceeded: true` silently drops all parameters - \[Deps] update `@ljharb/eslint-config` - \[Dev Deps] update `@ljharb/eslint-config`, `iconv-lite` - \[Tests] increase coverage ### [`v6.15.0`](https://redirect.github.com/ljharb/qs/blob/HEAD/CHANGELOG.md#6150) [Compare Source](https://redirect.github.com/ljharb/qs/compare/v6.14.2...v6.15.0) - \[New] `parse`: add `strictMerge` option to wrap object/primitive conflicts in an array ([#​425](https://redirect.github.com/ljharb/qs/issues/425), [#​122](https://redirect.github.com/ljharb/qs/issues/122)) - \[Fix] `duplicates` option should not apply to bracket notation keys ([#​514](https://redirect.github.com/ljharb/qs/issues/514)) </details> --- ### Configuration 📅 **Schedule**: (in timezone Etc/UTC) - Branch creation - "" - Automerge - Only on Sunday and Saturday (`* * * * 0,6`) - Between 12:00 AM and 12:59 PM, only on Monday (`* 0-12 * * 1`) - Between 10:00 PM and 11:59 PM, Monday through Friday (`* 22-23 * * 1-5`) - Between 12:00 AM and 04:59 AM, Tuesday through Saturday (`* 0-4 * * 2-6`) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/TryGhost/Ghost). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTQuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE5NC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
no ref - skips saving/uploading the production Docker image artifact when CI can push/pull from GHCR - keeps the artifact transfer path for external fork and cross-repo PR runs where GHCR push is unavailable
## Summary - skips the E2E report merge job when the E2E matrix succeeds - keeps report generation for failed E2E runs, where blob reports are uploaded and useful for debugging
no ref - changes the main browser E2E matrix from 8 shards to 10 shards - leaves analytics E2E at 2 shards - keeps `TEST_WORKERS_COUNT=1` unchanged
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )