github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/util/unzip.go (about) 1 package util 2 3 import ( 4 "archive/zip" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 "strings" 10 ) 11 12 // Unzips the archvie into the specified directory 13 // returns an error if a general issue occurred unzipping the archive 14 func Unzip(src, dest string) error { 15 r, err := zip.OpenReader(src) 16 if err != nil { 17 return err 18 } 19 defer r.Close() 20 21 for _, f := range r.File { 22 err = extractFile(dest, f) 23 if err != nil { 24 return err 25 } 26 } 27 return nil 28 } 29 30 // Unzips the specified files from the archive 31 // returns an error if any of the specified files are not found or a general issue occurred unzipping the archive 32 func UnzipSpecificFiles(src, dest string, onlyFiles ...string) error { 33 r, err := zip.OpenReader(src) 34 if err != nil { 35 return err 36 } 37 defer r.Close() 38 39 m := make(map[string]bool) 40 for _, f := range onlyFiles { 41 m[f] = false 42 } 43 44 for _, f := range r.File { 45 name := f.Name 46 if _, matched := m[name]; matched { 47 err = extractFile(dest, f) 48 if err != nil { 49 return err 50 } 51 m[name] = true 52 } 53 } 54 55 // check we unzip all the specified files 56 failed := false 57 errString := "" 58 for f, b := range m { 59 if !b { 60 if failed { 61 errString += ", " + f 62 } else { 63 errString += ", " + f 64 failed = true 65 } 66 } 67 } 68 if failed { 69 return fmt.Errorf("the specified files where not found within the zip [%s]", errString) 70 } 71 72 return nil 73 } 74 75 // extract the specific file into the destination directory. 76 func extractFile(dest string, f *zip.File) error { 77 name := filepath.Join(dest, f.Name) // #nosec 78 // We need to be secure to prevent attacks like 79 // https://snyk.io/blog/zip-slip-vulnerability 80 // the result is already 'Clean'ed so we only need to check the string starts 81 if !strings.HasPrefix(name, dest) { 82 return fmt.Errorf("refusing to unzip %s due to escaping out of expected directory", f.Name) 83 } 84 85 rc, err := f.Open() 86 if err != nil { 87 return err 88 } 89 defer rc.Close() 90 91 if f.FileInfo().IsDir() { 92 err := os.MkdirAll(name, os.ModePerm) 93 if err != nil { 94 return err 95 } 96 } else { 97 var fdir string 98 if lastIndex := strings.LastIndex(name, string(os.PathSeparator)); lastIndex > -1 { 99 fdir = name[:lastIndex] 100 } 101 102 err = os.MkdirAll(fdir, os.ModePerm) 103 if err != nil { 104 return err 105 } 106 f, err := os.OpenFile( 107 name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) 108 if err != nil { 109 return err 110 } 111 defer f.Close() 112 113 limited := io.LimitReader(rc, 100*1024*1024) 114 _, err = io.Copy(f, limited) 115 if err != nil { 116 return err 117 } 118 } 119 return nil 120 }