Update dependency js-yaml to v4.3.2 [SECURITY] #18023

Open
renovate[bot] opened 10:44am on September 16, 2026 wants to merge 1 Ξ” into withastro/astro main from
renovate/npm-js-yaml-vulnerability
Reviewing
257 fewer changed lines
(99% less) vs GitHub
This saves about 173.3 hours per year vs conventional diff tools
renovate[bot] authored
of work between September 16 and September 17
Diff Delta:
1
About 3 Diff Delta/hour
Classified as:  General

renovate-bot's Description of Work

This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| js-yaml | 4.3.0 β†’ 4.3.2 | age | confidence |


JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) β€” CVE-2026-59870 fix not backported

GHSA-5p4m-2wfm-xmqj

More information

#### 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)).


js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources

CVE-2026-84375 / GHSA-2883-xcg3-v3hh

More information

#### 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)).


Release Notes

nodeca/js-yaml (js-yaml)

### [`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)


Configuration

πŸ“… Schedule: (UTC)


  • Branch creation


    • At any time (no schedule defined)



  • Automerge


    • At any time (no schedule defined)



🚦 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 these updates again.



  • [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box


This PR was generated by Mend Renovate. View the repository job log.
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC45My43IiwidXBkYXRlZEluVmVyIjoiNDQuOTMuNyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->

Building updated commit group diff...
2 total changed files
(1 file ignored)
Loading changes...