-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
analyze.go
138 lines (114 loc) · 4.15 KB
/
analyze.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package language
import (
"io"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/digest"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/licensing"
"github.com/aquasecurity/trivy/pkg/log"
xio "github.com/aquasecurity/trivy/pkg/x/io"
)
type Parser interface {
// Parse parses the dependency file
Parse(r xio.ReadSeekerAt) ([]types.Package, []types.Dependency, error)
}
// Analyze returns an analysis result of the lock file
func Analyze(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser Parser) (*analyzer.AnalysisResult, error) {
app, err := Parse(fileType, filePath, r, parser)
if err != nil {
return nil, xerrors.Errorf("failed to parse %s: %w", filePath, err)
}
if app == nil {
return nil, nil
}
return &analyzer.AnalysisResult{Applications: []types.Application{*app}}, nil
}
// AnalyzePackage returns an analysis result of the package file other than lock files
func AnalyzePackage(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser Parser, checksum bool) (*analyzer.AnalysisResult, error) {
app, err := ParsePackage(fileType, filePath, r, parser, checksum)
if err != nil {
return nil, xerrors.Errorf("failed to parse %s: %w", filePath, err)
}
if app == nil {
return nil, nil
}
return &analyzer.AnalysisResult{Applications: []types.Application{*app}}, nil
}
// Parse returns a parsed result of the lock file
func Parse(fileType types.LangType, filePath string, r io.Reader, parser Parser) (*types.Application, error) {
rr, err := xio.NewReadSeekerAt(r)
if err != nil {
return nil, xerrors.Errorf("reader error: %w", err)
}
parsedPkgs, parsedDependencies, err := parser.Parse(rr)
if err != nil {
return nil, xerrors.Errorf("failed to parse %s: %w", filePath, err)
}
// The file path of each library should be empty in case of dependency list such as lock file
// since they all will be the same path.
return toApplication(fileType, filePath, "", nil, parsedPkgs, parsedDependencies), nil
}
// ParsePackage returns a parsed result of the package file
func ParsePackage(fileType types.LangType, filePath string, r xio.ReadSeekerAt, parser Parser, checksum bool) (*types.Application, error) {
parsedPkgs, parsedDependencies, err := parser.Parse(r)
if err != nil {
return nil, xerrors.Errorf("failed to parse %s: %w", filePath, err)
}
// The reader is not passed if the checksum is not necessarily calculated.
if !checksum {
r = nil
}
// The file path of each library should be empty in case of dependency list such as lock file
// since they all will be the same path.
return toApplication(fileType, filePath, filePath, r, parsedPkgs, parsedDependencies), nil
}
func toApplication(fileType types.LangType, filePath, libFilePath string, r xio.ReadSeekerAt, pkgs []types.Package, depGraph []types.Dependency) *types.Application {
if len(pkgs) == 0 {
return nil
}
// Calculate the file digest when one of `spdx` formats is selected
d, err := calculateDigest(r)
if err != nil {
log.Warn("Unable to get checksum", log.String("file_path", filePath), log.Err(err))
}
deps := make(map[string][]string)
for _, dep := range depGraph {
deps[dep.ID] = dep.DependsOn
}
for i, pkg := range pkgs {
// This file path is populated for virtual file paths within archives, such as nested JAR files.
if pkg.FilePath == "" {
pkgs[i].FilePath = libFilePath
}
pkgs[i].DependsOn = deps[pkg.ID]
pkgs[i].Digest = d
pkgs[i].Indirect = isIndirect(pkg.Relationship) // For backward compatibility
for j, license := range pkg.Licenses {
pkgs[i].Licenses[j] = licensing.Normalize(license)
}
}
return &types.Application{
Type: fileType,
FilePath: filePath,
Packages: pkgs,
}
}
func calculateDigest(r xio.ReadSeekerAt) (digest.Digest, error) {
if r == nil {
return "", nil
}
// return reader to start after it has been read in analyzer
if _, err := r.Seek(0, io.SeekStart); err != nil {
return "", xerrors.Errorf("unable to seek: %w", err)
}
return digest.CalcSHA1(r)
}
func isIndirect(rel types.Relationship) bool {
switch rel {
case types.RelationshipIndirect:
return true
default:
return false
}
}