-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
csaf.go
113 lines (99 loc) · 3.45 KB
/
csaf.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
package vex
import (
csaf "github.com/csaf-poc/csaf_distribution/v3/csaf"
"github.com/package-url/packageurl-go"
"github.com/samber/lo"
"go.uber.org/zap"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/purl"
"github.com/aquasecurity/trivy/pkg/types"
)
type CSAF struct {
advisory csaf.Advisory
logger *zap.SugaredLogger
}
func newCSAF(advisory csaf.Advisory) VEX {
return &CSAF{
advisory: advisory,
logger: log.Logger.With(zap.String("VEX format", "CSAF")),
}
}
func (v *CSAF) Filter(vulns []types.DetectedVulnerability) []types.DetectedVulnerability {
return lo.Filter(vulns, func(vuln types.DetectedVulnerability, _ int) bool {
found, ok := lo.Find(v.advisory.Vulnerabilities, func(item *csaf.Vulnerability) bool {
return string(*item.CVE) == vuln.VulnerabilityID
})
if !ok {
return true
}
return v.affected(found, vuln.PkgIdentifier.PURL)
})
}
func (v *CSAF) affected(vuln *csaf.Vulnerability, pkgURL *packageurl.PackageURL) bool {
if pkgURL == nil || vuln.ProductStatus == nil {
return true
}
var status Status
switch {
case v.affectedByStatus(vuln.ProductStatus.KnownNotAffected, pkgURL):
status = StatusNotAffected
case v.affectedByStatus(vuln.ProductStatus.Fixed, pkgURL):
status = StatusFixed
}
if status != "" {
v.logger.Infow("Filtered out the detected vulnerability",
zap.String("vulnerability-id", string(*vuln.CVE)),
zap.String("status", string(status)))
return false
}
return true
}
// affectedByStatus returns true if a package (identified by a given PackageURL) belongs to certain status
// (e.g. KnownNotAffected, Fixed, etc.) which products are provided.
func (v *CSAF) affectedByStatus(statusProducts *csaf.Products, pkgURL *packageurl.PackageURL) bool {
for _, product := range lo.FromPtr(statusProducts) {
helpers := v.collectProductIdentificationHelpers(lo.FromPtr(product))
purls := lo.FilterMap(helpers, func(helper *csaf.ProductIdentificationHelper, _ int) (*purl.PackageURL, bool) {
if helper == nil || helper.PURL == nil {
return nil, false
}
p, err := purl.FromString(string(*helper.PURL))
if err != nil {
v.logger.Errorw("Invalid PURL", zap.String("purl", string(*helper.PURL)), zap.Error(err))
return nil, false
}
return p, true
})
for _, p := range purls {
if p.Match(pkgURL) {
return true
}
}
}
return false
}
// collectProductIdentificationHelpers collects ProductIdentificationHelpers from the given CSAF product.
func (v *CSAF) collectProductIdentificationHelpers(product csaf.ProductID) []*csaf.ProductIdentificationHelper {
helpers := v.advisory.ProductTree.CollectProductIdentificationHelpers(product)
// Iterate over relationships looking for sub-products that might be part of the original product.
var subProducts csaf.Products
if rels := v.advisory.ProductTree.RelationShips; rels != nil {
for _, rel := range lo.FromPtr(rels) {
if rel != nil {
switch lo.FromPtr(rel.Category) {
case csaf.CSAFRelationshipCategoryDefaultComponentOf,
csaf.CSAFRelationshipCategoryInstalledOn,
csaf.CSAFRelationshipCategoryInstalledWith:
if fpn := rel.FullProductName; fpn != nil && fpn.ProductID != nil &&
lo.FromPtr(fpn.ProductID) == product {
subProducts = append(subProducts, rel.ProductReference)
}
}
}
}
}
for _, subProduct := range subProducts {
helpers = append(helpers, v.advisory.ProductTree.CollectProductIdentificationHelpers(lo.FromPtr(subProduct))...)
}
return helpers
}