Add more check for file extraction from tarball.

Signed-off-by: Xun Jiang <xun.jiang@broadcom.com>
This commit is contained in:
Xun Jiang
2026-03-13 11:24:09 +08:00
parent 66ac235e1f
commit b1ddf70795
3 changed files with 38 additions and 1 deletions

View File

@@ -0,0 +1 @@
Add check for file extraction from tarball.

View File

@@ -19,8 +19,10 @@ package archive
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"path/filepath"
"strings"
"github.com/sirupsen/logrus"
@@ -84,7 +86,15 @@ func (e *Extractor) readBackup(tarRdr *tar.Reader) (string, error) {
return "", err
}
target := filepath.Join(dir, header.Name) //nolint:gosec // Internal usage. No need to check.
target := filepath.Join(dir, header.Name)
relPath, err := filepath.Rel(filepath.Clean(dir), filepath.Clean(target))
if err != nil {
e.log.Infof("error validating extraction target %q: %v", header.Name, err)
return "", err
}
if relPath == ".." || strings.HasPrefix(relPath, ".."+string(filepath.Separator)) {
return "", fmt.Errorf("invalid archive path %q: escapes target directory", header.Name)
}
switch header.Typeflag {
case tar.TypeDir:

View File

@@ -18,6 +18,7 @@ package archive
import (
"archive/tar"
"bytes"
"compress/gzip"
"io"
"os"
@@ -87,6 +88,31 @@ func TestUnzipAndExtractBackup(t *testing.T) {
}
}
func TestUnzipAndExtractBackupRejectsPathTraversal(t *testing.T) {
ext := NewExtractor(test.NewLogger(), test.NewFakeFileSystem())
var buf bytes.Buffer
gzw := gzip.NewWriter(&buf)
tw := tar.NewWriter(gzw)
err := tw.WriteHeader(&tar.Header{
Name: "../escape.txt",
Mode: 0600,
Typeflag: tar.TypeReg,
Size: int64(len("data")),
})
require.NoError(t, err)
_, err = tw.Write([]byte("data"))
require.NoError(t, err)
require.NoError(t, tw.Close())
require.NoError(t, gzw.Close())
_, err = ext.UnzipAndExtractBackup(&buf)
require.Error(t, err)
require.Contains(t, err.Error(), "invalid archive path")
}
func createArchive(files []string, fs filesystem.Interface) (string, error) {
outName := "output.tar.gz"
out, err := fs.Create(outName)