Understanding the Vulnerability
js-yaml is a widely used YAML parser for JavaScript and Node.js applications. It converts YAML documents—human-readable data serialization format—into JavaScript objects. Services that accept YAML input from users, configuration systems, or APIs rely on js-yaml to deserialize untrusted data safely.
A flaw in js-yaml 5.2.1's handling of flow collections (YAML's inline syntax for arrays and objects) allows an attacker to trigger exponential parsing behavior. Flow collections use brackets and braces: [item1, item2] for arrays and {key: value} for objects. When deeply nested, the parser must recursively evaluate each level, but in version 5.2.1, this recursion enters an exponential time loop—each nesting level multiplies the work required.
Affected Versions
| Affected | js-yaml >= 5.0.0, < 5.2.2 |
| Fixed in | 5.2.2 |
| Ecosystem | npm |
| CVE / GHSA | CVE-2026-73643 |
| CWE | unknown |
Attack Scenario
Consider a service that accepts YAML configuration from external sources:
const yaml = require('js-yaml');
app.post('/config', (req, res) => {
const config = yaml.load(req.body);
processConfig(config);
res.send('Config updated');
});
An attacker sends a POST request with a deeply nested flow collection:
[[[[[[[[[[[[[[[[[[[[[
[[[[[[[[[[[[[[[[[[[[[
[true]
]]]]]]]]]]]]]]]]]]]
]]]]]]]]]]]]]]]]]]]
The parser begins evaluating the outermost bracket, then recursively enters each nested level. At each depth, the algorithm re-evaluates previous levels to resolve the structure. With 20 levels of nesting, the parser performs exponentially increasing amounts of work, consuming CPU until the request handler times out or the process crashes. The attacker achieves denial of service without submitting a gigabyte of data—just a few kilobytes of carefully nested YAML.
The Technical Details
The vulnerability stems from how the parser resolves flow collection delimiters. Each opening bracket or brace triggers a recursive descent. In 5.2.1, the parser lacked optimization to memoize or short-circuit redundant evaluations at each recursion level. This means a 30-level nested flow collection doesn't just cost 30× work—it costs 2³⁰ work, or roughly one billion operations, even though the structure itself is trivial.
Services that parse untrusted YAML are particularly at risk:
- Config management systems accepting user-supplied configuration
- API gateways that forward YAML payloads
- Log aggregators ingesting YAML-formatted events
- CI/CD pipelines processing YAML job definitions
Any of these could be stalled by a single malicious request.
The Fix
js-yaml 5.2.2 optimizes the flow collection parser to avoid redundant recursion. The fix modifies the internal parsing state machine to recognize and efficiently handle deeply nested structures without re-evaluating ancestor levels.
The dependency upgrade in agent-core/package.json enforces this fix:
- "js-yaml": "^5.2.1",
+ "js-yaml": "^5.2.2",
The lockfile is updated to pull the patched version:
"node_modules/js-yaml": {
- "version": "5.2.1",
+ "version": "5.2.2",
This ensures that the next npm install in agent-core will fetch js-yaml 5.2.2, which contains the optimized parser. Existing deployments running 5.2.1 should update their dependencies immediately.
How the Optimization Works
The 5.2.2 release doesn't change the YAML specification or the public API—it changes only the internal algorithm. The parser now uses a single-pass state machine for flow collections instead of recursive re-evaluation. For a 20-level nested structure, parsing time drops from exponential to linear.
Legitimate YAML documents benefit from this too. Configuration files with moderate nesting (5–10 levels) parse faster. Deeply nested legitimate structures (rare in practice) now parse instead of timing out.
How Orbis AppSec Detected This
Source: YAML documents supplied via yaml.load() or yaml.parse() on untrusted input—HTTP request bodies, uploaded files, API payloads, or any external data source.
Sink: The flow collection parser's recursive descent function within js-yaml, invoked whenever the parser encounters [ or { characters.
Missing control: js-yaml 5.2.1 lacked algorithmic safeguards against nested flow collections. There was no recursion depth limit, no parser state memoization, and no early-exit heuristics for exponential patterns.
CWE: unknown; this is an algorithmic complexity issue (related to CWE-407 "Inefficient Algorithm") rather than a memory corruption or traditional injection vulnerability.
Fix: Upgrade js-yaml to 5.2.2, which implements an optimized single-pass parser for flow collections.
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Key Takeaways
-
Deeply nested flow collections in YAML can cause exponential parsing time: js-yaml 5.2.1 re-evaluates ancestor levels during recursion, making nesting depth a direct multiplier of computation. Always validate nesting depth or use version 5.2.2+.
-
Denial of service doesn't require large payloads: A few kilobytes of nested YAML crashed the parser—less data than a typical HTTP request. Monitor for slow or hanging requests even when input size is small.
-
Algorithmic vulnerabilities are as real as buffer overflows: This wasn't memory corruption or code injection; it was an algorithmic flaw that spent 2³⁰ cycles processing what should cost 30. Auditing algorithms is as important as auditing bounds checking.
-
Lock in dependency versions in production: agent-core's lockfile ensures reproducible builds. Updating the lockfile from 5.2.1 to 5.2.2 is a low-risk fix—same major.minor, same public API, just safer internals.
-
Untrusted YAML is particularly risky: If your service parses YAML from users or external systems, patch immediately. YAML parsing is a high-value attack surface because the format is expressive and the parser is complex.
Conclusion
CVE-2026-73643 demonstrates that not all denial-of-service vulnerabilities come from infinite loops or memory exhaustion—they can come from algorithmic complexity. js-yaml 5.2.1's exponential parsing behavior on nested flow collections allowed attackers to crash services with minimal input. The fix in 5.2.2 optimizes the parser to handle these structures efficiently, and upgrading is straightforward: a single version bump in your dependency manifest. If you use js-yaml in any capacity that touches untrusted YAML, update to 5.2.2 or later now.