From 046da1940c8f1fe5892b4263505ee7e88ea3d7b0 Mon Sep 17 00:00:00 2001 From: VaibhavMalik4187 Date: Tue, 16 Jan 2024 14:26:52 +0530 Subject: [PATCH] Added support for targets in image exceptions This commit introduces the ability to specify targets in image exceptions. Each target will have the following 4 attributes: 1. Registry 2. Organization 3. ImageName 4. ImageTag These attributes will be used to match against the canonical image name of the image to be scanned. The vulnerabilites and the severities specified in the VulnerabilitiesIgnorePolicy object will be considered only if the image to be scanned matches the targets specified for that policy. Regular expressions can also be used to specify the image attributes. Signed-off-by: VaibhavMalik4187 --- core/core/image_scan.go | 82 ++++++++++++++++++++--- core/meta/datastructures/v1/image_scan.go | 9 +-- 2 files changed, 76 insertions(+), 15 deletions(-) diff --git a/core/core/image_scan.go b/core/core/image_scan.go index 9886e1f3..fc944896 100644 --- a/core/core/image_scan.go +++ b/core/core/image_scan.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "os" + "regexp" "strings" "github.com/anchore/grype/grype/presenter/models" @@ -60,23 +61,82 @@ func GetImageExceptionsFromFile(filePath string) ([]VulnerabilitiesIgnorePolicy, return policies, nil } -func getUniqueVulnerabilitiesAndSeverities(policies []VulnerabilitiesIgnorePolicy) ([]string, []string) { +func getAttributesFromImage(imgName string) (Attributes, error) { + canonicalImageName, err := cautils.NormalizeImageName(imgName) + if err != nil { + return Attributes{}, err + } + + tokens := strings.Split(canonicalImageName, "/") + registry := tokens[0] + organization := tokens[1] + + imageNameAndTag := strings.Split(tokens[2], ":") + imageName := imageNameAndTag[0] + + // Intialize the image tag with default value + imageTag := "latest" + if len(imageNameAndTag) > 1 { + imageTag = imageNameAndTag[1] + } + + attributes := Attributes{ + Registry: registry, + Organization: organization, + ImageName: imageName, + ImageTag: imageTag, + } + + return attributes, nil +} + +func regexStringMatch(pattern, target string) bool { + re, err := regexp.Compile(pattern) + if err != nil { + logger.L().StopError(fmt.Sprintf("Failed to generate regular expression: %s", err)) + return false + } + + if re.MatchString(target) { + return true + } + + return false +} + +func isTargetImage(targets []Target, attributes Attributes) bool { + for _, target := range targets { + return regexStringMatch(target.Attributes.Registry, attributes.Registry) && regexStringMatch(target.Attributes.Organization, attributes.Organization) && regexStringMatch(target.Attributes.ImageName, attributes.ImageName) && regexStringMatch(target.Attributes.ImageTag, attributes.ImageTag) + } + + return false +} + +func getUniqueVulnerabilitiesAndSeverities(policies []VulnerabilitiesIgnorePolicy, image string) ([]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) + imageAttributes, err := getAttributesFromImage(image) + if err != nil { + logger.L().StopError(fmt.Sprintf("Failed to generate image attributes: %s", err)) + } + // Iterate over each policy and its vulnerabilities/severities for _, policy := range policies { - for _, vulnerability := range policy.Vulnerabilities { - // Add to slice directly - vulnerabilityUppercase := strings.ToUpper(vulnerability) - uniqueVulns[vulnerabilityUppercase] = append(uniqueVulns[vulnerabilityUppercase], vulnerability) - } + // Include the exceptions only if the image is one of the targets + if isTargetImage(policy.Targets, imageAttributes) { + for _, vulnerability := range policy.Vulnerabilities { + // Add to slice directly + vulnerabilityUppercase := strings.ToUpper(vulnerability) + uniqueVulns[vulnerabilityUppercase] = append(uniqueVulns[vulnerabilityUppercase], vulnerability) + } - for _, severity := range policy.Severities { - // Add to slice directly - severityUppercase := strings.ToUpper(severity) - uniqueSevers[severityUppercase] = append(uniqueSevers[severityUppercase], severity) + for _, severity := range policy.Severities { + // Add to slice directly + severityUppercase := strings.ToUpper(severity) + uniqueSevers[severityUppercase] = append(uniqueSevers[severityUppercase], severity) + } } } @@ -114,7 +174,7 @@ func (ks *Kubescape) ScanImage(ctx context.Context, imgScanInfo *ksmetav1.ImageS return nil, err } - vulnerabilityExceptions, severityExceptions = getUniqueVulnerabilitiesAndSeverities(exceptionPolicies) + vulnerabilityExceptions, severityExceptions = getUniqueVulnerabilitiesAndSeverities(exceptionPolicies, imgScanInfo.Image) } scanResults, err := svc.Scan(ctx, imgScanInfo.Image, creds, vulnerabilityExceptions, severityExceptions) diff --git a/core/meta/datastructures/v1/image_scan.go b/core/meta/datastructures/v1/image_scan.go index 3bf3bb6c..17e11b1a 100644 --- a/core/meta/datastructures/v1/image_scan.go +++ b/core/meta/datastructures/v1/image_scan.go @@ -1,8 +1,9 @@ package v1 type ImageScanInfo struct { - Username string - Password string - Image string - Exceptions string + Username string + Password string + Image string + Exceptions string + CanonicalImageName string }