{"record":{"id":"5983c8801e0b1ed6","repo":"anomalyco/sst","slug":"illegal-file-path-in-tar-s","errorCode":null,"errorMessage":"illegal file path in tar: %s","messagePattern":"illegal file path in tar: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/runtime/python/build.go","lineNumber":424,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create gzip reader: %w\", err)\n\t}\n\tdefer gz.Close()\n\n\ttr := tar.NewReader(gz)\n\tfor {\n\t\thdr, err := tr.Next()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to read tar entry: %w\", err)\n\t\t}\n\n\t\ttarget := filepath.Join(destDir, hdr.Name)\n\t\t// Guard against tar slip\n\t\tif !strings.HasPrefix(filepath.Clean(target), filepath.Clean(destDir)+string(os.PathSeparator)) {\n\t\t\treturn fmt.Errorf(\"illegal file path in tar: %s\", hdr.Name)\n\t\t}\n\n\t\tswitch hdr.Typeflag {\n\t\tcase tar.TypeDir:\n\t\t\tif err := os.MkdirAll(target, 0755); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase tar.TypeReg:\n\t\t\tif err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tout, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(hdr.Mode))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err := io.Copy(out, tr); err != nil {\n\t\t\t\tout.Close()\n\t\t\t\treturn err","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/anomalyco/sst/blob/a0bd20f762883e72a35caccb4896c42ce5b3f707/pkg/runtime/python/build.go#L406-L442","documentation":"extractTarGz guards against path traversal (\"tar slip\") by checking that each entry's joined target path stays inside destDir after cleaning. If hdr.Name escapes the destination (e.g. starts with ../ or is an absolute path), extraction aborts with \"illegal file path in tar\". This is an intentional security check, not an environment problem.","triggerScenarios":"The archive contains entry names like \"../../../etc/passwd\", absolute paths (\"/home/user/...\") or symlink tricks whose cleaned path does not have destDir as prefix.","commonSituations":"A maliciously crafted or typo'd package on an internal index; an archive built on another OS with absolute paths embedded; archives generated with a tool that emits leading \"./../\" segments; running unvetted sdists in CI.","solutions":["Do not extract the archive — treat it as untrusted and remove it","Only install packages from trusted sources/verified hashes (pip --require-hashes)","Inspect the archive contents (tar -tzvf) to find the offending entry name","Rebuild/repackage the archive with relative, normalized entry paths if you own it","Pin known-good package versions so compromised uploads are not picked up"],"exampleFix":"// before: blindly extracting an untrusted archive\nextractTarGz(archiveFile, destDir)\n\n// after: pre-screen entries for traversal attempts\nbad := exec.Command(\"sh\", \"-c\", fmt.Sprintf(\"tar -tzf %s | grep -E '(^/|\\.\\./)'\", archiveFile))\nif err := bad.Run(); err == nil {\n    return fmt.Errorf(\"archive %s contains path traversal entries\", archiveFile)\n}\nextractTarGz(archiveFile, destDir)","handlingStrategy":"validation","validationCode":"out, err := exec.Command(\"tar\", \"-tzf\", archiveFile).Output()\nif err != nil {\n    return err\n}\nfor _, name := range strings.Split(strings.TrimSpace(string(out)), \"\\n\") {\n    cleaned := filepath.Clean(name)\n    if strings.HasPrefix(cleaned, \"../\") || filepath.IsAbs(cleaned) {\n        return fmt.Errorf(\"unsafe entry %q in archive\", name)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := extractTarGz(archiveFile, destDir); err != nil {\n    if strings.Contains(err.Error(), \"illegal file path in tar\") {\n        // treat archive as malicious: quarantine it, never retry blindly\n        os.Remove(archiveFile)\n        return fmt.Errorf(\"refusing to install untrusted archive: %w\", err)\n    }\n    return err\n}","preventionTips":["Only install packages from trusted registries","Use hash pinning (--require-hashes / lockfiles) to guarantee artifact identity","Pre-screen archive entry names for ../ and absolute paths","Keep the tar-slip guard in place; never bypass it for convenience"],"tags":["security","tar-slip","path-traversal","archive"],"backgroundTag":"path-traversal-detected","analyzedSha":"a0bd20f762883e72a35caccb4896c42ce5b3f707","analyzedAt":"2026-08-30T11:26:00.383Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}