github.com/aquasecurity/trivy-iac@v0.8.1-0.20240127024015-3d8e412cf0ab/pkg/scanners/helm/parser/parser_tar.go (about) 1 package parser 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "compress/gzip" 7 "errors" 8 "fmt" 9 "io" 10 "io/fs" 11 "os" 12 "path/filepath" 13 14 "github.com/aquasecurity/trivy-iac/pkg/detection" 15 "github.com/liamg/memoryfs" 16 ) 17 18 var errSkipFS = errors.New("skip parse FS") 19 20 func (p *Parser) addTarToFS(path string) (fs.FS, error) { 21 tarFS := memoryfs.CloneFS(p.workingFS) 22 23 file, err := tarFS.Open(path) 24 if err != nil { 25 return nil, fmt.Errorf("failed to open tar: %w", err) 26 } 27 defer file.Close() 28 29 var tr *tar.Reader 30 31 if detection.IsZip(path) { 32 zipped, err := gzip.NewReader(file) 33 if err != nil { 34 return nil, fmt.Errorf("failed to create gzip reader: %w", err) 35 } 36 defer zipped.Close() 37 tr = tar.NewReader(zipped) 38 } else { 39 tr = tar.NewReader(file) 40 } 41 42 checkExistedChart := true 43 44 for { 45 header, err := tr.Next() 46 if err != nil { 47 if errors.Is(err, io.EOF) { 48 break 49 } 50 return nil, fmt.Errorf("failed to get next entry: %w", err) 51 } 52 53 if checkExistedChart { 54 // Do not add archive files to FS if the chart already exists 55 // This can happen when the source chart is located next to an archived chart (with the `helm package` command) 56 // The first level folder in the archive is equal to the Chart name 57 if _, err := tarFS.Stat(filepath.Dir(path) + "/" + filepath.Dir(header.Name)); err == nil { 58 return nil, errSkipFS 59 } 60 checkExistedChart = false 61 } 62 63 // get the individual path and extract to the current directory 64 entryPath := header.Name 65 66 switch header.Typeflag { 67 case tar.TypeDir: 68 if err := tarFS.MkdirAll(entryPath, os.FileMode(header.Mode)); err != nil && !errors.Is(err, fs.ErrExist) { 69 return nil, err 70 } 71 case tar.TypeReg: 72 writePath := filepath.Dir(path) + "/" + entryPath 73 p.debug.Log("Unpacking tar entry %s", writePath) 74 75 _ = tarFS.MkdirAll(filepath.Dir(writePath), fs.ModePerm) 76 77 buf, err := copyChunked(tr, 1024) 78 if err != nil { 79 return nil, err 80 } 81 82 p.debug.Log("writing file contents to %s", writePath) 83 if err := tarFS.WriteFile(writePath, buf.Bytes(), fs.ModePerm); err != nil { 84 return nil, fmt.Errorf("write file error: %w", err) 85 } 86 default: 87 return nil, fmt.Errorf("header type %q is not supported", header.Typeflag) 88 } 89 } 90 91 if err := tarFS.Remove(path); err != nil { 92 return nil, fmt.Errorf("failed to remove tar from FS: %w", err) 93 } 94 95 return tarFS, nil 96 } 97 98 func copyChunked(src io.Reader, chunkSize int64) (*bytes.Buffer, error) { 99 buf := new(bytes.Buffer) 100 for { 101 if _, err := io.CopyN(buf, src, chunkSize); err != nil { 102 if errors.Is(err, io.EOF) { 103 break 104 } 105 return nil, fmt.Errorf("failed to copy: %w", err) 106 } 107 } 108 109 return buf, nil 110 }