diff --git a/operations/initialize.go b/operations/initialize.go index 70ab13e..ea939d5 100644 --- a/operations/initialize.go +++ b/operations/initialize.go @@ -168,7 +168,12 @@ func unpack(content []byte, directory string) error { if entry.FileInfo().IsDir() { continue } - target := filepath.Join(directory, entry.Name) + target, err := zipEntryTarget(directory, entry.Name) + if err != nil { + common.Error("zip extract", err) + success = false + continue + } todo := WriteTarget{ Source: entry, Target: target, diff --git a/operations/zipper.go b/operations/zipper.go index 61af423..de16a02 100644 --- a/operations/zipper.go +++ b/operations/zipper.go @@ -47,6 +47,18 @@ func slashed(text string) string { return strings.Replace(text, backslash, slash, -1) } +func zipEntryTarget(directory, entry string) (string, error) { + target := filepath.Join(directory, slashed(entry)) + relative, err := filepath.Rel(directory, target) + if err != nil { + return "", err + } + if relative == ".." || strings.HasPrefix(relative, ".."+string(os.PathSeparator)) { + return "", fmt.Errorf("invalid archive entry path: %q", entry) + } + return target, nil +} + func HololibZipShape(file *zip.File) error { library := libraryPattern.MatchString(file.Name) catalog := catalogPattern.MatchString(file.Name) @@ -161,9 +173,13 @@ func (it *unzipper) Explode(workers int, directory string) error { if entry.FileInfo().IsDir() { continue } + target, err := zipEntryTarget(directory, entry.Name) + if err != nil { + return err + } todo <- &WriteTarget{ Source: entry, - Target: filepath.Join(directory, slashed(entry.Name)), + Target: target, } } @@ -246,12 +262,15 @@ func (it *unzipper) Extract(directory string) error { if entry.FileInfo().IsDir() { continue } - target := filepath.Join(directory, slashed(entry.Name)[limit:]) + target, err := zipEntryTarget(directory, slashed(entry.Name)[limit:]) + if err != nil { + return fmt.Errorf("Problem while extracting zip, reason: %v", err) + } todo := WriteTarget{ Source: entry, Target: target, } - err := todo.execute() + err = todo.execute() if err != nil { return fmt.Errorf("Problem while extracting zip, reason: %v", err) } diff --git a/operations/zipper_test.go b/operations/zipper_test.go index e611408..14d07ab 100644 --- a/operations/zipper_test.go +++ b/operations/zipper_test.go @@ -18,3 +18,16 @@ func TestCanConvertSlashes(t *testing.T) { must.Equal(3, len(wintestpath)) must.Equal(slashed(wintestpath), nixtestpath) } + +func TestZipEntryTargetAcceptsNestedPath(t *testing.T) { + must, _ := hamlet.Specifications(t) + target, err := zipEntryTarget("/tmp/destination", "repo/robot.yaml") + must.Equal(nil, err) + must.Equal("/tmp/destination/repo/robot.yaml", target) +} + +func TestZipEntryTargetRejectsTraversalPath(t *testing.T) { + _, wont := hamlet.Specifications(t) + _, err := zipEntryTarget("/tmp/destination", "../../outside.txt") + wont.Equal(nil, err) +}