Skip to content

Commit

Permalink
fix: prevent extraction of archived files outside target path (#65)
Browse files Browse the repository at this point in the history
* fix: prevent extraction of archived files outside target path

* CR: consolidate the path sanitaiton logic
  • Loading branch information
aviadatsnyk authored and mholt committed Apr 17, 2018
1 parent 26cf5bb commit e4ef56d
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 8 deletions.
12 changes: 12 additions & 0 deletions archiver.go
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
)

// Archiver represent a archive format
Expand Down Expand Up @@ -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
}
5 changes: 4 additions & 1 deletion cmd/archiver/main.go
Expand Up @@ -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 {
Expand Down
13 changes: 10 additions & 3 deletions rar.go
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
15 changes: 11 additions & 4 deletions tar.go
Expand Up @@ -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)
}
Expand Down
5 changes: 5 additions & 0 deletions zip.go
Expand Up @@ -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))
}
Expand Down

0 comments on commit e4ef56d

Please sign in to comment.