Skip to content
This repository was archived by the owner on Oct 14, 2026. It is now read-only.

Commit f64d379

Browse files
chore(deps): update dependency js-yaml to v4.3.2 [security] (#1924)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [js-yaml](https://redirect.github.com/nodeca/js-yaml) | [`4.3.1` → `4.3.2`](https://renovatebot.com/diffs/npm/js-yaml/4.2.0/4.3.2) | ![age](https://developer.mend.io/api/mc/badges/age/npm/js-yaml/4.3.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/js-yaml/4.2.0/4.3.2?slim=true) | --- ### JS-YAML: Quadratic-complexity DoS in merge key handling via repeated aliases [CVE-2026-53550](https://nvd.nist.gov/vuln/detail/CVE-2026-53550) / [GHSA-h67p-54hq-rp68](https://redirect.github.com/advisories/GHSA-h67p-54hq-rp68) <details> <summary>More information</summary> #### Details ##### Summary A crafted YAML document can trigger algorithmic CPU exhaustion in `js-yaml` merge-key processing (`<<`) by repeating the same alias many times in a merge sequence. This causes quadratic parse-time behavior relative to input size and can block a Node.js worker/event loop for seconds with a relatively small payload (tens of KB), resulting in denial of service. ##### Details The issue is in merge handling inside `lib/loader.js`: - `storeMappingPair(...)` iterates every element of a merge sequence when key tag is `tag:yaml.org,2002:merge`. - For each element, it calls `mergeMappings(...)`. - `mergeMappings(...)` computes `Object.keys(source)` and performs `_hasOwnProperty.call(destination, key)` checks for each key. When input is of the form: a: &a {k0:0, k1:0, ..., kK:0} b: {<<: [*a, *a, *a, ... repeated M times ...]} all *a entries refer to the same anchored object. After the first merge, subsequent merges are semantically no-ops, but the parser still reprocesses all keys each time. Resulting work is O(K * M), while input size is O(K + M), giving quadratic scaling as payload grows. Relevant code path: lib/loader.js in storeMappingPair(...) merge branch (keyTag === 'tag:yaml.org,2002:merge') lib/loader.js mergeMappings(...) ##### Root cause File: lib/loader.js Function: storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, startLine, startLineStart, startPos) Lines: ~359-366 if (keyTag === 'tag:yaml.org,2002:merge') { if (Array.isArray(valueNode)) { for (index = 0, quantity = valueNode.length; index < quantity; index += 1) { mergeMappings(state, _result, valueNode[index], overridableKeys); } } else { mergeMappings(state, _result, valueNode, overridableKeys); } } When the merge value is a sequence (YAML 1.1 <<: [ *a, *a, ... ]), each element is handed to mergeMappings() without deduplication. mergeMappings() then does sourceKeys = Object.keys(source); for (index = 0; index < sourceKeys.length; index += 1) { key = sourceKeys[index]; if (!_hasOwnProperty.call(destination, key)) { setProperty(destination, key, source[key]); overridableKeys[key] = true; } } Every alias reference in the sequence resolves (by design) to the SAME object via state.anchorMap. After the first merge, every subsequent merge of that same reference is a pure no-op semantically, but still performs: * one Object.keys(source) call (O(K)) * K _hasOwnProperty.call checks on the destination Total: M * K hasOwnProperty checks + M Object.keys allocations, while the final object and all observable side effects are identical to a single merge. YAML semantics for `<<:` are idempotent and commutative over duplicate sources, so collapsing duplicates preserves behavior exactly; this isn't a spec trade-off. ##### PoC Environment: js-yaml version: 4.1.1 Node.js: v24.5.0 Platform: arm64 macOS (reproduced consistently) Reproduction script: Create many keys in one anchored map (&a). Merge that same alias repeatedly via <<: [*a, *a, ...]. Measure parse time and compare with control payload using single merge (<<: *a). Observed repeated runs (same machine): K=M=1000, input 9,909 bytes: ~33–36 ms K=M=2000, input 20,909 bytes: ~121–123 ms K=M=4000, input 42,909 bytes: ~524–537 ms K=M=6000, input 64,909 bytes: ~1,608–1,829 ms K=M=8000, input 86,909 bytes: ~3,395–3,565 ms Control (single merge, similar key counts): K=2000: ~1–2 ms K=4000: ~3 ms K=8000: ~5 ms Also verified: repeated-merge output equals single-merge output (same key count and same JSON), confirming excess time is redundant computation. ##### Impact This is a denial-of-service vulnerability (CPU exhaustion / algorithmic complexity). Any service parsing untrusted YAML with js-yaml can be impacted, including API backends, CI tools, config processors, and automation services. An attacker can submit crafted YAML to significantly increase CPU time and reduce availability. ##### Suggested fix: Dedupe the merge source list by reference before invoking mergeMappings. Any of the following are minimal and preserve YAML 1.1 merge semantics: dedupe in storeMappingPair: if (keyTag === 'tag:yaml.org,2002:merge') { if (Array.isArray(valueNode)) { var seen = new Set(); for (index = 0, quantity = valueNode.length; index < quantity; index += 1) { var src = valueNode[index]; if (seen.has(src)) continue; // idempotent; skip redundant alias seen.add(src); mergeMappings(state, _result, src, overridableKeys); } } else { mergeMappings(state, _result, valueNode, overridableKeys); } } #### Severity - CVSS Score: 5.3 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L` #### References - [https://github.com/nodeca/js-yaml/security/advisories/GHSA-h67p-54hq-rp68](https://redirect.github.com/nodeca/js-yaml/security/advisories/GHSA-h67p-54hq-rp68) - [https://nvd.nist.gov/vuln/detail/CVE-2026-53550](https://nvd.nist.gov/vuln/detail/CVE-2026-53550) - [https://github.com/advisories/GHSA-h67p-54hq-rp68](https://redirect.github.com/advisories/GHSA-h67p-54hq-rp68) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-h67p-54hq-rp68) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### js-yaml: YAML merge-key chains can force quadratic CPU consumption [CVE-2026-59869](https://nvd.nist.gov/vuln/detail/CVE-2026-59869) / [GHSA-52cp-r559-cp3m](https://redirect.github.com/advisories/GHSA-52cp-r559-cp3m) <details> <summary>More information</summary> #### Details ##### Impact js-yaml can spend quadratic CPU time parsing a document whose size grows only linearly. The issue is triggered by a chain of mappings where each mapping merges the previous one: ```yaml a0: &a0 { k0: 0 } a1: &a1 { <<: *a0, k1: 1 } a2: &a2 { <<: *a1, k2: 2 } a3: &a3 { <<: *a2, k3: 3 } ... b: *aN ``` For each new mapping, the loader has to enumerate the keys inherited from the previous mapping. With N chained mappings, this results in roughly 1 + 2 + ... + N merged-key visits, i.e., O(N^2) work for O(N) input size. ##### PoC From N = 4000 delay become > 1s (doc size < 100K) ```js import { performance } from 'node:perf_hooks' import { Buffer } from 'node:buffer' import { load, YAML11_SCHEMA } from 'js-yaml' const n = Number(process.argv[2] || 4000) function makeMergeChain (count) { const lines = ['a0: &a0 { k0: 0 }'] for (let i = 1; i < count; i++) { lines.push(`a${i}: &a${i} { <<: *a${i - 1}, k${i}: ${i} }`) } lines.push(`b: *a${count - 1}`) return `${lines.join('\n')}\n` } const source = makeMergeChain(n) console.log(source.split('\n').slice(0, 8).join('\n')) console.log('...') console.log(source.split('\n').slice(-4).join('\n')) console.log() console.log(`N: ${n}`) console.log(`YAML size: ${Buffer.byteLength(source)} bytes`) const started = performance.now() const result = load(source, { schema: YAML11_SCHEMA }) const elapsed = performance.now() - started console.log(`parse time: ${elapsed.toFixed(1)} ms`) console.log(`top-level keys: ${Object.keys(result).length}`) console.log(`b keys: ${Object.keys(result.b).length}`) ``` ##### Patches Fix released. The most robust protection is to limit the total number of merged keys per parse call. This should close all past and future edge cases with merge. The default 10K-key limit should be okay in most cases. #### Severity - CVSS Score: 7.5 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` #### References - [https://github.com/nodeca/js-yaml/security/advisories/GHSA-52cp-r559-cp3m](https://redirect.github.com/nodeca/js-yaml/security/advisories/GHSA-52cp-r559-cp3m) - [https://nvd.nist.gov/vuln/detail/CVE-2026-59869](https://nvd.nist.gov/vuln/detail/CVE-2026-59869) - [https://github.com/nodeca/js-yaml/commit/24f13e79ee1343a7e30bd6f6c9d9cdbf0ac9b2b7](https://redirect.github.com/nodeca/js-yaml/commit/24f13e79ee1343a7e30bd6f6c9d9cdbf0ac9b2b7) - [https://github.com/nodeca/js-yaml/commit/59423c6f8cdc78742ac00e25a4dd39ef16b702e4](https://redirect.github.com/nodeca/js-yaml/commit/59423c6f8cdc78742ac00e25a4dd39ef16b702e4) - [https://github.com/nodeca/js-yaml/releases/tag/3.15.0](https://redirect.github.com/nodeca/js-yaml/releases/tag/3.15.0) - [https://github.com/nodeca/js-yaml/releases/tag/4.3.0](https://redirect.github.com/nodeca/js-yaml/releases/tag/4.3.0) - [https://github.com/advisories/GHSA-52cp-r559-cp3m](https://redirect.github.com/advisories/GHSA-52cp-r559-cp3m) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-52cp-r559-cp3m) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026-59870 fix not backported [GHSA-5p4m-2wfm-xmqj](https://redirect.github.com/advisories/GHSA-5p4m-2wfm-xmqj) <details> <summary>More information</summary> #### Details ##### Quadratic CPU consumption in `!!omap` resolution (js-yaml 3.x and 4.x) ##### Summary `resolveYamlOmap()` enforces key uniqueness for `!!omap` sequences with a linear scan (`objectKeys.indexOf(...)`) inside the per-element loop, making resolution **O(n²)** in the number of entries. A modestly sized YAML document therefore consumes disproportionate CPU inside `yaml.load()`, giving a denial of service against any consumer that parses untrusted YAML. `!!omap` is registered in the **default schema** (`lib/schema/default.js` → `require('../type/omap')`), so a plain `yaml.load(untrustedInput)` with no options is affected — no custom schema or non-default configuration is required. **This is the same weakness as CVE-2026-59870 / GHSA-724g-mxrg-4qvm**, which was fixed in the 5.x line in 5.2.1. That fix was never backported: both currently maintained legacy lines still carry the original implementation. ##### Affected versions | Line | Latest tested | Status | |---|---|---| | 3.x | **3.15.0** | Affected — `objectKeys.indexOf(pairKey)` at `lib/type/omap.js:29` | | 4.x | **4.3.0** | Affected — `objectKeys.indexOf(pairKey)` at `lib/type/omap.js:30` | | 5.x | 5.2.2 | **Not affected** — fixed in 5.2.1 (uses a `Set`) | Both figures are the newest release of each line at the time of writing, so this is not a "you are on an old version" issue. ##### Details `lib/type/omap.js` (js-yaml 4.3.0): ```js if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey) else return false ``` `objectKeys` grows by one element per entry, and `Array.prototype.indexOf` is a linear scan, so resolving an `n`-entry `!!omap` performs roughly `1 + 2 + … + n` comparisons — quadratic in `n`. The work happens synchronously inside `yaml.load()`, blocking the event loop for its whole duration. The 5.x line already solves exactly this by tracking seen keys in a `Set` (`src/tag/sequence/omap.ts`): ```ts if (carrier.seen.has(key)) return 'duplicate key in ordered map' carrier.seen.add(key) ``` ##### Proof of concept ```js // poc.js — node poc.js const yaml = require('js-yaml'); const doc = n => '!!omap\n' + Array.from({length: n}, (_, i) => `- k${i}: ${i}`).join('\n') + '\n'; for (const n of [10000, 20000, 40000, 80000]) { const d = doc(n), t = Date.now(); yaml.load(d); // default schema, no options console.log(`n=${n} bytes=${d.length} load=${Date.now() - t}ms`); } ``` ##### Measured (node v20.20.2, default heap, no flags) **js-yaml 4.3.0** ``` n=10000 bytes=137787 load=54ms n=20000 bytes=297787 load=169ms n=40000 bytes=617787 load=646ms n=80000 bytes=1257787 load=2607ms ``` **js-yaml 3.15.0** ``` n=10000 bytes=137787 load=53ms n=20000 bytes=297787 load=166ms n=40000 bytes=617787 load=641ms n=80000 bytes=1257787 load=2567ms ``` Runtime grows by a factor of ~4 for each doubling of `n`, which is the signature of O(n²) (linear growth would be ~2×). Scaling further: a **2.48 MB** document with 150,000 entries blocked `yaml.load()` for **10.8 seconds**. ##### Impact Any service that parses attacker-influenced YAML with js-yaml 3.x or 4.x can be stalled with a small input. Because the loop is synchronous, a single request blocks the Node.js event loop and stalls every other request in the process — so the amplification is per-process, not just per-request. Suggested severity: consistent with **CVE-2026-59870** (the same weakness in 5.x), i.e. Availability-only impact, network attack vector, no privileges or user interaction required. ##### Suggested fix Mirror the 5.x fix — replace the linear scan with a `Set`: ```js // lib/type/omap.js const seen = new Set() // ... if (seen.has(pairKey)) return false seen.add(pairKey) ``` This preserves the existing duplicate-key rejection semantics exactly while making resolution O(n). A `maxOmapLength`-style cap would also work, but the `Set` matches what 5.x already ships and requires no new option. ##### References - CVE-2026-59870 / GHSA-724g-mxrg-4qvm — same weakness in 5.0.0–5.2.0, fixed in 5.2.1 - `lib/type/omap.js` (3.x, 4.x) — the affected resolver - `lib/schema/default.js` — registers `!!omap` in the default schema ##### Discovery Found by an automated static-analysis and executed-proof-of-concept scanner run against js-yaml 4.2.0, then manually verified against 3.15.0 and 4.3.0 by executing the proof of concept above. All timings in this report were measured on the **current** releases of each line, not on the version originally scanned. #### Severity - CVSS Score: 7.5 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` #### References - [https://github.com/nodeca/js-yaml/security/advisories/GHSA-5p4m-2wfm-xmqj](https://redirect.github.com/nodeca/js-yaml/security/advisories/GHSA-5p4m-2wfm-xmqj) - [https://github.com/advisories/GHSA-5p4m-2wfm-xmqj](https://redirect.github.com/advisories/GHSA-5p4m-2wfm-xmqj) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-5p4m-2wfm-xmqj) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources [CVE-2026-84375](https://nvd.nist.gov/vuln/detail/CVE-2026-84375) / [GHSA-2883-xcg3-v3hh](https://redirect.github.com/advisories/GHSA-2883-xcg3-v3hh) <details> <summary>More information</summary> #### Details ##### Summary `maxTotalMergeKeys` does not count empty mappings. An attacker can repeatedly merge a large sequence of them and consume significant CPU without reaching the configured limit. ##### Example ```yaml arr: &arr [{}, {}, {}, ...] # N empty mappings targets: - <<: *arr # repeated K times ``` For every target, the loader iterates all `N` elements of `arr`. This results in `O(N * K)` work while `totalMergeKeys` remains unchanged. ##### PoC ```js import { performance } from 'node:perf_hooks' import { load, YAML11_SCHEMA } from 'js-yaml' const n = 20000 const src = 'arr: &arr [' + '{},'.repeat(n).slice(0, -1) + ']\n' + 'targets:\n' + ' - <<: *arr\n'.repeat(n) const started = performance.now() load(src, { schema: YAML11_SCHEMA }) console.log(`${(performance.now() - started).toFixed(1)} ms`) ``` Observed results: | N | YAML size | Time | |---:|---:|---:| | 800 | ~13 KB | ~20 ms | | 3200 | ~50 KB | ~180 ms | | 20000 | ~500 KB | ~13 s | ##### Impact An attacker can submit a relatively small YAML document that causes prolonged CPU consumption despite the default `maxTotalMergeKeys` limit. ##### Fix Count each merge-source mapping as one budget unit, in addition to counting its keys. ##### Difference with v5 In v3 & v4, merge is enabled by default. So, the severity score is higher. #### Severity - CVSS Score: 7.5 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` #### References - [https://github.com/nodeca/js-yaml/security/advisories/GHSA-2883-xcg3-v3hh](https://redirect.github.com/nodeca/js-yaml/security/advisories/GHSA-2883-xcg3-v3hh) - [https://nvd.nist.gov/vuln/detail/CVE-2026-84375](https://nvd.nist.gov/vuln/detail/CVE-2026-84375) - [https://github.com/nodeca/js-yaml/pull/797](https://redirect.github.com/nodeca/js-yaml/pull/797) - [https://github.com/nodeca/js-yaml/commit/3485bc06ff8a0251505f44a00414d90df2466639](https://redirect.github.com/nodeca/js-yaml/commit/3485bc06ff8a0251505f44a00414d90df2466639) - [https://github.com/nodeca/js-yaml/commit/6a8e05f9a485188ed730ac81e81ae221352ef480](https://redirect.github.com/nodeca/js-yaml/commit/6a8e05f9a485188ed730ac81e81ae221352ef480) - [https://github.com/nodeca/js-yaml/commit/d90b6612a5a84385bdcb556c44578eac76dc0f6b](https://redirect.github.com/nodeca/js-yaml/commit/d90b6612a5a84385bdcb556c44578eac76dc0f6b) - [https://github.com/nodeca/js-yaml/releases/tag/3.15.2](https://redirect.github.com/nodeca/js-yaml/releases/tag/3.15.2) - [https://github.com/nodeca/js-yaml/releases/tag/4.3.2](https://redirect.github.com/nodeca/js-yaml/releases/tag/4.3.2) - [https://github.com/advisories/GHSA-2883-xcg3-v3hh](https://redirect.github.com/advisories/GHSA-2883-xcg3-v3hh) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-2883-xcg3-v3hh) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Release Notes <details> <summary>nodeca/js-yaml (js-yaml)</summary> ### [`v4.3.2`](https://redirect.github.com/nodeca/js-yaml/compare/4.3.1...4.3.2) [Compare Source](https://redirect.github.com/nodeca/js-yaml/compare/4.3.1...4.3.2) ### [`v4.3.1`](https://redirect.github.com/nodeca/js-yaml/compare/4.3.0...4.3.1) [Compare Source](https://redirect.github.com/nodeca/js-yaml/compare/4.3.0...4.3.1) ### [`v4.3.0`](https://redirect.github.com/nodeca/js-yaml/compare/4.2.0...33d05b5d29a8c21360f620f7e1c1706e24522eda) [Compare Source](https://redirect.github.com/nodeca/js-yaml/compare/4.2.0...4.3.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone Europe/Zurich) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **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/adobe/spacecat-shared). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC42OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNjkuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
1 parent fcc368b commit f64d379

3 files changed

Lines changed: 7 additions & 7 deletions

File tree

‎package-lock.json‎

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/spacecat-shared-project-engine-client/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"c8": "11.0.0",
6464
"chai": "6.2.2",
6565
"counterfact": "2.14.0",
66-
"js-yaml": "4.3.1",
66+
"js-yaml": "4.3.2",
6767
"mocha": "11.7.6",
6868
"mocha-multi-reporters": "1.5.1",
6969
"openapi-typescript": "7.13.0",

‎packages/spacecat-shared-user-manager-client/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"c8": "11.0.0",
6161
"chai": "6.2.2",
6262
"counterfact": "2.14.0",
63-
"js-yaml": "4.3.1",
63+
"js-yaml": "4.3.2",
6464
"mocha": "11.7.6",
6565
"mocha-multi-reporters": "1.5.1",
6666
"openapi-typescript": "7.13.0",

0 commit comments

Comments
 (0)