github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/walker/tar.go (about) 1 package walker 2 3 import ( 4 "archive/tar" 5 "io" 6 "io/fs" 7 "path" 8 "path/filepath" 9 "strings" 10 11 "golang.org/x/xerrors" 12 13 "github.com/devseccon/trivy/pkg/fanal/utils" 14 ) 15 16 const ( 17 opq string = ".wh..wh..opq" 18 wh string = ".wh." 19 ) 20 21 var parentDir = ".." + utils.PathSeparator 22 23 type LayerTar struct { 24 walker 25 threshold int64 26 } 27 28 func NewLayerTar(skipFiles, skipDirs []string) LayerTar { 29 threshold := defaultSizeThreshold 30 return LayerTar{ 31 walker: newWalker(skipFiles, skipDirs), 32 threshold: threshold, 33 } 34 } 35 36 func (w LayerTar) Walk(layer io.Reader, analyzeFn WalkFunc) ([]string, []string, error) { 37 var opqDirs, whFiles, skipDirs []string 38 tr := tar.NewReader(layer) 39 for { 40 hdr, err := tr.Next() 41 if err == io.EOF { 42 break 43 } else if err != nil { 44 return nil, nil, xerrors.Errorf("failed to extract the archive: %w", err) 45 } 46 47 // filepath.Clean cannot be used since tar file paths should be OS-agnostic. 48 filePath := path.Clean(hdr.Name) 49 filePath = strings.TrimLeft(filePath, "/") 50 fileDir, fileName := path.Split(filePath) 51 52 // e.g. etc/.wh..wh..opq 53 if opq == fileName { 54 opqDirs = append(opqDirs, fileDir) 55 continue 56 } 57 // etc/.wh.hostname 58 if strings.HasPrefix(fileName, wh) { 59 name := strings.TrimPrefix(fileName, wh) 60 fpath := path.Join(fileDir, name) 61 whFiles = append(whFiles, fpath) 62 continue 63 } 64 65 switch hdr.Typeflag { 66 case tar.TypeDir: 67 if w.shouldSkipDir(filePath) { 68 skipDirs = append(skipDirs, filePath) 69 continue 70 } 71 case tar.TypeReg: 72 if w.shouldSkipFile(filePath) { 73 continue 74 } 75 // symlinks and hardlinks have no content in reader, skip them 76 default: 77 continue 78 } 79 80 if underSkippedDir(filePath, skipDirs) { 81 continue 82 } 83 84 // A regular file will reach here. 85 if err = w.processFile(filePath, tr, hdr.FileInfo(), analyzeFn); err != nil { 86 return nil, nil, xerrors.Errorf("failed to process the file: %w", err) 87 } 88 } 89 return opqDirs, whFiles, nil 90 } 91 92 func (w LayerTar) processFile(filePath string, tr *tar.Reader, fi fs.FileInfo, analyzeFn WalkFunc) error { 93 cf := newCachedFile(fi.Size(), tr, w.threshold) 94 defer func() { 95 // nolint 96 _ = cf.Clean() 97 }() 98 99 if err := analyzeFn(filePath, fi, cf.Open); err != nil { 100 return xerrors.Errorf("failed to analyze file: %w", err) 101 } 102 103 return nil 104 } 105 106 func underSkippedDir(filePath string, skipDirs []string) bool { 107 for _, skipDir := range skipDirs { 108 rel, err := filepath.Rel(skipDir, filePath) 109 if err != nil { 110 return false 111 } 112 if !strings.HasPrefix(rel, parentDir) { 113 return true 114 } 115 } 116 return false 117 }