Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support Additional Metadata + CVSS Score Logic Update #2199

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
Prev Previous commit
Next Next commit
update logic
  • Loading branch information
Hacks4Snacks authored and hacks4snacks committed Jul 23, 2024
commit d92f9736e92b5d97855930236d674a0d107371c5
2 changes: 1 addition & 1 deletion deploy/helm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ Keeps security report resources updated
| targetNamespaces | string | `""` | targetNamespace defines where you want trivy-operator to operate. By default, it's a blank string to select all namespaces, but you can specify another namespace, or a comma separated list of namespaces. |
| targetWorkloads | string | `"pod,replicaset,replicationcontroller,statefulset,daemonset,cronjob,job"` | targetWorkloads is a comma seperated list of Kubernetes workload resources to be included in the vulnerability and config-audit scans if left blank, all workload resources will be scanned |
| tolerations | list | `[]` | tolerations set the operator tolerations |
| trivy.additionalVulnerabilityReportFields | string | `""` | additionalVulnerabilityReportFields is a comma separated list of additional fields which can be added to the VulnerabilityReport. Supported parameters: Description, Links, CVSS, Target, Class, PackagePath, PackageType, and SeveritySource |
| trivy.additionalVulnerabilityReportFields | string | `""` | additionalVulnerabilityReportFields is a comma separated list of additional fields which can be added to the VulnerabilityReport. Supported parameters: Description, Links, CVSS, Target, Class, PackagePath, PackageType, SeveritySource, and DataSource |
| trivy.clientServerSkipUpdate | bool | `false` | clientServerSkipUpdate is the flag to enable skip databases update for Trivy client. Only applicable in ClientServer mode. |
| trivy.command | string | `"image"` | command. One of `image`, `filesystem` or `rootfs` scanning, depending on the target type required for the scan. For 'filesystem' and `rootfs` scanning, ensure that the `trivyOperator.scanJobPodTemplateContainerSecurityContext` is configured to run as the root user (runAsUser = 0). |
| trivy.createConfig | bool | `true` | createConfig indicates whether to create config objects |
Expand Down
13 changes: 13 additions & 0 deletions deploy/helm/crds/aquasecurity.github.io_vulnerabilityreports.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,19 @@ spec:
vulnerabilityID:
description: VulnerabilityID the vulnerability identifier.
type: string
dataSource:
description: DataSource is the data source of the vulnerability data.
type: object
properties:
id:
description: ID is the identifier of the data source.
type: string
name:
description: Name is the name of the data source.
type: string
url:
description: URL is the URL of the data source.
type: string
required:
- fixedVersion
- installedVersion
Expand Down
2 changes: 1 addition & 1 deletion deploy/helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ trivy:
priorityClassName: ""

# -- additionalVulnerabilityReportFields is a comma separated list of additional fields which
# can be added to the VulnerabilityReport. Supported parameters: Description, Links, CVSS, Target, Class, PackagePath and PackageType
# can be added to the VulnerabilityReport. Supported parameters: Description, Links, CVSS, Target, Class, PackagePath, PackageType, SeveritySource, and DataSource
additionalVulnerabilityReportFields: ""

# -- httpProxy is the HTTP proxy used by Trivy to download the vulnerabilities database from GitHub.
Expand Down
13 changes: 13 additions & 0 deletions deploy/static/trivy-operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2906,6 +2906,19 @@ spec:
vulnerabilityID:
description: VulnerabilityID the vulnerability identifier.
type: string
dataSource:
description: DataSource is the data source of the vulnerability data.
type: object
properties:
id:
description: ID is the identifier of the data source.
type: string
name:
description: Name is the name of the data source.
type: string
url:
description: URL is the URL of the data source.
type: string
required:
- fixedVersion
- installedVersion
Expand Down
8 changes: 8 additions & 0 deletions pkg/apis/aquasecurity/v1alpha1/vulnerability_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ type OS struct {
Name string `json:"name,omitempty"`
}

// DataSource represents the source of vulnerability data.
type DataSource struct {
ID types.SourceID `json:"id,omitempty"`
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
}

