-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
Copy pathanalyze.go
144 lines (122 loc) · 4.27 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
139
140
141
142
143
144
package language
import (
"io"
"strings"
"golang.org/x/xerrors"
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
"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"
)
// Analyze returns an analysis result of the lock file
func Analyze(fileType, filePath string, r dio.ReadSeekerAt, parser godeptypes.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, filePath string, r dio.ReadSeekerAt, parser godeptypes.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, filePath string, r dio.ReadSeekerAt, parser godeptypes.Parser) (*types.Application, error) {
parsedLibs, parsedDependencies, err := parser.Parse(r)
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, parsedLibs, parsedDependencies), nil
}
// ParsePackage returns a parsed result of the package file
func ParsePackage(fileType, filePath string, r dio.ReadSeekerAt, parser godeptypes.Parser, checksum bool) (*types.Application, error) {
parsedLibs, 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, parsedLibs, parsedDependencies), nil
}
func toApplication(fileType, filePath, libFilePath string, r dio.ReadSeekerAt, libs []godeptypes.Library, depGraph []godeptypes.Dependency) *types.Application {
if len(libs) == 0 {
return nil
}
// Calculate the file digest when one of `spdx` formats is selected
d, err := calculateDigest(r)
if err != nil {
log.Logger.Warnf("Unable to get checksum for %s: %s", filePath, err)
}
deps := make(map[string][]string)
for _, dep := range depGraph {
deps[dep.ID] = dep.DependsOn
}
var pkgs []types.Package
for _, lib := range libs {
var licenses []string
if lib.License != "" {
licenses = strings.Split(lib.License, ",")
for i, license := range licenses {
licenses[i] = licensing.Normalize(strings.TrimSpace(license))
}
}
var locs []types.Location
for _, loc := range lib.Locations {
l := types.Location{
StartLine: loc.StartLine,
EndLine: loc.EndLine,
}
locs = append(locs, l)
}
// This file path is populated for virtual file paths within archives, such as nested JAR files.
libPath := libFilePath
if lib.FilePath != "" {
libPath = lib.FilePath
}
pkgs = append(pkgs, types.Package{
ID: lib.ID,
Name: lib.Name,
Version: lib.Version,
FilePath: libPath,
Indirect: lib.Indirect,
Licenses: licenses,
DependsOn: deps[lib.ID],
Locations: locs,
Digest: d,
})
}
return &types.Application{
Type: fileType,
FilePath: filePath,
Libraries: pkgs,
}
}
func calculateDigest(r dio.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)
}