github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/app/extract_plugin_tar.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "archive/tar" 8 "compress/gzip" 9 "io" 10 "os" 11 "path/filepath" 12 "strings" 13 14 "github.com/vnforks/kid/v5/mlog" 15 "github.com/pkg/errors" 16 ) 17 18 // extractTarGz takes in an io.Reader containing the bytes for a .tar.gz file and 19 // a destination string to extract to. 20 func extractTarGz(gzipStream io.Reader, dst string) error { 21 if dst == "" { 22 return errors.New("no destination path provided") 23 } 24 25 uncompressedStream, err := gzip.NewReader(gzipStream) 26 if err != nil { 27 return errors.Wrap(err, "failed to initialize gzip reader") 28 } 29 defer uncompressedStream.Close() 30 31 tarReader := tar.NewReader(uncompressedStream) 32 33 for { 34 header, err := tarReader.Next() 35 if err == io.EOF { 36 break 37 } else if err != nil { 38 return errors.Wrap(err, "failed to read next file from archive") 39 } 40 41 // Pre-emptively check type flag to avoid reporting a misleading error in 42 // trying to sanitize the header name. 43 switch header.Typeflag { 44 case tar.TypeDir: 45 case tar.TypeReg: 46 default: 47 mlog.Warn("skipping unsupported header type on extracting tar file", mlog.String("header_type", string(header.Typeflag)), mlog.String("header_name", header.Name)) 48 continue 49 } 50 51 // filepath.HasPrefix is deprecated, so we just use strings.HasPrefix to ensure 52 // the target path remains rooted at dst and has no `../` escaping outside. 53 path := filepath.Join(dst, header.Name) 54 if !strings.HasPrefix(path, dst) { 55 return errors.Errorf("failed to sanitize path %s", header.Name) 56 } 57 58 switch header.Typeflag { 59 case tar.TypeDir: 60 if err := os.Mkdir(path, 0744); err != nil && !os.IsExist(err) { 61 return err 62 } 63 case tar.TypeReg: 64 dir := filepath.Dir(path) 65 66 if err := os.MkdirAll(dir, 0744); err != nil { 67 return err 68 } 69 70 outFile, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode)) 71 if err != nil { 72 return err 73 } 74 defer outFile.Close() 75 if _, err := io.Copy(outFile, tarReader); err != nil { 76 return err 77 } 78 } 79 } 80 81 return nil 82 }