From a9047ddcb1d5b9e435dd9042e3b9224b0f422a15 Mon Sep 17 00:00:00 2001 From: clawbot Date: Sun, 8 Feb 2026 16:10:10 -0800 Subject: [PATCH] Add decompression size limit in deserializeInner() Wrap the zstd decompressor with io.LimitReader to prevent decompression bombs. Default limit is 256MB (MaxDecompressedSize). Closes #24 --- mfer/constants.go | 5 +++++ mfer/deserialize.go | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mfer/constants.go b/mfer/constants.go index f38c74a..4640637 100644 --- a/mfer/constants.go +++ b/mfer/constants.go @@ -3,4 +3,9 @@ package mfer const ( Version = "0.1.0" ReleaseDate = "2025-12-17" + + // MaxDecompressedSize is the maximum allowed size of decompressed manifest + // data (256 MB). This prevents decompression bombs from consuming excessive + // memory. + MaxDecompressedSize int64 = 256 * 1024 * 1024 ) diff --git a/mfer/deserialize.go b/mfer/deserialize.go index 76a8655..878bf8f 100644 --- a/mfer/deserialize.go +++ b/mfer/deserialize.go @@ -76,10 +76,20 @@ func (m *manifest) deserializeInner() error { } defer zr.Close() - dat, err := io.ReadAll(zr) + // Limit decompressed size to prevent decompression bombs. + // Use declared size + 1 byte to detect overflow, capped at MaxDecompressedSize. + maxSize := MaxDecompressedSize + if m.pbOuter.Size > 0 && m.pbOuter.Size < int64(maxSize) { + maxSize = int64(m.pbOuter.Size) + 1 + } + limitedReader := io.LimitReader(zr, maxSize) + dat, err := io.ReadAll(limitedReader) if err != nil { return err } + if int64(len(dat)) >= MaxDecompressedSize { + return fmt.Errorf("decompressed data exceeds maximum allowed size of %d bytes", MaxDecompressedSize) + } isize := len(dat) if int64(isize) != m.pbOuter.Size {