feat(parser): bound single-file gzip by ratio guard instead of fixed byte cap
Plain gzipped logs (nvidia-bug-report-*.log.gz) routinely exceed the old 50MB decompression cap, which silently dropped the tail. Replace it with a decompression-ratio bomb guard plus a 1GB absolute memory ceiling. See ADL-054. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
632067fef5
commit
9d701885da
@@ -3,6 +3,8 @@ package parser
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -71,6 +73,80 @@ func TestExtractArchiveFromReaderTXT_TruncatedWhenTooLarge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestExtractArchiveFromReaderGZ_NoLongerCapsAt50MB is a regression test for
|
||||
// a real nvidia-bug-report-*.log.gz dump (single gzipped log file, not a tar)
|
||||
// whose decompressed content was ~53MB and got silently truncated by the old
|
||||
// fixed 50MB cap, dropping the tail of the log. A single gzipped log with a
|
||||
// realistic (low) compression ratio should now come through whole.
|
||||
func TestExtractArchiveFromReaderGZ_NoLongerCapsAt50MB(t *testing.T) {
|
||||
var plain bytes.Buffer
|
||||
for i := 0; plain.Len() < 60*1024*1024; i++ {
|
||||
fmt.Fprintf(&plain, "Aug 24 13:%02d:%02d avi-hgx-b200-ef01 kernel: some log line %d with varying content xyzxyzxyz\n", i%60, (i*7)%60, i)
|
||||
}
|
||||
want := plain.Len()
|
||||
|
||||
var gz bytes.Buffer
|
||||
gw := gzip.NewWriter(&gz)
|
||||
if _, err := gw.Write(plain.Bytes()); err != nil {
|
||||
t.Fatalf("write gzip content: %v", err)
|
||||
}
|
||||
if err := gw.Close(); err != nil {
|
||||
t.Fatalf("close gzip writer: %v", err)
|
||||
}
|
||||
|
||||
files, err := ExtractArchiveFromReader(bytes.NewReader(gz.Bytes()), "nvidia-bug-report-host.log.gz")
|
||||
if err != nil {
|
||||
t.Fatalf("extract gzip from reader: %v", err)
|
||||
}
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("expected 1 file, got %d", len(files))
|
||||
}
|
||||
|
||||
f := files[0]
|
||||
if f.Truncated {
|
||||
t.Fatalf("expected file NOT to be truncated, got message %q", f.TruncatedMessage)
|
||||
}
|
||||
if len(f.Content) != want {
|
||||
t.Fatalf("expected full %d bytes decompressed, got %d", want, len(f.Content))
|
||||
}
|
||||
}
|
||||
|
||||
// TestExtractArchiveFromReaderGZ_AbortsOnDecompressionBomb verifies the
|
||||
// ratio-based guard that replaced the fixed byte cap still protects against
|
||||
// a pathological gzip bomb (tiny compressed input, huge decompressed output)
|
||||
// without requiring the whole bomb to be decompressed first.
|
||||
func TestExtractArchiveFromReaderGZ_AbortsOnDecompressionBomb(t *testing.T) {
|
||||
zeros := make([]byte, 50*1024*1024)
|
||||
|
||||
var gz bytes.Buffer
|
||||
gw := gzip.NewWriter(&gz)
|
||||
if _, err := gw.Write(zeros); err != nil {
|
||||
t.Fatalf("write gzip content: %v", err)
|
||||
}
|
||||
if err := gw.Close(); err != nil {
|
||||
t.Fatalf("close gzip writer: %v", err)
|
||||
}
|
||||
|
||||
files, err := ExtractArchiveFromReader(bytes.NewReader(gz.Bytes()), "bomb.log.gz")
|
||||
if err != nil {
|
||||
t.Fatalf("extract gzip from reader: %v", err)
|
||||
}
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("expected 1 file, got %d", len(files))
|
||||
}
|
||||
|
||||
f := files[0]
|
||||
if !f.Truncated {
|
||||
t.Fatalf("expected bomb to be caught and file marked truncated")
|
||||
}
|
||||
if f.TruncatedMessage == "" {
|
||||
t.Fatalf("expected a truncation message")
|
||||
}
|
||||
if len(f.Content) >= len(zeros) {
|
||||
t.Fatalf("expected bomb guard to abort well before full %d bytes, got %d", len(zeros), len(f.Content))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSupportedArchiveFilename(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
|
||||
Reference in New Issue
Block a user