Skip to content
This repository was archived by the owner on Oct 14, 2024. It is now read-only.

fix(scanner): enrich vulnerability data instead of using first vulnerability #1971

Merged
merged 2 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 13 additions & 15 deletions cli/presenter/apimodel.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,26 @@ func ConvertSBOMResultToPackages(result *sbom.Result) []apitypes.Package {
func ConvertVulnResultToVulnerabilities(result *vulnerabilities.Result) []apitypes.Vulnerability {
vuls := []apitypes.Vulnerability{}

if result == nil || result.MergedVulnerabilitiesByKey == nil {
if result == nil || result.VulnerabilitiesByKey == nil {
return vuls
}

for _, vulCandidates := range result.MergedVulnerabilitiesByKey {
if len(vulCandidates) < 1 {
for _, vulCandidate := range result.VulnerabilitiesByKey {
if vulCandidate.ID == "" {
continue
}

vulCandidate := vulCandidates[0]

vul := apitypes.Vulnerability{
Cvss: ConvertVulnCvssToAPIModel(vulCandidate.Vulnerability.CVSS),
Description: to.Ptr(vulCandidate.Vulnerability.Description),
Distro: ConvertVulnDistroToAPIModel(vulCandidate.Vulnerability.Distro),
Fix: ConvertVulnFixToAPIModel(vulCandidate.Vulnerability.Fix),
LayerId: to.Ptr(vulCandidate.Vulnerability.LayerID),
Links: to.Ptr(vulCandidate.Vulnerability.Links),
Package: ConvertVulnPackageToAPIModel(vulCandidate.Vulnerability.Package),
Path: to.Ptr(vulCandidate.Vulnerability.Path),
Severity: ConvertVulnSeverityToAPIModel(vulCandidate.Vulnerability.Severity),
VulnerabilityName: to.Ptr(vulCandidate.Vulnerability.ID),
Cvss: ConvertVulnCvssToAPIModel(vulCandidate.CVSS),
Description: to.Ptr(vulCandidate.Description),
Distro: ConvertVulnDistroToAPIModel(vulCandidate.Distro),
Fix: ConvertVulnFixToAPIModel(vulCandidate.Fix),
LayerId: to.Ptr(vulCandidate.LayerID),
Links: to.Ptr(vulCandidate.Links),
Package: ConvertVulnPackageToAPIModel(vulCandidate.Package),
Path: to.Ptr(vulCandidate.Path),
Severity: ConvertVulnSeverityToAPIModel(vulCandidate.Severity),
VulnerabilityName: to.Ptr(vulCandidate.ID),
}
vuls = append(vuls, vul)
}
Expand Down
172 changes: 81 additions & 91 deletions cli/presenter/apimodel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,108 +161,98 @@ func Test_ConvertVulnResultToVulnerabilities(t *testing.T) {
name: "Vuls",
args: args{
result: &vulnerabilities.Result{
MergedVulnerabilitiesByKey: map[vulnerabilities.VulnerabilityKey][]vulnerabilities.MergedVulnerability{
VulnerabilitiesByKey: map[vulnerabilities.VulnerabilityKey]vulnerabilities.Vulnerability{
"vulkey1": {
{
ID: "id1",
Vulnerability: vulnerabilities.Vulnerability{
ID: "CVE-test-test-foo",
Description: "testbleed",
Links: []string{"link1", "link2"},
Distro: vulnerabilities.Distro{
Name: "distro1",
Version: "distrov1",
IDLike: []string{"IDLike1", "IDLike2"},
},
CVSS: []vulnerabilities.CVSS{
{
Version: "v1",
Vector: "vector1",
Metrics: vulnerabilities.CvssMetrics{
BaseScore: 1,
ExploitabilityScore: nil,
ImpactScore: nil,
},
},
{
Version: "v2",
Vector: "vector2",
Metrics: vulnerabilities.CvssMetrics{
BaseScore: 2,
ExploitabilityScore: to.Ptr(2.1),
ImpactScore: to.Ptr(2.2),
},
},
},
Fix: vulnerabilities.Fix{
Versions: []string{"fv1", "fv2"},
State: "fixed",
ID: "CVE-test-test-foo",
Description: "testbleed",
Links: []string{"link1", "link2"},
Distro: vulnerabilities.Distro{
Name: "distro1",
Version: "distrov1",
IDLike: []string{"IDLike1", "IDLike2"},
},
CVSS: []vulnerabilities.CVSS{
{
Version: "v1",
Vector: "vector1",
Metrics: vulnerabilities.CvssMetrics{
BaseScore: 1,
ExploitabilityScore: nil,
ImpactScore: nil,
},
Severity: string(apitypes.CRITICAL),
Package: vulnerabilities.Package{
Name: "package1",
Version: "pv1",
Type: "pt1",
Language: "pl1",
Licenses: []string{"plic1", "plic2"},
CPEs: []string{"cpe1", "cpe2"},
PURL: "purl1",
},
{
Version: "v2",
Vector: "vector2",
Metrics: vulnerabilities.CvssMetrics{
BaseScore: 2,
ExploitabilityScore: to.Ptr(2.1),
ImpactScore: to.Ptr(2.2),
},
LayerID: "lid1",
Path: "path1",
},
},
Fix: vulnerabilities.Fix{
Versions: []string{"fv1", "fv2"},
State: "fixed",
},
Severity: string(apitypes.CRITICAL),
Package: vulnerabilities.Package{
Name: "package1",
Version: "pv1",
Type: "pt1",
Language: "pl1",
Licenses: []string{"plic1", "plic2"},
CPEs: []string{"cpe1", "cpe2"},
PURL: "purl1",
},
LayerID: "lid1",
Path: "path1",
},
"vulkey2": {
{
ID: "id2",
Vulnerability: vulnerabilities.Vulnerability{
ID: "CVE-test-test-bar",
Description: "solartest",
Links: []string{"link3", "link4"},
Distro: vulnerabilities.Distro{
Name: "distro2",
Version: "distrov2",
IDLike: []string{"IDLike3", "IDLike4"},
},
CVSS: []vulnerabilities.CVSS{
{
Version: "v3",
Vector: "vector3",
Metrics: vulnerabilities.CvssMetrics{
BaseScore: 3,
ExploitabilityScore: nil,
ImpactScore: nil,
},
},
{
Version: "v4",
Vector: "vector4",
Metrics: vulnerabilities.CvssMetrics{
BaseScore: 4,
ExploitabilityScore: to.Ptr(4.1),
ImpactScore: to.Ptr(4.2),
},
},
},
Fix: vulnerabilities.Fix{
Versions: []string{"fv3", "fv4"},
State: "not-fixed",
ID: "CVE-test-test-bar",
Description: "solartest",
Links: []string{"link3", "link4"},
Distro: vulnerabilities.Distro{
Name: "distro2",
Version: "distrov2",
IDLike: []string{"IDLike3", "IDLike4"},
},
CVSS: []vulnerabilities.CVSS{
{
Version: "v3",
Vector: "vector3",
Metrics: vulnerabilities.CvssMetrics{
BaseScore: 3,
ExploitabilityScore: nil,
ImpactScore: nil,
},
Severity: string(apitypes.HIGH),
Package: vulnerabilities.Package{
Name: "package2",
Version: "pv2",
Type: "pt2",
Language: "pl2",
Licenses: []string{"plic3", "plic4"},
CPEs: []string{"cpe3", "cpe4"},
PURL: "purl2",
},
{
Version: "v4",
Vector: "vector4",
Metrics: vulnerabilities.CvssMetrics{
BaseScore: 4,
ExploitabilityScore: to.Ptr(4.1),
ImpactScore: to.Ptr(4.2),
},
LayerID: "lid2",
Path: "path2",
},
},
Fix: vulnerabilities.Fix{
Versions: []string{"fv3", "fv4"},
State: "not-fixed",
},
Severity: string(apitypes.HIGH),
Package: vulnerabilities.Package{
Name: "package2",
Version: "pv2",
Type: "pt2",
Language: "pl2",
Licenses: []string{"plic3", "plic4"},
CPEs: []string{"cpe3", "cpe4"},
PURL: "purl2",
},
LayerID: "lid2",
Path: "path2",
},
"vulkey3": {},
},
Expand Down
11 changes: 4 additions & 7 deletions scanner/families/exploits/family.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,14 @@ func (e Exploits) Run(ctx context.Context, res *families.Results) (*types.Result
}

// create a comma separated representation of cveIDs array, as an input for the exploits scanners.
func getCVEIDsFromVulnerabilitiesResults(vulnerabilities *vulnerabilitytypes.Result) string {
if vulnerabilities == nil {
func getCVEIDsFromVulnerabilitiesResults(result *vulnerabilitytypes.Result) string {
if result == nil {
return ""
}

cvesMap := make(map[string]bool)

for _, mergedVulnerabilities := range vulnerabilities.MergedVulnerabilitiesByKey {
for _, vulnerability := range mergedVulnerabilities {
cvesMap[vulnerability.Vulnerability.ID] = true
}
for _, vulnerability := range result.VulnerabilitiesByKey {
cvesMap[vulnerability.ID] = true
}

cves := strings.Join(to.Keys(cvesMap), ",")
Expand Down
50 changes: 11 additions & 39 deletions scanner/families/exploits/family_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ func Test_getCVEIDsFromVulnerabilitiesResults(t *testing.T) {
wantCves: "",
},
{
name: "nil MergedVulnerabilitiesByKey",
name: "nil VulnerabilitiesByKey",
args: args{
vulnResults: &vulnerabilitytypes.Result{
MergedVulnerabilitiesByKey: nil,
VulnerabilitiesByKey: nil,
},
},
wantCves: "",
Expand All @@ -52,7 +52,7 @@ func Test_getCVEIDsFromVulnerabilitiesResults(t *testing.T) {
name: "no vulnerabilities",
args: args{
vulnResults: &vulnerabilitytypes.Result{
MergedVulnerabilitiesByKey: map[vulnerabilitytypes.VulnerabilityKey][]vulnerabilitytypes.MergedVulnerability{},
VulnerabilitiesByKey: map[vulnerabilitytypes.VulnerabilityKey]vulnerabilitytypes.Vulnerability{},
},
},
wantCves: "",
Expand All @@ -61,55 +61,27 @@ func Test_getCVEIDsFromVulnerabilitiesResults(t *testing.T) {
name: "sanity",
args: args{
vulnResults: &vulnerabilitytypes.Result{
MergedVulnerabilitiesByKey: map[vulnerabilitytypes.VulnerabilityKey][]vulnerabilitytypes.MergedVulnerability{
"vul1": {
{
Vulnerability: vulnerabilitytypes.Vulnerability{ID: "cve1"},
},
{
Vulnerability: vulnerabilitytypes.Vulnerability{ID: "cve2"},
},
},
"vul2": {
{
Vulnerability: vulnerabilitytypes.Vulnerability{ID: "cve3"},
},
{
Vulnerability: vulnerabilitytypes.Vulnerability{ID: "cve4"},
},
},
VulnerabilitiesByKey: map[vulnerabilitytypes.VulnerabilityKey]vulnerabilitytypes.Vulnerability{
"vul1": {ID: "cve1"},
"vul2": {ID: "cve3"},
},
Source: vulnerabilitytypes.Source{},
},
},
wantCves: "cve1,cve2,cve3,cve4",
wantCves: "cve1,cve3",
},
{
name: "same cve id in different vulnerabilities",
args: args{
vulnResults: &vulnerabilitytypes.Result{
MergedVulnerabilitiesByKey: map[vulnerabilitytypes.VulnerabilityKey][]vulnerabilitytypes.MergedVulnerability{
"vul1": {
{
Vulnerability: vulnerabilitytypes.Vulnerability{ID: "cve1"},
},
{
Vulnerability: vulnerabilitytypes.Vulnerability{ID: "cve2"},
},
},
"vul2": {
{
Vulnerability: vulnerabilitytypes.Vulnerability{ID: "cve1"},
},
{
Vulnerability: vulnerabilitytypes.Vulnerability{ID: "cve4"},
},
},
VulnerabilitiesByKey: map[vulnerabilitytypes.VulnerabilityKey]vulnerabilitytypes.Vulnerability{
"vul1": {ID: "cve1"},
"vul2": {ID: "cve1"},
},
Source: vulnerabilitytypes.Source{},
},
},
wantCves: "cve1,cve2,cve4",
wantCves: "cve1",
},
}
for _, tt := range tests {
Expand Down
30 changes: 13 additions & 17 deletions scanner/families/vulnerabilities/types/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ type Source struct {
}

type Result struct {
Metadata families.ScanMetadata `json:"Metadata"`
Source Source `json:"Source"`
MergedVulnerabilitiesByKey map[VulnerabilityKey][]MergedVulnerability `json:"MergedVulnerabilitiesByKey"`
Metadata families.ScanMetadata `json:"Metadata"`
Source Source `json:"Source"`
VulnerabilitiesByKey map[VulnerabilityKey]Vulnerability `json:"VulnerabilitiesByKey"`
}

func NewResult() *Result {
return &Result{
MergedVulnerabilitiesByKey: make(map[VulnerabilityKey][]MergedVulnerability),
VulnerabilitiesByKey: make(map[VulnerabilityKey]Vulnerability),
}
}

Expand Down Expand Up @@ -74,10 +74,10 @@ func (r *Result) GetSourceImageInfo() (*apitypes.ContainerImageInfo, error) {
return containerImageInfo, nil
}

// ToSlice returns MergedResults in a slice format and not by key.
func (r *Result) ToSlice() [][]MergedVulnerability {
ret := make([][]MergedVulnerability, 0)
for _, vulnerabilities := range r.MergedVulnerabilitiesByKey {
// ToSlice returns Result in a slice format and not by key.
func (r *Result) ToSlice() []Vulnerability {
ret := make([]Vulnerability, 0)
for _, vulnerabilities := range r.VulnerabilitiesByKey {
ret = append(ret, vulnerabilities)
}

Expand All @@ -92,19 +92,15 @@ func (r *Result) Merge(meta families.ScanInputMetadata, result *ScannerResult) {
return
}

otherVulnerabilityByKey := toVulnerabilityByKey(result.Vulnerabilities)
for _, vulnerability := range result.Vulnerabilities {
key := NewVulnerabilityKey(vulnerability)

// go over other vulnerabilities list
// 1. merge mutual vulnerabilities
// 2. add non mutual vulnerabilities
for key, otherVulnerability := range otherVulnerabilityByKey {
// look for other vulnerability key in the current merged vulnerabilities list
if mergedVulnerabilities, ok := r.MergedVulnerabilitiesByKey[key]; !ok {
// add non mutual vulnerability
if existingVulnerability, ok := r.VulnerabilitiesByKey[key]; !ok {
log.Debugf("Adding new vulnerability results from %v. key=%v", result.Scanner, key)
r.MergedVulnerabilitiesByKey[key] = []MergedVulnerability{*NewMergedVulnerability(otherVulnerability, result.Scanner)}
r.VulnerabilitiesByKey[key] = vulnerability
} else {
r.MergedVulnerabilitiesByKey[key] = handleVulnerabilityWithExistingKey(mergedVulnerabilities, otherVulnerability, result.Scanner)
r.VulnerabilitiesByKey[key] = handleVulnerabilityWithExistingKey(existingVulnerability, vulnerability)
}
}

Expand Down
Loading