diff --git a/core/core/image_scan.go b/core/core/image_scan.go index 647b2b12..9886e1f3 100644 --- a/core/core/image_scan.go +++ b/core/core/image_scan.go @@ -60,29 +60,38 @@ func GetImageExceptionsFromFile(filePath string) ([]VulnerabilitiesIgnorePolicy, return policies, nil } -func getUniqueVulnerabilities(policies []VulnerabilitiesIgnorePolicy) []string { - // Create a map to store unique vulnerabilities (case-insensitive) - uniqueVulns := make(map[string]bool) +func getUniqueVulnerabilitiesAndSeverities(policies []VulnerabilitiesIgnorePolicy) ([]string, []string) { + // Create maps with slices as values to store unique vulnerabilities and severities (case-insensitive) + uniqueVulns := make(map[string][]string) + uniqueSevers := make(map[string][]string) - // Iterate over each policy + // Iterate over each policy and its vulnerabilities/severities for _, policy := range policies { - // Iterate over each vulnerability in the policy - for _, vuln := range policy.Vulnerabilities { - // Convert to uppercase for case-insensitive comparison - vulnLower := strings.ToUpper(vuln) + for _, vulnerability := range policy.Vulnerabilities { + // Add to slice directly + vulnerabilityUppercase := strings.ToUpper(vulnerability) + uniqueVulns[vulnerabilityUppercase] = append(uniqueVulns[vulnerabilityUppercase], vulnerability) + } - // Add the vulnerability to the map (only if it's not already present) - uniqueVulns[vulnLower] = true + for _, severity := range policy.Severities { + // Add to slice directly + severityUppercase := strings.ToUpper(severity) + uniqueSevers[severityUppercase] = append(uniqueSevers[severityUppercase], severity) } } - // Convert the map keys (unique vulnerabilities) to a slice + // Extract unique keys (which are unique vulnerabilities/severities) and their slices uniqueVulnsList := make([]string, 0, len(uniqueVulns)) for vuln := range uniqueVulns { uniqueVulnsList = append(uniqueVulnsList, vuln) } - return uniqueVulnsList + uniqueSeversList := make([]string, 0, len(uniqueSevers)) + for sever := range uniqueSevers { + uniqueSeversList = append(uniqueSeversList, sever) + } + + return uniqueVulnsList, uniqueSeversList } func (ks *Kubescape) ScanImage(ctx context.Context, imgScanInfo *ksmetav1.ImageScanInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) { @@ -97,6 +106,7 @@ func (ks *Kubescape) ScanImage(ctx context.Context, imgScanInfo *ksmetav1.ImageS } var vulnerabilityExceptions []string + var severityExceptions []string if imgScanInfo.Exceptions != "" { exceptionPolicies, err := GetImageExceptionsFromFile(imgScanInfo.Exceptions) if err != nil { @@ -104,10 +114,10 @@ func (ks *Kubescape) ScanImage(ctx context.Context, imgScanInfo *ksmetav1.ImageS return nil, err } - vulnerabilityExceptions = getUniqueVulnerabilities(exceptionPolicies) + vulnerabilityExceptions, severityExceptions = getUniqueVulnerabilitiesAndSeverities(exceptionPolicies) } - scanResults, err := svc.Scan(ctx, imgScanInfo.Image, creds, vulnerabilityExceptions) + scanResults, err := svc.Scan(ctx, imgScanInfo.Image, creds, vulnerabilityExceptions, severityExceptions) if err != nil { logger.L().StopError(fmt.Sprintf("Failed to scan image: %s", imgScanInfo.Image)) return nil, err diff --git a/core/core/patch.go b/core/core/patch.go index 6e8e805f..bd999cd7 100644 --- a/core/core/patch.go +++ b/core/core/patch.go @@ -37,7 +37,7 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, s Password: patchInfo.Password, } // Scan the image - scanResults, err := svc.Scan(ctx, patchInfo.Image, creds, nil) + scanResults, err := svc.Scan(ctx, patchInfo.Image, creds, nil, nil) if err != nil { return nil, err } @@ -81,7 +81,7 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, s logger.L().Start(fmt.Sprintf("Re-scanning image: %s", patchedImageName)) - scanResultsPatched, err := svc.Scan(ctx, patchedImageName, creds, nil) + scanResultsPatched, err := svc.Scan(ctx, patchedImageName, creds, nil, nil) if err != nil { return nil, err } diff --git a/core/core/scan.go b/core/core/scan.go index 9da7a375..efddccdd 100644 --- a/core/core/scan.go +++ b/core/core/scan.go @@ -257,7 +257,7 @@ func scanImages(scanType cautils.ScanTypes, scanData *cautils.OPASessionObj, ctx func scanSingleImage(ctx context.Context, img string, svc imagescan.Service, resultsHandling *resultshandling.ResultsHandler) error { - scanResults, err := svc.Scan(ctx, img, imagescan.RegistryCredentials{}, nil) + scanResults, err := svc.Scan(ctx, img, imagescan.RegistryCredentials{}, nil, nil) if err != nil { return err } diff --git a/pkg/imagescan/imagescan.go b/pkg/imagescan/imagescan.go index 77fb8fb8..9d963a02 100644 --- a/pkg/imagescan/imagescan.go +++ b/pkg/imagescan/imagescan.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "path/filepath" + "strings" "github.com/adrg/xdg" "github.com/anchore/grype/grype" @@ -117,9 +118,40 @@ type Service struct { dbCfg db.Config } -func (s *Service) Scan(ctx context.Context, userInput string, creds RegistryCredentials, exceptions []string) (*models.PresenterConfig, error) { - if exceptions == nil { - exceptions = []string{} +// Filter the remaing matches based on severity exceptions. +func filterMatchesBasedOnSeverity(severityExceptions []string, remainingMatches match.Matches, store *store.Store) match.Matches { + filteredMatches := match.NewMatches() + + for m := range remainingMatches.Enumerate() { + metadata, err := store.GetMetadata(m.Vulnerability.ID, m.Vulnerability.Namespace) + if err != nil { + continue + } + + // Skip this match if the severity of this match is present in severityExceptions. + excludeSeverity := false + for _, sever := range severityExceptions { + if strings.ToUpper(metadata.Severity) == sever { + excludeSeverity = true + continue + } + } + + if !excludeSeverity { + filteredMatches.Add(m) + } + } + + return filteredMatches +} + +func (s *Service) Scan(ctx context.Context, userInput string, creds RegistryCredentials, vulnerabilityExceptions, severityExceptions []string) (*models.PresenterConfig, error) { + if vulnerabilityExceptions == nil { + vulnerabilityExceptions = []string{} + } + + if severityExceptions == nil { + severityExceptions = []string{} } var err error @@ -139,7 +171,7 @@ func (s *Service) Scan(ctx context.Context, userInput string, creds RegistryCred } var ignoreRules []match.IgnoreRule - for _, exception := range exceptions { + for _, exception := range vulnerabilityExceptions { rule := match.IgnoreRule{ Vulnerability: exception, } @@ -159,8 +191,10 @@ func (s *Service) Scan(ctx context.Context, userInput string, creds RegistryCred } } + filteredMatches := filterMatchesBasedOnSeverity(severityExceptions, *remainingMatches, store) + pb := models.PresenterConfig{ - Matches: *remainingMatches, + Matches: filteredMatches, IgnoredMatches: ignoredMatches, Packages: packages, Context: pkgContext, diff --git a/pkg/imagescan/imagescan_test.go b/pkg/imagescan/imagescan_test.go index 397f3004..17ec6bcd 100644 --- a/pkg/imagescan/imagescan_test.go +++ b/pkg/imagescan/imagescan_test.go @@ -72,7 +72,7 @@ func TestScan(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - scanResults, err := svc.Scan(ctx, tc.image, creds, tc.exceptions) + scanResults, err := svc.Scan(ctx, tc.image, creds, tc.exceptions, nil) assert.NoError(t, err) assert.IsType(t, &models.PresenterConfig{}, scanResults)