package parser import ( "archive/tar" "bytes" "compress/gzip" "fmt" "os" "path/filepath" "strings" "testing" ) func TestExtractArchiveFromReaderTXT(t *testing.T) { content := "loader_brand=\"XigmaNAS\"\nSystem uptime:\n" files, err := ExtractArchiveFromReader(strings.NewReader(content), "xigmanas.txt") if err != nil { t.Fatalf("extract txt from reader: %v", err) } if len(files) != 1 { t.Fatalf("expected 1 file, got %d", len(files)) } if files[0].Path != "xigmanas.txt" { t.Fatalf("expected filename xigmanas.txt, got %q", files[0].Path) } if string(files[0].Content) != content { t.Fatalf("content mismatch") } } func TestArchiveFileSelectionHelpers(t *testing.T) { extensions := SupportedArchiveExtensions() if len(extensions) == 0 || extensions[0] != ".ahs" { t.Fatalf("unexpected supported extensions: %v", extensions) } if !IsSupportedArchiveFilename("report.TGZ") || IsSupportedArchiveFilename("report.bin") { t.Fatal("unexpected supported archive detection") } files := []ExtractedFile{{Path: "logs/System.LOG"}, {Path: "config.json"}, {Path: "nested/event.log"}} if got := FindFileByPattern(files, "log"); len(got) != 2 { t.Fatalf("FindFileByPattern = %+v", got) } if got := FindFileByName(files, "EVENT.LOG"); got == nil || got.Path != "nested/event.log" { t.Fatalf("FindFileByName = %+v", got) } if got := FindFileByName(files, "missing"); got != nil { t.Fatalf("expected missing file, got %+v", got) } } func TestExtractArchiveTXT(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "sample.txt") want := "plain text log" if err := os.WriteFile(path, []byte(want), 0o600); err != nil { t.Fatalf("write sample txt: %v", err) } files, err := ExtractArchive(path) if err != nil { t.Fatalf("extract txt file: %v", err) } if len(files) != 1 { t.Fatalf("expected 1 file, got %d", len(files)) } if files[0].Path != "sample.txt" { t.Fatalf("expected sample.txt, got %q", files[0].Path) } if string(files[0].Content) != want { t.Fatalf("content mismatch") } } func TestExtractArchiveFromReaderTXT_TruncatedWhenTooLarge(t *testing.T) { large := bytes.Repeat([]byte("a"), maxSingleFileSize+1024) files, err := ExtractArchiveFromReader(bytes.NewReader(large), "huge.log") if err != nil { t.Fatalf("extract huge txt 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 to be marked as truncated") } if got := len(f.Content); got != maxSingleFileSize { t.Fatalf("expected truncated size %d, got %d", maxSingleFileSize, got) } if f.TruncatedMessage == "" { t.Fatalf("expected truncation message") } } // 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 want bool }{ {name: "HPE_CZ2D1X0GS3_20260330.ahs", want: true}, {name: "dump.tar.gz", want: true}, {name: "nvidia-bug-report-1651124000923.log.gz", want: true}, {name: "snapshot.zip", want: true}, {name: "h3c_20250819.sds", want: true}, {name: "report.log", want: true}, {name: "xigmanas.txt", want: true}, {name: "raw_export.json", want: false}, {name: "archive.bin", want: false}, } for _, tc := range cases { got := IsSupportedArchiveFilename(tc.name) if got != tc.want { t.Fatalf("IsSupportedArchiveFilename(%q)=%v, want %v", tc.name, got, tc.want) } } } func TestExtractArchiveFromReaderSDS(t *testing.T) { var buf bytes.Buffer tw := tar.NewWriter(&buf) payload := []byte("STARTTIME:0\nENDTIME:0\n") if err := tw.WriteHeader(&tar.Header{ Name: "bmc/pack.info", Mode: 0o600, Size: int64(len(payload)), }); err != nil { t.Fatalf("write tar header: %v", err) } if _, err := tw.Write(payload); err != nil { t.Fatalf("write tar payload: %v", err) } if err := tw.Close(); err != nil { t.Fatalf("close tar writer: %v", err) } files, err := ExtractArchiveFromReader(bytes.NewReader(buf.Bytes()), "sample.sds") if err != nil { t.Fatalf("extract sds from reader: %v", err) } if len(files) != 1 { t.Fatalf("expected 1 extracted file, got %d", len(files)) } if files[0].Path != "bmc/pack.info" { t.Fatalf("expected bmc/pack.info, got %q", files[0].Path) } } func TestExtractArchiveFromReaderAHS(t *testing.T) { payload := []byte("ABJRtest") files, err := ExtractArchiveFromReader(bytes.NewReader(payload), "sample.ahs") if err != nil { t.Fatalf("extract ahs from reader: %v", err) } if len(files) != 1 { t.Fatalf("expected 1 extracted file, got %d", len(files)) } if files[0].Path != "sample.ahs" { t.Fatalf("expected sample.ahs, got %q", files[0].Path) } if string(files[0].Content) != string(payload) { t.Fatalf("content mismatch") } }