2021-08-19 16:32:24 +05:30
|
|
|
package packaging
|
|
|
|
|
|
|
|
|
|
import (
|
2021-09-13 15:02:06 +03:00
|
|
|
"archive/zip"
|
2022-05-03 22:06:36 +06:00
|
|
|
"bytes"
|
2021-10-06 19:18:50 +05:30
|
|
|
"context"
|
2021-09-13 15:02:06 +03:00
|
|
|
"io"
|
2021-08-19 16:32:24 +05:30
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"golang.org/x/xerrors"
|
|
|
|
|
|
2022-05-03 22:06:36 +06:00
|
|
|
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
|
2021-08-19 16:32:24 +05:30
|
|
|
"github.com/aquasecurity/go-dep-parser/pkg/python/packaging"
|
2022-06-20 09:43:33 +01:00
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
|
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
|
|
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
2021-08-19 16:32:24 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
analyzer.RegisterAnalyzer(&packagingAnalyzer{})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const version = 1
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
requiredFiles = []string{
|
2021-09-13 14:14:17 +03:00
|
|
|
// .egg format
|
|
|
|
|
// https://setuptools.readthedocs.io/en/latest/deprecated/python_eggs.html#eggs-and-their-formats
|
2021-09-13 15:02:06 +03:00
|
|
|
".egg", // zip format
|
2021-09-13 14:14:17 +03:00
|
|
|
"EGG-INFO/PKG-INFO",
|
|
|
|
|
|
|
|
|
|
// .egg-info format: .egg-info can be a file or directory
|
|
|
|
|
// https://setuptools.readthedocs.io/en/latest/deprecated/python_eggs.html#eggs-and-their-formats
|
2021-08-19 16:32:24 +05:30
|
|
|
".egg-info",
|
|
|
|
|
".egg-info/PKG-INFO",
|
|
|
|
|
|
|
|
|
|
// wheel
|
|
|
|
|
".dist-info/METADATA",
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type packagingAnalyzer struct{}
|
|
|
|
|
|
|
|
|
|
// Analyze analyzes egg and wheel files.
|
2021-12-24 08:26:10 +02:00
|
|
|
func (a packagingAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
|
2022-05-03 22:06:36 +06:00
|
|
|
r := input.Content
|
2021-09-13 15:02:06 +03:00
|
|
|
|
|
|
|
|
// .egg file is zip format and PKG-INFO needs to be extracted from the zip file.
|
2021-12-24 08:26:10 +02:00
|
|
|
if strings.HasSuffix(input.FilePath, ".egg") {
|
|
|
|
|
pkginfoInZip, err := a.analyzeEggZip(input.Content, input.Info.Size())
|
2021-09-13 15:02:06 +03:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, xerrors.Errorf("egg analysis error: %w", err)
|
|
|
|
|
}
|
2022-05-03 22:06:36 +06:00
|
|
|
|
|
|
|
|
// Egg archive may not contain required files, then we will get nil. Skip this archives
|
|
|
|
|
if pkginfoInZip == nil {
|
2022-04-14 14:34:17 +06:00
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-24 03:15:36 +09:00
|
|
|
r = pkginfoInZip
|
2021-09-13 15:02:06 +03:00
|
|
|
}
|
|
|
|
|
|
2022-05-03 22:06:36 +06:00
|
|
|
p := packaging.NewParser()
|
|
|
|
|
libs, deps, err := p.Parse(r)
|
2021-08-19 16:32:24 +05:30
|
|
|
if err != nil {
|
2021-12-24 08:26:10 +02:00
|
|
|
return nil, xerrors.Errorf("unable to parse %s: %w", input.FilePath, err)
|
2021-08-19 16:32:24 +05:30
|
|
|
}
|
|
|
|
|
|
2022-05-03 22:06:36 +06:00
|
|
|
return language.ToAnalysisResult(types.PythonPkg, input.FilePath, input.FilePath, libs, deps), nil
|
2021-08-19 16:32:24 +05:30
|
|
|
}
|
|
|
|
|
|
2022-05-03 22:06:36 +06:00
|
|
|
func (a packagingAnalyzer) analyzeEggZip(r io.ReaderAt, size int64) (dio.ReadSeekerAt, error) {
|
2021-12-24 03:15:36 +09:00
|
|
|
zr, err := zip.NewReader(r, size)
|
2021-09-13 15:02:06 +03:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, xerrors.Errorf("zip reader error: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, file := range zr.File {
|
|
|
|
|
if !a.Required(file.Name, nil) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return a.open(file)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-03 22:06:36 +06:00
|
|
|
func (a packagingAnalyzer) open(file *zip.File) (dio.ReadSeekerAt, error) {
|
2021-09-13 15:02:06 +03:00
|
|
|
f, err := file.Open()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2022-05-03 22:06:36 +06:00
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
|
|
b, err := io.ReadAll(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, xerrors.Errorf("file %s open error: %w", file.Name, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bytes.NewReader(b), nil
|
2021-09-13 15:02:06 +03:00
|
|
|
}
|
|
|
|
|
|
2021-08-19 16:32:24 +05:30
|
|
|
func (a packagingAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
|
|
|
|
// For Windows
|
|
|
|
|
filePath = filepath.ToSlash(filePath)
|
|
|
|
|
|
|
|
|
|
for _, r := range requiredFiles {
|
|
|
|
|
if strings.HasSuffix(filePath, r) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a packagingAnalyzer) Type() analyzer.Type {
|
|
|
|
|
return analyzer.TypePythonPkg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a packagingAnalyzer) Version() int {
|
|
|
|
|
return version
|
|
|
|
|
}
|