github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/analyzer/language/python/packaging/packaging.go (about) 1 package packaging 2 3 import ( 4 "archive/zip" 5 "bytes" 6 "context" 7 "io" 8 "os" 9 "strings" 10 11 "golang.org/x/xerrors" 12 13 dio "github.com/aquasecurity/go-dep-parser/pkg/io" 14 "github.com/aquasecurity/go-dep-parser/pkg/python/packaging" 15 "github.com/devseccon/trivy/pkg/fanal/analyzer" 16 "github.com/devseccon/trivy/pkg/fanal/analyzer/language" 17 "github.com/devseccon/trivy/pkg/fanal/types" 18 ) 19 20 func init() { 21 analyzer.RegisterAnalyzer(&packagingAnalyzer{}) 22 } 23 24 const version = 1 25 26 var ( 27 requiredFiles = []string{ 28 // .egg format 29 // https://setuptools.readthedocs.io/en/latest/deprecated/python_eggs.html#eggs-and-their-formats 30 ".egg", // zip format 31 "EGG-INFO/PKG-INFO", 32 33 // .egg-info format: .egg-info can be a file or directory 34 // https://setuptools.readthedocs.io/en/latest/deprecated/python_eggs.html#eggs-and-their-formats 35 ".egg-info", 36 ".egg-info/PKG-INFO", 37 38 // wheel 39 ".dist-info/METADATA", 40 } 41 ) 42 43 type packagingAnalyzer struct{} 44 45 // Analyze analyzes egg and wheel files. 46 func (a packagingAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { 47 r := input.Content 48 49 // .egg file is zip format and PKG-INFO needs to be extracted from the zip file. 50 if strings.HasSuffix(input.FilePath, ".egg") { 51 pkginfoInZip, err := a.analyzeEggZip(input.Content, input.Info.Size()) 52 if err != nil { 53 return nil, xerrors.Errorf("egg analysis error: %w", err) 54 } 55 56 // Egg archive may not contain required files, then we will get nil. Skip this archives 57 if pkginfoInZip == nil { 58 return nil, nil 59 } 60 61 r = pkginfoInZip 62 } 63 64 p := packaging.NewParser() 65 return language.AnalyzePackage(types.PythonPkg, input.FilePath, r, p, input.Options.FileChecksum) 66 } 67 68 func (a packagingAnalyzer) analyzeEggZip(r io.ReaderAt, size int64) (dio.ReadSeekerAt, error) { 69 zr, err := zip.NewReader(r, size) 70 if err != nil { 71 return nil, xerrors.Errorf("zip reader error: %w", err) 72 } 73 74 for _, file := range zr.File { 75 if !a.Required(file.Name, nil) { 76 continue 77 } 78 79 return a.open(file) 80 } 81 82 return nil, nil 83 } 84 85 func (a packagingAnalyzer) open(file *zip.File) (dio.ReadSeekerAt, error) { 86 f, err := file.Open() 87 if err != nil { 88 return nil, err 89 } 90 defer f.Close() 91 92 b, err := io.ReadAll(f) 93 if err != nil { 94 return nil, xerrors.Errorf("file %s open error: %w", file.Name, err) 95 } 96 97 return bytes.NewReader(b), nil 98 } 99 100 func (a packagingAnalyzer) Required(filePath string, _ os.FileInfo) bool { 101 for _, r := range requiredFiles { 102 if strings.HasSuffix(filePath, r) { 103 return true 104 } 105 } 106 return false 107 } 108 109 func (a packagingAnalyzer) Type() analyzer.Type { 110 return analyzer.TypePythonPkg 111 } 112 113 func (a packagingAnalyzer) Version() int { 114 return version 115 }