diff --git a/archiver.go b/archiver.go index 68f8ea38..32baf3e0 100644 --- a/archiver.go +++ b/archiver.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "runtime" + "strings" ) // Archiver represent a archive format @@ -105,3 +106,14 @@ func mkdir(dirPath string) error { } return nil } + +func sanitizeExtractPath(filePath string, destination string) error { + // to avoid zip slip (writing outside of the destination), we resolve + // the target path, and make sure it's nested in the intended + // destination, or bail otherwise. + destpath := filepath.Join(destination, filePath) + if !strings.HasPrefix(destpath, destination) { + return fmt.Errorf("%s: illegal file path", filePath) + } + return nil +} diff --git a/cmd/archiver/main.go b/cmd/archiver/main.go index d274e167..2e181fa4 100644 --- a/cmd/archiver/main.go +++ b/cmd/archiver/main.go @@ -27,7 +27,10 @@ func main() { } err = ff.Make(filename, os.Args[3:]) case "open": - dest := "" + dest, osErr := os.Getwd() + if osErr != nil { + fatal(err) + } if len(os.Args) == 4 { dest = os.Args[3] } else if len(os.Args) > 4 { diff --git a/rar.go b/rar.go index 7744c24a..86f8a63d 100644 --- a/rar.go +++ b/rar.go @@ -72,8 +72,15 @@ func (rarFormat) Read(input io.Reader, destination string) error { return err } + err = sanitizeExtractPath(header.Name, destination) + if err != nil { + return err + } + + destpath := filepath.Join(destination, header.Name) + if header.IsDir { - err = mkdir(filepath.Join(destination, header.Name)) + err = mkdir(destpath) if err != nil { return err } @@ -82,12 +89,12 @@ func (rarFormat) Read(input io.Reader, destination string) error { // if files come before their containing folders, then we must // create their folders before writing the file - err = mkdir(filepath.Dir(filepath.Join(destination, header.Name))) + err = mkdir(filepath.Dir(destpath)) if err != nil { return err } - err = writeNewFile(filepath.Join(destination, header.Name), rr, header.Mode()) + err = writeNewFile(destpath, rr, header.Mode()) if err != nil { return err } diff --git a/tar.go b/tar.go index 2fbfbb27..caa9de22 100644 --- a/tar.go +++ b/tar.go @@ -219,15 +219,22 @@ func untar(tr *tar.Reader, destination string) error { // untarFile untars a single file from tr with header header into destination. func untarFile(tr *tar.Reader, header *tar.Header, destination string) error { + err := sanitizeExtractPath(header.Name, destination) + if err != nil { + return err + } + + destpath := filepath.Join(destination, header.Name) + switch header.Typeflag { case tar.TypeDir: - return mkdir(filepath.Join(destination, header.Name)) + return mkdir(destpath) case tar.TypeReg, tar.TypeRegA, tar.TypeChar, tar.TypeBlock, tar.TypeFifo: - return writeNewFile(filepath.Join(destination, header.Name), tr, header.FileInfo().Mode()) + return writeNewFile(destpath, tr, header.FileInfo().Mode()) case tar.TypeSymlink: - return writeNewSymbolicLink(filepath.Join(destination, header.Name), header.Linkname) + return writeNewSymbolicLink(destpath, header.Linkname) case tar.TypeLink: - return writeNewHardLink(filepath.Join(destination, header.Name), filepath.Join(destination, header.Linkname)) + return writeNewHardLink(destpath, filepath.Join(destination, header.Linkname)) default: return fmt.Errorf("%s: unknown type flag: %c", header.Name, header.Typeflag) } diff --git a/zip.go b/zip.go index 5f458ada..9d20bc1b 100644 --- a/zip.go +++ b/zip.go @@ -187,6 +187,11 @@ func unzipAll(r *zip.Reader, destination string) error { } func unzipFile(zf *zip.File, destination string) error { + err := sanitizeExtractPath(zf.Name, destination) + if err != nil { + return err + } + if strings.HasSuffix(zf.Name, "/") { return mkdir(filepath.Join(destination, zf.Name)) }