diff --git a/CHANGELOG.md b/CHANGELOG.md index 630f0a6..1e721e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -429,6 +429,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Tagged.Dumper` honors `sortKeys` in its block and canonical paths for mappings whose keys are all plain, untagged, unanchored string scalars, matching the model dumper's `orderedPairs` rule. +- `parse`/`parseStreaming` now reject an anchor-expansion ("billion + laughs") bomb instead of accepting it in milliseconds and handing a + caller a value whose logical size is exponential in the alias nesting + depth. `enforceNestingLimit` bounds physical collection depth in the + raw event stream, which does nothing against a handful of shallow, + small anchors each aliased many times with each alias nested inside + the next: a 25-level, fan-out-9 document (~1.5KB) expands past 9^25 + logical nodes while raw event depth stays at 2. `EventStreamComposer` + now charges a total expanded-node budget incrementally as the document + streams through -- every scalar, alias resolution, and completed + container charges its own marginal cost, with each anchor's expanded + size cached at definition and looked up in O(1) when later aliased -- + so a bomb is rejected within a few alias levels without ever + materializing or walking the exponential tree it would otherwise + produce. A document with genuinely wide but non-exponential alias + reuse (the same anchor referenced thousands of times at one level, no + nesting) still parses normally. ## [0.2.0] - 2026-07-05