github.com/ahlemtn/fabric@v2.1.1+incompatible/core/container/externalbuilder/tar.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package externalbuilder 8 9 import ( 10 "archive/tar" 11 "compress/gzip" 12 "io" 13 "os" 14 "path/filepath" 15 "strings" 16 17 "github.com/pkg/errors" 18 ) 19 20 // Untar takes a gzip-ed tar archive, and extracts it to dst. 21 // It returns an error if the tar contains any files which would escape to a 22 // parent of dst, or if the archive contains any files whose type is not 23 // a regular file or directory. 24 func Untar(buffer io.Reader, dst string) error { 25 gzr, err := gzip.NewReader(buffer) 26 if err != nil { 27 return err 28 } 29 defer gzr.Close() 30 31 tr := tar.NewReader(gzr) 32 33 for { 34 header, err := tr.Next() 35 36 if err == io.EOF { 37 return nil 38 } 39 40 if err != nil { 41 return errors.WithMessage(err, "could not get next tar element") 42 } 43 44 if !ValidPath(header.Name) { 45 return errors.Errorf("tar contains the absolute or escaping path '%s'", header.Name) 46 } 47 48 target := filepath.Join(dst, header.Name) 49 switch header.Typeflag { 50 case tar.TypeDir: 51 if err := os.MkdirAll(target, 0700); err != nil { 52 return errors.WithMessagef(err, "could not create directory '%s'", header.Name) 53 } 54 case tar.TypeReg: 55 if err := os.MkdirAll(filepath.Dir(target), 0700); err != nil { 56 return errors.WithMessagef(err, "could not create directory '%s'", filepath.Dir(header.Name)) 57 } 58 59 f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) 60 if err != nil { 61 return errors.WithMessagef(err, "could not create file '%s'", header.Name) 62 } 63 64 // copy over contents 65 if _, err := io.Copy(f, tr); err != nil { 66 return err 67 } 68 69 f.Close() 70 default: 71 return errors.Errorf("invalid file type '%v' contained in archive for file '%s'", header.Typeflag, header.Name) 72 } 73 } 74 } 75 76 // ValidPath checks to see if the path is absolute, or if it is a 77 // relative path higher in the tree. In these cases it returns false. 78 func ValidPath(uncleanPath string) bool { 79 // sanitizedPath will eliminate non-prefix instances of '..', as well 80 // as strip './' 81 sanitizedPath := filepath.Clean(uncleanPath) 82 83 switch { 84 case filepath.IsAbs(sanitizedPath): 85 return false 86 case strings.HasPrefix(sanitizedPath, ".."+string(filepath.Separator)) || sanitizedPath == "..": 87 // Path refers either to the parent, or a directory relative to the parent (but allows ..foo or ... for instance) 88 return false 89 default: 90 // Path appears to be relative without escaping higher 91 return true 92 } 93 }