feat: initial recursive archive cataloging impl

Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
This commit is contained in:
Keith Zantow
2026-09-10 08:45:36 -04:00
parent 0a93e60b16
commit 8c0d3747ce
85 changed files with 23477 additions and 868 deletions
+16 -4
View File
@@ -101,7 +101,7 @@ func ExtractFromZipToUniqueTempFile(ctx context.Context, archivePath, dir string
return fmt.Errorf("unable to extract directories, only files: %s", file.NameInArchive)
}
if err := safeCopy(tempFile, zippedFile); err != nil {
if err := SafeCopy(tempFile, zippedFile); err != nil {
return fmt.Errorf("unable to copy source=%q for zip=%q: %w", file.NameInArchive, archivePath, err)
}
@@ -138,7 +138,7 @@ func ContentsFromZip(ctx context.Context, archivePath string, paths ...string) (
}
var buffer bytes.Buffer
if err := safeCopy(&buffer, zippedFile); err != nil {
if err := SafeCopy(&buffer, zippedFile); err != nil {
return fmt.Errorf("unable to copy source=%q for zip=%q: %w", file.NameInArchive, archivePath, err)
}
@@ -165,10 +165,22 @@ func UnzipToDir(ctx context.Context, archivePath, targetDir string) error {
}
// SafeJoin ensures that any destinations do not resolve to a path above the prefix path.
//
// The comparison is on a path boundary rather than on the string prefix. A sibling directory that
// happens to share the prefix as a string is not inside it: under "/tmp/x/contents" the destination
// "../contents-evil/y" cleans to "/tmp/x/contents-evil/y", which starts with the prefix and is
// outside it.
func SafeJoin(prefix string, dest ...string) (string, error) {
joinResult := filepath.Join(append([]string{prefix}, dest...)...)
cleanPrefix := filepath.Clean(prefix)
cleanJoinResult := filepath.Clean(joinResult)
if !strings.HasPrefix(cleanJoinResult, filepath.Clean(prefix)) {
boundary := cleanPrefix
if !strings.HasSuffix(boundary, string(filepath.Separator)) {
boundary += string(filepath.Separator)
}
if cleanJoinResult != cleanPrefix && !strings.HasPrefix(cleanJoinResult, boundary) {
return "", &errZipSlipDetected{
Prefix: prefix,
JoinArgs: dest,
@@ -210,7 +222,7 @@ func extractSingleFile(file archives.FileInfo, expandedFilePath, archivePath str
}
}()
if err := safeCopy(outputFile, zippedFile); err != nil {
if err := SafeCopy(outputFile, zippedFile); err != nil {
return fmt.Errorf("unable to copy source=%q to dest=%q for zip=%q: %w", file.NameInArchive, outputFile.Name(), archivePath, err)
}
}