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

Store summary cr objects #144

Merged
merged 11 commits into from
Aug 18, 2023
1 change: 1 addition & 0 deletions core/ports/repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
type CVERepository interface {
GetCVE(ctx context.Context, name, SBOMCreatorVersion, CVEScannerVersion, CVEDBVersion string) (domain.CVEManifest, error)
StoreCVE(ctx context.Context, cve domain.CVEManifest, withRelevancy bool) error
StoreCVESummary(ctx context.Context, cve domain.CVEManifest, cvep domain.CVEManifest, withRelevancy bool) error
}

// SBOMRepository is the port implemented by adapters to be used in ScanService to store SBOMs
Expand Down
10 changes: 10 additions & 0 deletions core/services/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ func (s *ScanService) ScanCVE(ctx context.Context) error {
logger.L().Ctx(ctx).Warning("error storing CVE", helpers.Error(err),
helpers.String("imageSlug", workload.ImageSlug))
}
err = s.cveRepository.StoreCVESummary(ctx, cve, domain.CVEManifest{}, false)
if err != nil {
logger.L().Ctx(ctx).Warning("error storing CVE summary", helpers.Error(err),
helpers.String("imageSlug", workload.ImageSlug))
}
}
}

Expand Down Expand Up @@ -224,6 +229,11 @@ func (s *ScanService) ScanCVE(ctx context.Context) error {
logger.L().Ctx(ctx).Warning("error storing CVEp", helpers.Error(err),
helpers.String("instanceID", workload.InstanceID))
}
err = s.cveRepository.StoreCVESummary(ctx, cve, cvep, true)
if err != nil {
logger.L().Ctx(ctx).Warning("error storing CVE summary", helpers.Error(err),
helpers.String("imageSlug", workload.ImageSlug))
}
}
}

Expand Down
110 changes: 50 additions & 60 deletions repositories/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (a *APIServerStore) GetCVE(ctx context.Context, name, SBOMCreatorVersion, C
}, nil
}

