JS-YAML: Quadratic-complexity DoS in merge key handling via repeated aliases
Description
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 istag:yaml.org,2002:merge.- For each element, it calls
mergeMappings(...). mergeMappings(...)computesObject.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); } }
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
js-yamlnpm | < 4.2.0 | 4.2.0 |
Affected products
129- osv-coords128 versionspkg:apk/chainguard/airflow-3pkg:apk/chainguard/arangodb-3.11pkg:apk/chainguard/arangodb-3.12pkg:apk/chainguard/argo-workflows-ui-3.6pkg:apk/chainguard/argo-workflows-ui-3.7pkg:apk/chainguard/argo-workflows-ui-4.0pkg:apk/chainguard/authentik-2026.2pkg:apk/chainguard/authentik-2026.5pkg:apk/chainguard/authentik-fips-2026.5pkg:apk/chainguard/awxpkg:apk/chainguard/code-serverpkg:apk/chainguard/drupal-11.3pkg:apk/chainguard/eslintpkg:apk/chainguard/foxx-clipkg:apk/chainguard/gemini-clipkg:apk/chainguard/homepagepkg:apk/chainguard/katib-earlystoppingpkg:apk/chainguard/katib-suggestion-hyperbandpkg:apk/chainguard/katib-suggestion-hyperoptpkg:apk/chainguard/katib-suggestion-nas-dartspkg:apk/chainguard/katib-suggestion-nas-enaspkg:apk/chainguard/katib-suggestion-optuna-enaspkg:apk/chainguard/katib-suggestion-pbt-enaspkg:apk/chainguard/katib-suggestion-skopt-enaspkg:apk/chainguard/katib-tfevent-metricscollectorpkg:apk/chainguard/kibana-8.17pkg:apk/chainguard/kibana-8.17-bitnamipkg:apk/chainguard/kibana-8.17-iamguardedpkg:apk/chainguard/kibana-8.19pkg:apk/chainguard/kibana-8.19-bitnamipkg:apk/chainguard/kibana-8.19-iamguardedpkg:apk/chainguard/kibana-9.1pkg:apk/chainguard/kibana-9.1-iamguardedpkg:apk/chainguard/kibana-9.2pkg:apk/chainguard/kibana-9.2-iamguardedpkg:apk/chainguard/kibana-9.3pkg:apk/chainguard/kibana-9.3-iamguardedpkg:apk/chainguard/kibana-9.4pkg:apk/chainguard/kibana-9.4-iamguardedpkg:apk/chainguard/kubeflow-pipelines-frontendpkg:apk/chainguard/langfuse-3pkg:apk/chainguard/langfuse-3-workerpkg:apk/chainguard/langfuse-fips-3pkg:apk/chainguard/langfuse-fips-3-workerpkg:apk/chainguard/lernapkg:apk/chainguard/mongod-7.0-oci-entrypoint-compatpkg:apk/chainguard/mongod-8.0-oci-entrypoint-compatpkg:apk/chainguard/mongod-8.2-oci-entrypoint-compatpkg:apk/chainguard/mongod-8.3-oci-entrypoint-compatpkg:apk/chainguard/netboxpkg:apk/chainguard/netbox-fipspkg:apk/chainguard/opensearch-dashboards-2pkg:apk/chainguard/opensearch-dashboards-2-fipspkg:apk/chainguard/opensearch-dashboards-3pkg:apk/chainguard/opensearch-dashboards-3-dashboards-observabilitypkg:apk/chainguard/opensearch-dashboards-3-fipspkg:apk/chainguard/opensearch-dashboards-3-fips-dashboards-observabilitypkg:apk/chainguard/pelias-apipkg:apk/chainguard/prismpkg:apk/chainguard/pulumi-language-nodejspkg:apk/chainguard/py3.10-captumpkg:apk/chainguard/py3.10-jupyterlabpkg:apk/chainguard/py3.11-captumpkg:apk/chainguard/py3.11-jupyterlabpkg:apk/chainguard/py3.12-captumpkg:apk/chainguard/py3.12-jupyterlabpkg:apk/chainguard/py3.13-captumpkg:apk/chainguard/py3.13-jupyterlabpkg:apk/chainguard/redisinsightpkg:apk/chainguard/renovatepkg:apk/chainguard/safpkg:apk/chainguard/semaphorepkg:apk/chainguard/sqlpadpkg:apk/chainguard/tensorflow-cpu-jupyterpkg:apk/chainguard/thingsboard-tb-js-executorpkg:apk/chainguard/thingsboard-tb-js-executor-fipspkg:apk/chainguard/thingsboard-tb-web-uipkg:apk/chainguard/thingsboard-tb-web-ui-fipspkg:apk/chainguard/tileserver-glpkg:apk/chainguard/tileserver-gl-fipspkg:apk/chainguard/unleashpkg:apk/chainguard/vitess-24pkg:apk/chainguard/wazuh-dashboardpkg:apk/chainguard/wazuh-dashboard-anomaly-detection-dashboards-pluginpkg:apk/chainguard/wazuh-dashboard-anomaly-detection-dashboards-plugin-fipspkg:apk/chainguard/wazuh-dashboard-dashboards-reportingpkg:apk/chainguard/wazuh-dashboard-dashboards-reporting-fipspkg:apk/chainguard/wazuh-dashboard-dashboards-visualizationspkg:apk/chainguard/wazuh-dashboard-dashboards-visualizations-fipspkg:apk/chainguard/wazuh-dashboard-fipspkg:apk/chainguard/wazuh-dashboard-index-management-dashboards-pluginpkg:apk/chainguard/wazuh-dashboard-index-management-dashboards-plugin-fipspkg:apk/chainguard/wazuh-dashboard-pluginspkg:apk/chainguard/wazuh-dashboard-plugins-fipspkg:apk/wolfi/airflow-3pkg:apk/wolfi/argo-workflows-ui-3.7pkg:apk/wolfi/argo-workflows-ui-4.0pkg:apk/wolfi/code-serverpkg:apk/wolfi/eslintpkg:apk/wolfi/katib-earlystoppingpkg:apk/wolfi/katib-suggestion-hyperbandpkg:apk/wolfi/katib-suggestion-hyperoptpkg:apk/wolfi/katib-suggestion-nas-dartspkg:apk/wolfi/katib-suggestion-nas-enaspkg:apk/wolfi/katib-suggestion-optuna-enaspkg:apk/wolfi/katib-suggestion-pbt-enaspkg:apk/wolfi/katib-suggestion-skopt-enaspkg:apk/wolfi/katib-tfevent-metricscollectorpkg:apk/wolfi/kubeflow-pipelines-frontendpkg:apk/wolfi/langfuse-3pkg:apk/wolfi/langfuse-3-workerpkg:apk/wolfi/lernapkg:apk/wolfi/opensearch-dashboards-3pkg:apk/wolfi/opensearch-dashboards-3-dashboards-observabilitypkg:apk/wolfi/prismpkg:apk/wolfi/pulumi-language-nodejspkg:apk/wolfi/py3.10-jupyterlabpkg:apk/wolfi/py3.11-jupyterlabpkg:apk/wolfi/py3.12-jupyterlabpkg:apk/wolfi/py3.13-jupyterlabpkg:apk/wolfi/renovatepkg:apk/wolfi/safpkg:apk/wolfi/sqlpadpkg:apk/wolfi/tensorflow-cpu-jupyterpkg:apk/wolfi/thingsboard-tb-js-executorpkg:apk/wolfi/thingsboard-tb-web-uipkg:apk/wolfi/tileserver-glpkg:apk/wolfi/vitess-24
< 3.2.2-r8+ 127 more
- (no CPE)range: < 3.2.2-r8
- (no CPE)range: < 3.11.14.4-r7
- (no CPE)range: < 3.12.9.4-r3
- (no CPE)range: < 3.6.19-r7
- (no CPE)range: < 3.7.15-r2
- (no CPE)range: < 4.0.6-r2
- (no CPE)range: < 2026.2.5-r5
- (no CPE)range: < 2026.5.3-r8
- (no CPE)range: < 2026.5.4-r0
- (no CPE)range: < 24.6.1-r42
- (no CPE)range: < 4.124.2-r0
- (no CPE)range: < 11.3.13-r3
- (no CPE)range: < 10.5.0-r1
- (no CPE)range: < 2.1.1-r8
- (no CPE)range: < 0.49.0-r4
- (no CPE)range: < 1.13.2-r4
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 8.17.10-r27
- (no CPE)range: < 8.17.10-r27
- (no CPE)range: < 8.17.10-r27
- (no CPE)range: < 8.19.17-r1
- (no CPE)range: < 8.19.17-r1
- (no CPE)range: < 8.19.17-r1
- (no CPE)range: < 9.1.10-r26
- (no CPE)range: < 9.1.10-r26
- (no CPE)range: < 9.2.8-r12
- (no CPE)range: < 9.2.8-r12
- (no CPE)range: < 9.3.6-r2
- (no CPE)range: < 9.3.6-r2
- (no CPE)range: < 9.4.2-r6
- (no CPE)range: < 9.4.2-r6
- (no CPE)range: < 2.16.1-r9
- (no CPE)range: < 3.205.1-r3
- (no CPE)range: < 3.205.1-r3
- (no CPE)range: < 3.205.1-r2
- (no CPE)range: < 3.205.1-r2
- (no CPE)range: < 9.0.7-r10
- (no CPE)range: < 7.0.37-r1
- (no CPE)range: < 8.0.26-r1
- (no CPE)range: < 8.2.11-r1
- (no CPE)range: < 8.3.4-r1
- (no CPE)range: < 4.6.3-r3
- (no CPE)range: < 4.6.3-r3
- (no CPE)range: < 2.19.5-r15
- (no CPE)range: < 2.19.5-r16
- (no CPE)range: < 3.7.0-r2
- (no CPE)range: < 3.7.0-r2
- (no CPE)range: < 3.7.0-r1
- (no CPE)range: < 3.7.0-r1
- (no CPE)range: < 7.8.0-r5
- (no CPE)range: < 5.15.11-r3
- (no CPE)range: < 3.248.0-r0
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 4.6.1-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 4.6.1-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 4.6.1-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 4.6.1-r1
- (no CPE)range: < 3.6.0-r1
- (no CPE)range: < 43.257.5-r0
- (no CPE)range: < 1.6.0-r1
- (no CPE)range: < 2.18.12-r2
- (no CPE)range: < 7.5.7-r28
- (no CPE)range: < 2.21.0-r8
- (no CPE)range: < 4.3.1.3-r1
- (no CPE)range: < 4.3.1.2-r1
- (no CPE)range: < 4.3.1.3-r1
- (no CPE)range: < 4.3.1.2-r1
- (no CPE)range: < 5.6.0-r7
- (no CPE)range: < 5.6.0-r7
- (no CPE)range: < 8.0.1-r0
- (no CPE)range: < 24.0.1-r4
- (no CPE)range: < 4.14.5-r8
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 4.14.5-r9
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 3.2.2-r8
- (no CPE)range: < 3.7.15-r2
- (no CPE)range: < 4.0.6-r2
- (no CPE)range: < 4.124.2-r0
- (no CPE)range: < 10.5.0-r1
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 0.19.0-r31
- (no CPE)range: < 2.16.1-r9
- (no CPE)range: < 3.205.1-r3
- (no CPE)range: < 3.205.1-r3
- (no CPE)range: < 9.0.7-r10
- (no CPE)range: < 3.7.0-r2
- (no CPE)range: < 3.7.0-r2
- (no CPE)range: < 5.15.11-r3
- (no CPE)range: < 3.248.0-r0
- (no CPE)range: < 4.6.1-r1
- (no CPE)range: < 4.6.1-r1
- (no CPE)range: < 4.6.1-r1
- (no CPE)range: < 4.6.1-r1
- (no CPE)range: < 43.257.5-r0
- (no CPE)range: < 1.6.0-r1
- (no CPE)range: < 7.5.7-r28
- (no CPE)range: < 2.21.0-r8
- (no CPE)range: < 4.3.1.3-r1
- (no CPE)range: < 4.3.1.3-r1
- (no CPE)range: < 5.6.0-r7
- (no CPE)range: < 24.0.1-r4
Patches
Vulnerability mechanics
References
2News mentions
0No linked articles in our index yet.