From 5fb93cceaf76827ded9fd67938f74e23c6511637 Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Tue, 18 Aug 2026 09:49:58 +0200 Subject: [PATCH] prevent again security warning about 'Zip Slip' in RPI camera downloader (#6108) --- .../rpicamera/mtxrpicamdownloader/main.go | 23 +++++--- .../mtxrpicamdownloader/main_test.go | 54 +++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 internal/staticsources/rpicamera/mtxrpicamdownloader/main_test.go diff --git a/internal/staticsources/rpicamera/mtxrpicamdownloader/main.go b/internal/staticsources/rpicamera/mtxrpicamdownloader/main.go index 4a5e9830..5f522777 100644 --- a/internal/staticsources/rpicamera/mtxrpicamdownloader/main.go +++ b/internal/staticsources/rpicamera/mtxrpicamdownloader/main.go @@ -21,13 +21,18 @@ const ( maxInboundRPICameraSize = 10 * 1024 * 1024 ) -func safeArchivePath(name string) (string, error) { +func safeArchivePath(baseDir string, name string) (string, error) { name = filepath.Clean(filepath.FromSlash(name)) - if filepath.IsAbs(name) || name == ".." || strings.HasPrefix(name, ".."+string(filepath.Separator)) { + if name == "." || !filepath.IsLocal(name) { return "", fmt.Errorf("invalid archive entry: %s", name) } - return name, nil + targetPath := filepath.Join(baseDir, name) + if targetPath != baseDir && !strings.HasPrefix(targetPath, baseDir+string(filepath.Separator)) { + return "", fmt.Errorf("invalid archive entry: %s", name) + } + + return targetPath, nil } func dumpTar(src io.Reader) error { @@ -36,7 +41,11 @@ func dumpTar(src io.Reader) error { return err } - baseDir := "." + baseDir, err := filepath.Abs(".") + if err != nil { + return err + } + tr := tar.NewReader(uncompressed) for { @@ -49,14 +58,12 @@ func dumpTar(src io.Reader) error { return err } - var entryName string - entryName, err = safeArchivePath(header.Name) + var targetPath string + targetPath, err = safeArchivePath(baseDir, header.Name) if err != nil { return err } - targetPath := filepath.Join(baseDir, entryName) - switch header.Typeflag { case tar.TypeDir: err = os.Mkdir(targetPath, header.FileInfo().Mode()) diff --git a/internal/staticsources/rpicamera/mtxrpicamdownloader/main_test.go b/internal/staticsources/rpicamera/mtxrpicamdownloader/main_test.go new file mode 100644 index 00000000..bb2f7409 --- /dev/null +++ b/internal/staticsources/rpicamera/mtxrpicamdownloader/main_test.go @@ -0,0 +1,54 @@ +package main + +import ( + "path/filepath" + "testing" +) + +func TestSafeArchivePath(t *testing.T) { + baseDir := filepath.Clean(string(filepath.Separator) + filepath.Join("tmp", "base")) + + for _, ca := range []struct { + name string + path string + ok bool + result string + }{ + { + name: "simple file", + path: "dir/file", + ok: true, + result: filepath.Join(baseDir, "dir", "file"), + }, + { + name: "parent traversal", + path: "../file", + }, + { + name: "absolute path", + path: "/etc/passwd", + }, + { + name: "empty path", + path: "", + }, + { + name: "dot path", + path: ".", + }, + } { + t.Run(ca.name, func(t *testing.T) { + res, err := safeArchivePath(baseDir, ca.path) + if ca.ok { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if res != ca.result { + t.Fatalf("unexpected result: got %q, want %q", res, ca.result) + } + } else if err == nil { + t.Fatalf("expected error") + } + }) + } +}