func (a *APIServerStore) storeCVEWithFullContent(ctx context.Context, cve domain.CVEManifest, withRelevancy bool) error {
func (a *APIServerStore) StoreCVE(ctx context.Context, cve domain.CVEManifest, withRelevancy bool) error {
_, span := otel.Tracer("").Start(ctx, "APIServerStore.StoreCVEWithFullContent")
defer span.End()

Expand Down Expand Up @@ -183,22 +183,22 @@ func (a *APIServerStore) storeCVEWithFullContent(ctx context.Context, cve domain
return nil
}

func parseVulnerabilitiesComponents(name, namespace string, withRelevancy bool) v1beta1.VulnerabilitiesComponents {
func parseVulnerabilitiesComponents(cve domain.CVEManifest, cvep domain.CVEManifest, namespace string, withRelevancy bool) v1beta1.VulnerabilitiesComponents {
vulComp := v1beta1.VulnerabilitiesComponents{}

if withRelevancy {
vulComp.WorkloadVulnerabilitiesObj.Name = name
vulComp.WorkloadVulnerabilitiesObj.Name = cvep.Name
vulComp.WorkloadVulnerabilitiesObj.Kind = vulnerabilityManifestSummaryKindPlural
vulComp.WorkloadVulnerabilitiesObj.Namespace = namespace
} else {
vulComp.ImageVulnerabilitiesObj.Name = name
vulComp.ImageVulnerabilitiesObj.Kind = vulnerabilityManifestSummaryKindPlural
vulComp.ImageVulnerabilitiesObj.Namespace = namespace
}
vulComp.ImageVulnerabilitiesObj.Name = cve.Name
vulComp.ImageVulnerabilitiesObj.Kind = vulnerabilityManifestSummaryKindPlural
vulComp.ImageVulnerabilitiesObj.Namespace = namespace

return vulComp
}

func parseSeverities(cve domain.CVEManifest, withRelevancy bool) v1beta1.SeveritySummary {
func parseSeverities(cve domain.CVEManifest, cvep domain.CVEManifest, withRelevancy bool) v1beta1.SeveritySummary {
critical := 0
criticalRelevant := 0
high := 0
Expand All @@ -215,40 +215,34 @@ func parseSeverities(cve domain.CVEManifest, withRelevancy bool) v1beta1.Severit
for i := range cve.Content.Matches {
switch cve.Content.Matches[i].Vulnerability.Severity {
case domain.CriticalSeverity:
if withRelevancy {
criticalRelevant += 1
} else {
critical += 1
}
critical += 1
case domain.HighSeverity:
if withRelevancy {
highRelevant += 1
} else {
high += 1
}
high += 1
case domain.MediumSeverity:
if withRelevancy {
mediumRelevant += 1
} else {
medium += 1
}
medium += 1
case domain.LowSeverity:
if withRelevancy {
lowRelevant += 1
} else {
low += 1
}
low += 1
case domain.NegligibleSeverity:
if withRelevancy {
negligibleRelevant += 1
} else {
negligible += 1
}
negligible += 1
case domain.UnknownSeverity:
if withRelevancy {
unknown += 1
}
}
if withRelevancy {
for i := range cvep.Content.Matches {
switch cvep.Content.Matches[i].Vulnerability.Severity {
case domain.CriticalSeverity:
criticalRelevant += 1
case domain.HighSeverity:
highRelevant += 1
case domain.MediumSeverity:
mediumRelevant += 1
case domain.LowSeverity:
lowRelevant += 1
case domain.NegligibleSeverity:
negligibleRelevant += 1
case domain.UnknownSeverity:
unknownRelevant += 1
} else {
unknown += 1
}
}
}
Expand Down Expand Up @@ -323,8 +317,17 @@ func GetCVESummaryK8sResourceName(ctx context.Context) (string, error) {
return fmt.Sprintf(vulnSummaryContNameFormat, kind, name, contName), nil
}

func (a *APIServerStore) storeCVESummary(ctx context.Context, cve domain.CVEManifest, withRelevancy bool) error {
_, span := otel.Tracer("").Start(ctx, "APIServerStore.storeCVESummary")
func GetCVESummaryK8sResourceNamespace(ctx context.Context) (string, error) {
workload, ok := ctx.Value(domain.WorkloadKey{}).(domain.ScanCommand)
if !ok {
return "", domain.ErrCastingWorkload
}

return wlid.GetNamespaceFromWlid(workload.Wlid), nil
}

func (a *APIServerStore) StoreCVESummary(ctx context.Context, cve domain.CVEManifest, cvep domain.CVEManifest, withRelevancy bool) error {
_, span := otel.Tracer("").Start(ctx, "APIServerStore.StoreCVESummary")
defer span.End()

if cve.Name == "" {
Expand All @@ -345,6 +348,10 @@ func (a *APIServerStore) storeCVESummary(ctx context.Context, cve domain.CVEMani
if err != nil {
return err
}
workloadNamespace, err := GetCVESummaryK8sResourceNamespace(ctx)
if err != nil {
return err
}

manifest := v1beta1.VulnerabilityManifestSummary{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -353,17 +360,17 @@ func (a *APIServerStore) storeCVESummary(ctx context.Context, cve domain.CVEMani
Labels: labels,
},
Spec: v1beta1.VulnerabilityManifestSummarySpec{
Severities: parseSeverities(cve, withRelevancy),
Vulnerabilities: parseVulnerabilitiesComponents(cve.Name, a.Namespace, withRelevancy),
Severities: parseSeverities(cve, cvep, withRelevancy),
Vulnerabilities: parseVulnerabilitiesComponents(cve, cvep, workloadNamespace, withRelevancy),
},
}
_, err = a.StorageClient.VulnerabilityManifestSummaries(a.Namespace).Create(context.Background(), &manifest, metav1.CreateOptions{})
_, err = a.StorageClient.VulnerabilityManifestSummaries(workloadNamespace).Create(context.Background(), &manifest, metav1.CreateOptions{})
switch {
case errors.IsAlreadyExists(err):
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
// retrieve the latest version before attempting update
// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
result, getErr := a.StorageClient.VulnerabilityManifestSummaries(a.Namespace).Get(context.Background(), cve.Name, metav1.GetOptions{})
result, getErr := a.StorageClient.VulnerabilityManifestSummaries(workloadNamespace).Get(context.Background(), cve.Name, metav1.GetOptions{})
if getErr != nil {
return getErr
}
Expand All @@ -372,7 +379,7 @@ func (a *APIServerStore) storeCVESummary(ctx context.Context, cve domain.CVEMani
result.Labels = manifest.Labels
result.Spec = manifest.Spec
// try to send the updated vulnerability manifest
_, updateErr := a.StorageClient.VulnerabilityManifestSummaries(a.Namespace).Update(context.Background(), result, metav1.UpdateOptions{})
_, updateErr := a.StorageClient.VulnerabilityManifestSummaries(workloadNamespace).Update(context.Background(), result, metav1.UpdateOptions{})
return updateErr
})
if retryErr != nil {
Expand All @@ -396,23 +403,6 @@ func (a *APIServerStore) storeCVESummary(ctx context.Context, cve domain.CVEMani
return nil
}

func (a *APIServerStore) StoreCVE(ctx context.Context, cve domain.CVEManifest, withRelevancy bool) error {
innerCtx, span := otel.Tracer("").Start(ctx, "APIServerStore.StoreCVE")
defer span.End()

err := a.storeCVEWithFullContent(innerCtx, cve, withRelevancy)
if err != nil {
return err
}

err = a.storeCVESummary(innerCtx, cve, withRelevancy)
if err != nil {
return err
}

return nil
}

func (a *APIServerStore) GetSBOM(ctx context.Context, name, SBOMCreatorVersion string) (domain.SBOM, error) {
_, span := otel.Tracer("").Start(ctx, "APIServerStore.GetSBOM")
defer span.End()
Expand Down
36 changes: 27 additions & 9 deletions repositories/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,15 +391,29 @@ func TestAPIServerStore_parseSeverities(t *testing.T) {
var nginxCVEUnknownSeveritiesNumber = 0

cveManifest := tools.FileToCVEManifest("testdata/nginx-cve.json")
severities := parseSeverities(cveManifest, false)
severities := parseSeverities(cveManifest, cveManifest, false)
assert.Equal(t, nginxCVECriticalSeveritiesNumber, severities.Critical.All)
assert.Equal(t, nginxCVEHighSeveritiesNumber, severities.High.All)
assert.Equal(t, nginxCVEMediumSeveritiesNumber, severities.Medium.All)
assert.Equal(t, nginxCVELowSeveritiesNumber, severities.Low.All)
assert.Equal(t, nginxCVENegligibleSeveritiesNumber, severities.Negligible.All)
assert.Equal(t, nginxCVEUnknownSeveritiesNumber, severities.Unknown.All)

assert.Equal(t, 0, severities.Critical.Relevant)
assert.Equal(t, 0, severities.High.Relevant)
assert.Equal(t, 0, severities.Medium.Relevant)
assert.Equal(t, 0, severities.Low.Relevant)
assert.Equal(t, 0, severities.Negligible.Relevant)
assert.Equal(t, 0, severities.Unknown.Relevant)

severities = parseSeverities(cveManifest, cveManifest, true)
assert.Equal(t, nginxCVECriticalSeveritiesNumber, severities.Critical.All)
assert.Equal(t, nginxCVEHighSeveritiesNumber, severities.High.All)
assert.Equal(t, nginxCVEMediumSeveritiesNumber, severities.Medium.All)
assert.Equal(t, nginxCVELowSeveritiesNumber, severities.Low.All)
assert.Equal(t, nginxCVENegligibleSeveritiesNumber, severities.Negligible.All)
assert.Equal(t, nginxCVEUnknownSeveritiesNumber, severities.Unknown.All)

severities = parseSeverities(cveManifest, true)
assert.Equal(t, nginxCVECriticalSeveritiesNumber, severities.Critical.Relevant)
assert.Equal(t, nginxCVEHighSeveritiesNumber, severities.High.Relevant)
assert.Equal(t, nginxCVEMediumSeveritiesNumber, severities.Medium.Relevant)
Expand All @@ -409,26 +423,30 @@ func TestAPIServerStore_parseSeverities(t *testing.T) {
}

func TestAPIServerStore_parseVulnerabilitiesComponents(t *testing.T) {
any := "any"
namespace := "namespace"

res := parseVulnerabilitiesComponents(any, namespace, false)
assert.Equal(t, res.ImageVulnerabilitiesObj.Name, any)
cveManifest := tools.FileToCVEManifest("testdata/nginx-cve.json")
res := parseVulnerabilitiesComponents(cveManifest, cveManifest, namespace, false)
assert.Equal(t, res.ImageVulnerabilitiesObj.Name, cveManifest.Name)
assert.Equal(t, res.ImageVulnerabilitiesObj.Namespace, namespace)
assert.Equal(t, res.WorkloadVulnerabilitiesObj.Name, "")
assert.Equal(t, res.WorkloadVulnerabilitiesObj.Namespace, "")

res = parseVulnerabilitiesComponents(any, namespace, true)
assert.Equal(t, res.WorkloadVulnerabilitiesObj.Name, any)
res = parseVulnerabilitiesComponents(cveManifest, cveManifest, namespace, true)
assert.Equal(t, res.ImageVulnerabilitiesObj.Name, cveManifest.Name)
assert.Equal(t, res.ImageVulnerabilitiesObj.Namespace, namespace)
assert.Equal(t, res.WorkloadVulnerabilitiesObj.Name, cveManifest.Name)
assert.Equal(t, res.WorkloadVulnerabilitiesObj.Namespace, namespace)
}

func TestAPIServerStore_storeCVESummary(t *testing.T) {
cveManifest := tools.FileToCVEManifest("testdata/nginx-cve.json")
a := NewFakeAPIServerStorage("kubescape")

err := a.storeCVESummary(context.TODO(), cveManifest, false)
err := a.StoreCVESummary(context.TODO(), cveManifest, cveManifest, false)
assert.Equal(t, err, nil)

err = a.storeCVESummary(context.TODO(), cveManifest, true)
err = a.StoreCVESummary(context.TODO(), cveManifest, cveManifest, true)
assert.Equal(t, err, nil)
}

Expand Down
6 changes: 6 additions & 0 deletions repositories/broken.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,9 @@ func (b BrokenStore) StoreCVE(ctx context.Context, _ domain.CVEManifest, _ bool)
defer span.End()
return domain.ErrExpectedError
}

func (b BrokenStore) StoreCVESummary(ctx context.Context, _ domain.CVEManifest, _ domain.CVEManifest, _ bool) error {
_, span := otel.Tracer("").Start(ctx, "BrokenStore.StoreCVESummary")
defer span.End()
return domain.ErrExpectedError
}
30 changes: 30 additions & 0 deletions repositories/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,36 @@ func (m *MemoryStore) StoreCVE(ctx context.Context, cve domain.CVEManifest, _ bo
return nil
}

// StoreCVE stores a CVE Summary to an in-memory map
func (m *MemoryStore) StoreCVESummary(ctx context.Context, cve domain.CVEManifest, cvep domain.CVEManifest, withRelevancy bool) error {
_, span := otel.Tracer("").Start(ctx, "MemoryStore.StoreCVESummary")
defer span.End()

if m.storeError {
return domain.ErrMockError
}

id := cveID{
Name: cve.Name,
SBOMCreatorVersion: cve.SBOMCreatorVersion,
CVEScannerVersion: cve.CVEScannerVersion,
CVEDBVersion: cve.CVEDBVersion,
}

if withRelevancy {
idSumm := cveID{
Name: cvep.Name,
SBOMCreatorVersion: cvep.SBOMCreatorVersion,
CVEScannerVersion: cvep.CVEScannerVersion,
CVEDBVersion: cvep.CVEDBVersion,
}
m.cveManifests[idSumm] = cvep
}

m.cveManifests[id] = cve
return nil
}

// GetSBOM returns a SBOM from an in-memory map
func (m *MemoryStore) GetSBOM(ctx context.Context, name, SBOMCreatorVersion string) (domain.SBOM, error) {
_, span := otel.Tracer("").Start(ctx, "MemoryStore.GetSBOM")
Expand Down
Loading