prevent again security warning about 'Zip Slip' in RPI camera downloader (#6108)
This commit is contained in:
@@ -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())
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user