@@ -100,9 +100,26 @@ v2-superset work must also satisfy.
100100
101101---
102102
103- ## OPEN — SEC-001: decompression bomb — no decode-size / frame-count cap → OOM DoS from a tiny hostile capture
104-
105- ** Severity:** HIGH (DoS). Easiest crash to trigger from a crafted file. Status: open.
103+ ## FIXED — SEC-001: decompression bomb — no decode-size / frame-count cap → OOM DoS from a tiny hostile capture
104+
105+ ** Severity:** HIGH (DoS). Easiest crash to trigger from a crafted file.
106+ ** Status: FIXED** in ` 545f3b6 ` (branch ` fix/sec-decompression-guards ` ).
107+ All capture readers (` Reader ` , ` LegacyReader ` , ` EchoReplay ` ) now enforce
108+ documented, configurable budgets (` pkg/codec/limits.go ` ): total decoded bytes
109+ (default 8 GiB) and frame count (default 10M), overridable/disableable per
110+ reader via ` WithMaxDecodedBytes ` / ` WithMaxFrameCount ` / ` WithoutLimits ` .
111+ ` zstd.WithDecoderMaxMemory ` is set from the budget on both decoder open sites;
112+ accumulation sites (` OpenSession ` , ` NewSessionReconstructor ` , ` ReadFrames ` )
113+ enforce the reader's frame budget again. Past budget: clear error wrapping
114+ ` ErrMaxDecodedBytes ` / ` ErrMaxFrameCount ` .
115+ Tests: ` TestReader_SEC001_FrameCountBudget ` , ` TestReader_SEC001_DecodedBytesBudget ` ,
116+ ` TestReader_SEC001_DefaultLimitsApplied ` , ` TestReader_SEC001_WithoutLimitsOptOut ` ,
117+ ` TestLegacyReader_SEC001_FrameCountBudget ` , ` TestLegacyReader_SEC001_DecodedBytesBudget ` ,
118+ ` TestEchoReplay_SEC001_FrameCountBudget ` , ` TestEchoReplay_SEC001_DecodedBytesBudget `
119+ (` pkg/codec/sec001_bomb_test.go ` ); ` TestOpenSession_SEC001_FrameBudget ` ,
120+ ` TestNewSessionReconstructor_SEC001_FrameBudget ` ,
121+ ` TestOpenSession_SEC001_DecodedBytesBudget ` (` pkg/conversion/sec001_budget_test.go ` ).
122+ Fuzz: ` FuzzReadEnvelope ` , ` FuzzReadDelimitedMessage ` (` e547216 ` ).
106123
107124** What:** Both tape decoders wrap the input in a Zstd stream reader with ** no**
108125memory or decoded-size guard, and the streaming APIs that most callers use
@@ -140,9 +157,19 @@ an error past the budget. Budget should be a documented, non-hardcoded limit.
140157
141158---
142159
143- ## OPEN — SEC-002: 256 MB single allocation from a ~ 5-byte length prefix (allocate-before-verify)
144-
145- ** Severity:** MEDIUM (memory-spike DoS; amplifies SEC-001). Status: open.
160+ ## FIXED — SEC-002: 256 MB single allocation from a ~ 5-byte length prefix (allocate-before-verify)
161+
162+ ** Severity:** MEDIUM (memory-spike DoS; amplifies SEC-001).
163+ ** Status: FIXED** in ` aa9f215 ` (branch ` fix/sec-decompression-guards ` ).
164+ Both readers now use shared ` readMessageBody ` (` pkg/codec/limits.go ` ): at most
165+ 1 MiB (` maxEagerMessageAlloc ` , cited against measured sample message sizes) is
166+ allocated before any bytes arrive; larger messages grow the buffer only as
167+ bytes are actually read, so a truncated giant length prefix costs a bounded
168+ allocation plus a clean error. Measured: 268,445,480 bytes allocated before the
169+ fix from a 5-byte stream; under the 64 MiB test threshold after.
170+ Tests: ` TestReader_SEC002_TruncatedGiantLengthPrefix ` ,
171+ ` TestLegacyReader_SEC002_TruncatedGiantLengthPrefix `
172+ (` pkg/codec/sec002_alloc_test.go ` ).
146173
147174** What:** The length-delimited readers allocate the full declared message buffer
148175** before** confirming that many bytes are actually available in the stream. The
0 commit comments