diff --git a/pkg/tar/untar.go b/pkg/tar/untar.go index 5cf746994d6b..6b3a20052d13 100644 --- a/pkg/tar/untar.go +++ b/pkg/tar/untar.go @@ -2,8 +2,10 @@ package tar import ( "archive/tar" + "fmt" "io" "os" + "strings" ) func UntarFile(tarFile, dstFolder string) error { @@ -32,6 +34,12 @@ func Untar(source io.Reader, router Router) error { continue } + // Prevent malicous directory traversals. + // https://cwe.mitre.org/data/definitions/22.html + if strings.Contains(header.Name, "..") { + return fmt.Errorf("file in tarball contains a directory traversal component (..): %v", header.Name) + } + info := header.FileInfo() if info.IsDir() { if err = os.MkdirAll(path, info.Mode()); err != nil { diff --git a/pkg/tar/untar_test.go b/pkg/tar/untar_test.go index b23f143f9fd2..c6afa894f30d 100644 --- a/pkg/tar/untar_test.go +++ b/pkg/tar/untar_test.go @@ -1,6 +1,10 @@ package tar_test import ( + stdtar "archive/tar" + "bytes" + "io" + "io/fs" "os" "path/filepath" "testing" @@ -30,3 +34,43 @@ func TestUntarFile(t *testing.T) { g.Expect(filepath.Join(untarFolder, "dummy3")).To(BeADirectory()) g.Expect(filepath.Join(untarFolder, "dummy3", "dummy4")).To(BeARegularFile()) } + +func TestUntarFile_DirTraversalComponents(t *testing.T) { + // This test ensures Untar fails when a tarball contains paths with directory traversal + // components. It addresses https://cwe.mitre.org/data/definitions/22.html. + g := NewWithT(t) + + dir := t.TempDir() + tarPath := filepath.Join(dir, "test") + fh, err := os.Create(tarPath) + g.Expect(err).To(Succeed()) + + createArbitraryTarball(t, fh) + + g.Expect(tar.UntarFile(tarPath, dir)).ToNot(Succeed()) +} + +func createArbitraryTarball(t *testing.T, w io.Writer) { + t.Helper() + + tb := stdtar.NewWriter(w) + + data := bytes.NewBufferString("Hello, world!") + header := stdtar.Header{ + Name: "../foobar", + Mode: int64(fs.ModePerm), + Typeflag: stdtar.TypeReg, + Size: int64(data.Len()), + } + + if err := tb.WriteHeader(&header); err != nil { + t.Fatal(err) + } + + _, err := io.Copy(tb, data) + if err != nil { + t.Fatal(err) + } + + tb.Close() +}