/
transformer.go
136 lines (115 loc) · 3.51 KB
/
transformer.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
package scan
import (
"log/slog"
"time"
"github.com/samber/lo"
"github.com/aquasecurity/harbor-scanner-trivy/pkg/harbor"
"github.com/aquasecurity/harbor-scanner-trivy/pkg/http/api"
"github.com/aquasecurity/harbor-scanner-trivy/pkg/trivy"
)
// Clock wraps the Now method. Introduced to allow replacing the global state with fixed clocks to facilitate testing.
// Now returns the current time.
type Clock interface {
Now() time.Time
}
type SystemClock struct {
}
func (c *SystemClock) Now() time.Time {
return time.Now()
}
// Transformer wraps the Transform method.
// Transform transforms Trivy's scan report into Harbor's packages vulnerabilities report.
type Transformer interface {
Transform(mediaType api.MediaType, req harbor.ScanRequest, source trivy.Report) harbor.ScanReport
}
type transformer struct {
clock Clock
}
// NewTransformer constructs a Transformer with the given Clock.
func NewTransformer(clock Clock) Transformer {
return &transformer{
clock: clock,
}
}
func (t *transformer) Transform(mediaType api.MediaType, req harbor.ScanRequest, source trivy.Report) harbor.ScanReport {
report := harbor.ScanReport{
GeneratedAt: t.clock.Now(),
Scanner: harbor.GetScannerMetadata(),
Artifact: req.Artifact,
}
switch mediaType {
case api.MediaTypeSPDX, api.MediaTypeCycloneDX:
report.MediaType = mediaType
report.SBOM = source.SBOM
default:
report.Vulnerabilities = t.transformVulnerabilities(source.Vulnerabilities)
report.Severity = t.toHighestSeverity(report.Vulnerabilities)
}
return report
}
func (t *transformer) transformVulnerabilities(source []trivy.Vulnerability) []harbor.VulnerabilityItem {
if len(source) == 0 {
return nil
}
return lo.Map(source, func(v trivy.Vulnerability, _ int) harbor.VulnerabilityItem {
return harbor.VulnerabilityItem{
ID: v.VulnerabilityID,
Pkg: v.PkgName,
Version: v.InstalledVersion,
FixVersion: v.FixedVersion,
Severity: t.toHarborSeverity(v.Severity),
Description: v.Description,
Links: t.toLinks(v.PrimaryURL, v.References),
Layer: t.toHarborLayer(v.Layer),
CweIDs: v.CweIDs,
VendorAttributes: t.toVendorAttributes(v.CVSS),
}
})
}
func (t *transformer) toLinks(primaryURL string, references []string) []string {
if primaryURL != "" {
return []string{primaryURL}
}
if references == nil {
return []string{}
}
return references
}
var trivyToHarborSeverityMap = map[string]harbor.Severity{
"CRITICAL": harbor.SevCritical,
"HIGH": harbor.SevHigh,
"MEDIUM": harbor.SevMedium,
"LOW": harbor.SevLow,
"UNKNOWN": harbor.SevUnknown,
}
func (t *transformer) toHarborLayer(tLayer *trivy.Layer) (hLayer *harbor.Layer) {
if tLayer == nil {
return
}
hLayer = &harbor.Layer{
Digest: tLayer.Digest,
DiffID: tLayer.DiffID,
}
return
}
func (t *transformer) toHarborSeverity(severity string) harbor.Severity {
harborSev, ok := trivyToHarborSeverityMap[severity]
if !ok {
slog.Warn("Unknown trivy severity", slog.String("severity", severity))
return harbor.SevUnknown
}
return harborSev
}
func (t *transformer) toVendorAttributes(info map[string]trivy.CVSSInfo) map[string]interface{} {
attributes := make(map[string]interface{})
if len(info) > 0 {
attributes["CVSS"] = info
}
return attributes
}
func (t *transformer) toHighestSeverity(vulns []harbor.VulnerabilityItem) harbor.Severity {
highest := lo.MaxBy(vulns, func(a, b harbor.VulnerabilityItem) bool {
return a.Severity > b.Severity
})
return highest.Severity
}