Skip to content

Commit

Permalink
Address more gzip, tar, and tar.gz edge cases
Browse files Browse the repository at this point in the history
Signed-off-by: egibs <20933572+egibs@users.noreply.github.com>
  • Loading branch information
egibs committed Dec 20, 2024
1 parent 6e326a4 commit 0acf3ec
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 17 deletions.
4 changes: 2 additions & 2 deletions pkg/archive/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ func ExtractionMethod(ext string) func(context.Context, string, string) error {
switch ext {
case ".jar", ".zip", ".whl":
return ExtractZip
case ".gz":
return ExtractGzip
case ".apk", ".gem", ".tar", ".tar.bz2", ".tar.gz", ".tgz", ".tar.xz", ".tbz", ".xz":
return ExtractTar
case ".gz":
return ExtractGzip
case ".bz2", ".bzip2":
return ExtractBz2
case ".rpm":
Expand Down
14 changes: 7 additions & 7 deletions pkg/archive/deb.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,21 @@ func ExtractDeb(ctx context.Context, d, f string) error {
return fmt.Errorf("failed to close file: %w", err)
}
case tar.TypeSymlink:
// Skip symlinks for targets that do not exist
_, err = os.Readlink(target)
// Ensure that symlinks are not relative path traversals
// #nosec G305 // L88 handles the check
fullLink := filepath.Join(d, header.Linkname)
_, err = os.Lstat(fullLink)
if os.IsNotExist(err) {
continue
}
// Ensure that symlinks are not relative path traversals
// #nosec G305 // L208 handles the check
linkReal, err := filepath.EvalSymlinks(filepath.Join(d, header.Linkname))
linkReal, err := filepath.EvalSymlinks(fullLink)
if err != nil {
return fmt.Errorf("failed to evaluate symlink: %w", err)
}
if !IsValidPath(linkReal, d) {
if !IsValidPath(target, d) {
return fmt.Errorf("symlink points outside temporary directory: %s", linkReal)
}
if err := os.Symlink(linkReal, target); err != nil {
if err := os.Symlink(header.Linkname, target); err != nil {
return fmt.Errorf("failed to create symlink: %w", err)
}
}
Expand Down
13 changes: 13 additions & 0 deletions pkg/archive/gzip.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,23 @@ import (
"path/filepath"

"github.com/chainguard-dev/clog"
"github.com/chainguard-dev/malcontent/pkg/programkind"
)

// extractGzip extracts .gz archives.
func ExtractGzip(ctx context.Context, d string, f string) error {
// Check whether the provided file is a valid gzip archive
var isGzip bool
if ft, err := programkind.File(f); err == nil && ft != nil {
if ft.MIME == "application/gzip" {
isGzip = true
}
}

if !isGzip {
return fmt.Errorf("not a valid gzip archive")
}

logger := clog.FromContext(ctx).With("dir", d, "file", f)
logger.Debug("extracting gzip")

Expand Down
27 changes: 19 additions & 8 deletions pkg/archive/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ import (
"strings"

"github.com/chainguard-dev/clog"
"github.com/chainguard-dev/malcontent/pkg/programkind"
"github.com/ulikunitz/xz"
)

// extractTar extracts .apk and .tar* archives.
//
//nolint:cyclop // ignore complexity of 39
func ExtractTar(ctx context.Context, d string, f string) error {
logger := clog.FromContext(ctx).With("dir", d, "file", f)
logger.Debug("extracting tar")
Expand All @@ -33,16 +36,24 @@ func ExtractTar(ctx context.Context, d string, f string) error {
return fmt.Errorf("failed to open file: %w", err)
}
defer tf.Close()

isTGZ := strings.Contains(f, ".tar.gz") || strings.Contains(f, ".tgz")
var isGzip bool
if ft, err := programkind.File(f); err == nil && ft != nil {
if ft.MIME == "application/gzip" {
isGzip = true
}
}

// Set offset to the file origin regardless of type
_, err = tf.Seek(0, io.SeekStart)
if err != nil {
return fmt.Errorf("failed to seek to start: %w", err)
}

var tr *tar.Reader

switch {
case strings.Contains(f, ".apk") || strings.Contains(f, ".tar.gz") || strings.Contains(f, ".tgz"):
case strings.Contains(f, ".apk") || (isTGZ && isGzip):
gzStream, err := gzip.NewReader(tf)
if err != nil {
return fmt.Errorf("failed to create gzip reader: %w", err)
Expand Down Expand Up @@ -133,21 +144,21 @@ func ExtractTar(ctx context.Context, d string, f string) error {
return fmt.Errorf("failed to close file: %w", err)
}
case tar.TypeSymlink:
// Skip symlinks for targets that do not exist
_, err = os.Readlink(target)
// Ensure that symlinks are not relative path traversals
// #nosec G305 // L158 handles the check
fullLink := filepath.Join(d, header.Linkname)
_, err = os.Lstat(fullLink)
if os.IsNotExist(err) {
continue
}
// Ensure that symlinks are not relative path traversals
// #nosec G305 // L208 handles the check
linkReal, err := filepath.EvalSymlinks(filepath.Join(d, header.Linkname))
linkReal, err := filepath.EvalSymlinks(fullLink)
if err != nil {
return fmt.Errorf("failed to evaluate symlink: %w", err)
}
if !IsValidPath(target, d) {
return fmt.Errorf("symlink points outside temporary directory: %s", linkReal)
}
if err := os.Symlink(linkReal, target); err != nil {
if err := os.Symlink(header.Linkname, target); err != nil {
return fmt.Errorf("failed to create symlink: %w", err)
}
}
Expand Down

0 comments on commit 0acf3ec

Please sign in to comment.