// Vulnerability is the spec for a vulnerability record.
type Vulnerability struct {
// VulnerabilityID the vulnerability identifier.
Expand Down Expand Up @@ -112,6 +119,7 @@ type Vulnerability struct {
PackageType string `json:"packageType,omitempty"`
PkgPath string `json:"packagePath,omitempty"`
SeveritySource types.SourceID `json:"severitySource,omitempty"`
DataSource *DataSource `json:"dataSource,omitempty"`
}

// +kubebuilder:object:root=true
Expand Down
2 changes: 2 additions & 0 deletions pkg/plugins/trivy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ func (c Config) GetAdditionalVulnerabilityReportFields() vulnerabilityreport.Add
addFields.PkgPath = true
case "SeveritySource":
addFields.SeveritySource = true
case "DataSource":
addFields.DataSource = true
}
}
return addFields
Expand Down
5 changes: 3 additions & 2 deletions pkg/plugins/trivy/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ func TestConfig_GetAdditionalVulnerabilityReportFields(t *testing.T) {
name: "all additional fields are set",
configData: Config{PluginConfig: trivyoperator.PluginConfig{
Data: map[string]string{
"trivy.additionalVulnerabilityReportFields": "PackageType,PkgPath,Class,Target,Links,Description,CVSS,SeveritySource",
"trivy.additionalVulnerabilityReportFields": "PackageType,PkgPath,Class,Target,Links,Description,CVSS,SeveritySource,DataSource",
},
}},
additionalFields: vulnerabilityreport.AdditionalFields{Description: true, Links: true, CVSS: true, Class: true, PackageType: true, PkgPath: true, Target: true, SeveritySource: true},
additionalFields: vulnerabilityreport.AdditionalFields{Description: true, Links: true, CVSS: true, Class: true, PackageType: true, PkgPath: true, Target: true, SeveritySource: true, DataSource: true},
},
{
name: "some additional fields are set",
Expand All @@ -125,6 +125,7 @@ func TestConfig_GetAdditionalVulnerabilityReportFields(t *testing.T) {
assert.True(t, addFields.Class == tc.additionalFields.Class)
assert.True(t, addFields.Links == tc.additionalFields.Links)
assert.True(t, addFields.SeveritySource == tc.additionalFields.SeveritySource)
assert.True(t, addFields.DataSource == tc.additionalFields.DataSource)
})
}
}
Expand Down
47 changes: 38 additions & 9 deletions pkg/vulnerabilityreport/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,16 @@ type AdditionalFields struct {
PackageType bool
PkgPath bool
SeveritySource bool
DataSource bool
}

func GetVulnerabilitiesFromScanResult(report ty.Result, addFields AdditionalFields) []v1alpha1.Vulnerability {
vulnerabilities := make([]v1alpha1.Vulnerability, 0)

preferredSources := []string{
"nvd", "redhat", "debian", "ubuntu", "alpine", "amazon", "oracleoval", "susecvrf", "photon",
"archlinux", "alma", "rocky", "cblmariner", "rubysec", "phpsecurityadvisories", "nodejssecuritywg",
"ghsa", "glad", "osv", "k8svulndb",
}
for _, sr := range report.Vulnerabilities {
var pd, lmd string
if sr.PublishedDate != nil {
Expand All @@ -149,6 +154,18 @@ func GetVulnerabilitiesFromScanResult(report ty.Result, addFields AdditionalFiel
if sr.LastModifiedDate != nil {
lmd = sr.LastModifiedDate.Format(time.RFC3339)
}

// Get CVSS scores mapped by vendor
cvssV3 := GetCvssV3(sr.CVSS)

// Determine the severity source and CVSS score
severitySource := string(sr.SeveritySource)
score, updatedSeveritySource := GetScoreFromCVSS(cvssV3, severitySource, preferredSources)

if severitySource == "" {
severitySource = updatedSeveritySource
}

vulnerability := v1alpha1.Vulnerability{
VulnerabilityID: sr.VulnerabilityID,
Resource: sr.PkgName,
Expand All @@ -160,7 +177,7 @@ func GetVulnerabilitiesFromScanResult(report ty.Result, addFields AdditionalFiel
Title: sr.Title,
PrimaryLink: sr.PrimaryURL,
Links: []string{},
Score: GetScoreFromCVSS(GetCvssV3(sr.CVSS), string(sr.SeveritySource)),
Score: score,
}

if addFields.Description {
Expand All @@ -185,7 +202,14 @@ func GetVulnerabilitiesFromScanResult(report ty.Result, addFields AdditionalFiel
vulnerability.PkgPath = sr.PkgPath
}
if addFields.SeveritySource {
vulnerability.SeveritySource = sr.SeveritySource
vulnerability.SeveritySource = types.SourceID(severitySource)
}
if addFields.DataSource {
vulnerability.DataSource = &v1alpha1.DataSource{
ID: sr.DataSource.ID,
Name: sr.DataSource.Name,
URL: sr.DataSource.URL,
}
}

vulnerabilities = append(vulnerabilities, vulnerability)
Expand All @@ -206,15 +230,20 @@ func GetCvssV3(findingCvss types.VendorCVSS) map[string]*CVSS {
return cvssV3
}

// Get CVSS from same source as severity, if not found, get from NVD
func GetScoreFromCVSS(cvsss map[string]*CVSS, severitySource string) *float64 {
// Get CVSS from same source as severity, if not found, follow the order of preferredSources
func GetScoreFromCVSS(cvsss map[string]*CVSS, severitySource string, preferredSources []string) (*float64, string) {
if score, exists := cvsss[severitySource]; exists {
return score.V3Score
return score.V3Score, severitySource
}
if score, exists := cvsss["nvd"]; exists {
return score.V3Score
for _, source := range preferredSources {
if score, exists := cvsss[source]; exists {
return score.V3Score, source
}
}
return nil
for source, score := range cvsss {
return score.V3Score, source
}
return nil, ""
}

type CVSS struct {
Expand Down