This repository was archived by the owner on Oct 14, 2026. It is now read-only.
Repository navigation
Commit f64d379
authored
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) |

|

|
---
### 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
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
| 66 | + | |
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
| 63 | + | |
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| |||
0 commit comments