//go:build linux || freebsd // +build linux freebsd package archive import ( "bytes" "testing" ) // FuzzTarExtract tests tar extraction with fuzzed input to catch path traversal // and other security issues in header parsing. func FuzzTarExtract(f *testing.F) { // Seed corpus with valid tar data f.Add([]byte{}) // Minimal valid tar header (512 bytes of zeros) emptyHeader := make([]byte, 512) f.Add(emptyHeader) // Tar file with a ".." path traversal attempt traversalTar := makeTarWithName("../../../etc/passwd") f.Add(traversalTar) // Tar file with absolute path absTar := makeTarWithName("/etc/passwd") f.Add(absTar) // Tar file with symlink pointing outside symlinkTar := makeSymlinkTar("link", "../../../etc/passwd") f.Add(symlinkTar) f.Fuzz(func(t *testing.T, data []byte) { // Fuzz the tar extraction — it must never panic or escape the temp dir dir := t.TempDir() ext := NewTarExtractor() reader := bytes.NewReader(data) // Ignore errors — we only care about panics and path escapes _ = ext.Extract(reader, dir) }) } func makeTarWithName(name string) []byte { // Create a minimal tar file with a regular file at the given path var buf bytes.Buffer nameBytes := []byte(name) if len(nameBytes) > 99 { nameBytes = nameBytes[:99] } header := make([]byte, 512) copy(header[0:99], nameBytes) // Set type flag to regular file header[156] = '0' // Set size to zero buf.Write(header) // Two zero blocks to mark end of archive buf.Write(make([]byte, 1024)) return buf.Bytes() } func makeSymlinkTar(name, target string) []byte { var buf bytes.Buffer nameBytes := []byte(name) if len(nameBytes) > 99 { nameBytes = nameBytes[:99] } header := make([]byte, 512) copy(header[0:99], nameBytes) // Set type flag to symlink header[156] = '2' // Write link target targetBytes := []byte(target) if len(targetBytes) > 99 { targetBytes = targetBytes[:99] } copy(header[157:256], targetBytes) buf.Write(header) buf.Write(make([]byte, 1024)) return buf.Bytes() }