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  }