diff --git a/vacation/vacation.go b/vacation/vacation.go index a6966b9a..28a25b8a 100644 --- a/vacation/vacation.go +++ b/vacation/vacation.go @@ -142,6 +142,11 @@ func (ta TarArchive) Decompress(destination string) error { } case tar.TypeSymlink: + err = checkExtractPath(filepath.Join(filepath.Dir(hdr.Name), hdr.Linkname), destination) + if err != nil { + return err + } + err = os.Symlink(hdr.Linkname, path) if err != nil { return fmt.Errorf("failed to extract symlink: %s", err) @@ -313,6 +318,11 @@ func (z ZipArchive) Decompress(destination string) error { return err } + err = checkExtractPath(filepath.Join(filepath.Dir(f.Name), string(content)), destination) + if err != nil { + return err + } + err = os.Symlink(string(content), path) if err != nil { return fmt.Errorf("failed to unzip symlink: %w", err) diff --git a/vacation/vacation_tar_test.go b/vacation/vacation_tar_test.go index c2095bd7..ae7ad677 100644 --- a/vacation/vacation_tar_test.go +++ b/vacation/vacation_tar_test.go @@ -229,6 +229,30 @@ func testVacationTar(t *testing.T, context spec.G, it spec.S) { }) }) + context("when it tries to symlink that tries to link to a file outside of the directory", func() { + var zipSlipSymlinkTar vacation.TarArchive + + it.Before(func() { + var err error + + buffer := bytes.NewBuffer(nil) + tw := tar.NewWriter(buffer) + + Expect(tw.WriteHeader(&tar.Header{Name: "symlink", Mode: 0755, Size: int64(0), Typeflag: tar.TypeSymlink, Linkname: filepath.Join("..", "some-file")})).To(Succeed()) + _, err = tw.Write([]byte{}) + Expect(err).NotTo(HaveOccurred()) + + Expect(tw.Close()).To(Succeed()) + + zipSlipSymlinkTar = vacation.NewTarArchive(bytes.NewReader(buffer.Bytes())) + }) + + it("returns an error", func() { + err := zipSlipSymlinkTar.Decompress(tempDir) + Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("illegal file path %q: the file path does not occur within the destination directory", filepath.Join("..", "some-file"))))) + }) + }) + context("when it tries to decompress a broken symlink", func() { var brokenSymlinkTar vacation.TarArchive @@ -238,12 +262,16 @@ func testVacationTar(t *testing.T, context spec.G, it spec.S) { buffer := bytes.NewBuffer(nil) tw := tar.NewWriter(buffer) - Expect(tw.WriteHeader(&tar.Header{Name: "symlink", Mode: 0755, Size: int64(0), Typeflag: tar.TypeSymlink, Linkname: ""})).To(Succeed()) + Expect(tw.WriteHeader(&tar.Header{Name: "symlink", Mode: 0755, Size: int64(0), Typeflag: tar.TypeSymlink, Linkname: "some-file"})).To(Succeed()) _, err = tw.Write([]byte{}) Expect(err).NotTo(HaveOccurred()) Expect(tw.Close()).To(Succeed()) + // Create a symlink in the target to force the new symlink create to + // fail + Expect(os.Symlink("something", filepath.Join(tempDir, "symlink"))).To(Succeed()) + brokenSymlinkTar = vacation.NewTarArchive(bytes.NewReader(buffer.Bytes())) }) diff --git a/vacation/vacation_zip_test.go b/vacation/vacation_zip_test.go index a8a82f57..876be27b 100644 --- a/vacation/vacation_zip_test.go +++ b/vacation/vacation_zip_test.go @@ -194,6 +194,34 @@ func testVacationZip(t *testing.T, context spec.G, it spec.S) { }) }) + context("when it tries to symlink that tries to link to a file outside of the directory", func() { + var buffer *bytes.Buffer + it.Before(func() { + var err error + buffer = bytes.NewBuffer(nil) + zw := zip.NewWriter(buffer) + + header := &zip.FileHeader{Name: "symlink"} + header.SetMode(0755 | os.ModeSymlink) + + symlink, err := zw.CreateHeader(header) + Expect(err).NotTo(HaveOccurred()) + + _, err = symlink.Write([]byte(filepath.Join("..", "some-file"))) + Expect(err).NotTo(HaveOccurred()) + + Expect(zw.Close()).To(Succeed()) + + }) + + it("returns an error", func() { + readyArchive := vacation.NewZipArchive(buffer) + + err := readyArchive.Decompress(tempDir) + Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("illegal file path %q: the file path does not occur within the destination directory", filepath.Join("..", "some-file"))))) + }) + }) + context("when it fails to unzip a symlink", func() { var buffer *bytes.Buffer it.Before(func() {