github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/run/download/zip.go (about)

     1  package download
     2  
     3  import (
     4  	"archive/zip"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  )
    11  
    12  const (
    13  	dirMode  os.FileMode = 0755
    14  	fileMode os.FileMode = 0644
    15  	execMode os.FileMode = 0755
    16  )
    17  
    18  func extractZip(zr *zip.Reader, destDir string) error {
    19  	for _, zf := range zr.File {
    20  		fpath := filepath.Join(destDir, filepath.FromSlash(zf.Name))
    21  		if !filepathDescendsFrom(fpath, destDir) {
    22  			continue
    23  		}
    24  		if err := extractZipFile(zf, fpath); err != nil {
    25  			return fmt.Errorf("error extracting %q: %w", zf.Name, err)
    26  		}
    27  	}
    28  	return nil
    29  }
    30  
    31  func extractZipFile(zf *zip.File, dest string) error {
    32  	zm := zf.Mode()
    33  	if zm.IsDir() {
    34  		return os.MkdirAll(dest, dirMode)
    35  	}
    36  
    37  	f, err := zf.Open()
    38  	if err != nil {
    39  		return err
    40  	}
    41  	defer f.Close()
    42  
    43  	if dir := filepath.Dir(dest); dir != "." {
    44  		if err := os.MkdirAll(dir, dirMode); err != nil {
    45  			return err
    46  		}
    47  	}
    48  
    49  	df, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_EXCL, getPerm(zm))
    50  	if err != nil {
    51  		return err
    52  	}
    53  	defer df.Close()
    54  
    55  	_, err = io.Copy(df, f)
    56  	return err
    57  }
    58  
    59  func getPerm(m os.FileMode) os.FileMode {
    60  	if m&0111 == 0 {
    61  		return fileMode
    62  	}
    63  	return execMode
    64  }
    65  
    66  func filepathDescendsFrom(p, dir string) bool {
    67  	p = filepath.Clean(p)
    68  	dir = filepath.Clean(dir)
    69  	if dir == "." && !filepath.IsAbs(p) {
    70  		return !strings.HasPrefix(p, ".."+string(filepath.Separator))
    71  	}
    72  	if !strings.HasSuffix(dir, string(filepath.Separator)) {
    73  		dir += string(filepath.Separator)
    74  	}
    75  	return strings.HasPrefix(p, dir)
    76  }