Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 32 additions & 11 deletions evetest/utils/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"io"
"os"
"path"
"strings"
)

// MaxDecompressedContentSize is the maximum size of a file that can be written to disk after decompression.
Expand All @@ -18,9 +19,21 @@

// ExtractFromTar extracts files from a tar reader into the destination directory
func ExtractFromTar(u io.Reader, destination string) error {
// path inside tar is relative
pathBuilder := func(oldPath string) string {
return path.Join(destination, oldPath)
cleanDestination := path.Clean(destination)
// path inside tar must remain relative and contained within destination
safePath := func(oldPath string) (string, error) {
if oldPath == "" {
return "", fmt.Errorf("empty archive path")
}

Check failure

Code scanning / CodeQL

Arbitrary file access during archive extraction ("Zip Slip") High

Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
Unsanitized archive entry, which may contain '..', is used in a
file system operation
.
cleanOldPath := path.Clean(oldPath)
if cleanOldPath == "." || strings.HasPrefix(cleanOldPath, "..") || strings.Contains(cleanOldPath, "/..") || path.IsAbs(cleanOldPath) {
return "", fmt.Errorf("invalid archive path: %s", oldPath)
}
fullPath := path.Clean(path.Join(cleanDestination, cleanOldPath))
if fullPath != cleanDestination && !strings.HasPrefix(fullPath, cleanDestination+"/") {
return "", fmt.Errorf("archive path escapes destination: %s", oldPath)
}
return fullPath, nil
}
tarReader := tar.NewReader(u)
for {
Expand All @@ -31,19 +44,23 @@
if err != nil {
return fmt.Errorf("ExtractFromTar: Next() failed: %w", err)
}
entryPath, err := safePath(header.Name)

Check failure

Code scanning / CodeQL

Arbitrary file write extracting an archive containing symbolic links High

Unresolved path from an archive header, which may point outside the archive root, is used in
symlink creation
.
if err != nil {
return fmt.Errorf("ExtractFromTar: invalid entry name %q: %w", header.Name, err)
}
switch header.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(pathBuilder(header.Name), os.FileMode(header.Mode)); err != nil {
if err := os.MkdirAll(entryPath, os.FileMode(header.Mode)); err != nil {
return fmt.Errorf("ExtractFromTar: Mkdir() failed: %w", err)
}
case tar.TypeReg:
if _, err := os.Lstat(pathBuilder(header.Name)); err == nil {
err = os.Remove(pathBuilder(header.Name))
if _, err := os.Lstat(entryPath); err == nil {
err = os.Remove(entryPath)
if err != nil {
return fmt.Errorf("ExtractFromTar: cannot remove old file: %w", err)
}
}
outFile, err := os.OpenFile(pathBuilder(header.Name), os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
outFile, err := os.OpenFile(entryPath, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
if err != nil {
return fmt.Errorf("ExtractFromTar: OpenFile() failed: %w", err)
}
Expand All @@ -60,15 +77,19 @@
return fmt.Errorf("ExtractFromTar: outFile.Close() failed: %w", err)
}
case tar.TypeLink, tar.TypeSymlink:
if _, err := os.Lstat(pathBuilder(header.Name)); err == nil {
err = os.Remove(pathBuilder(header.Name))
if _, err := os.Lstat(entryPath); err == nil {
err = os.Remove(entryPath)
if err != nil {
return fmt.Errorf("ExtractFromTar: cannot remove old symlink: %w", err)
}
}
if err := os.Symlink(pathBuilder(header.Linkname), pathBuilder(header.Name)); err != nil {
linkPath, err := safePath(header.Linkname)

Check failure

Code scanning / CodeQL

Arbitrary file write extracting an archive containing symbolic links High

Unresolved path from an archive header, which may point outside the archive root, is used in
symlink creation
.
if err != nil {
return fmt.Errorf("ExtractFromTar: invalid link name %q: %w", header.Linkname, err)
}
if err := os.Symlink(linkPath, entryPath); err != nil {
return fmt.Errorf("ExtractFromTar: Symlink(%s, %s) failed: %w",
pathBuilder(header.Name), pathBuilder(header.Linkname), err)
entryPath, linkPath, err)
}
default:
return fmt.Errorf(
Expand Down
Loading