Skip to content

Commit

Permalink
Store summary cr objects (#144)
Browse files Browse the repository at this point in the history
* store workload name in the summary object

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* change error

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* get name from instance id

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* should return err

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* add to test desired input

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* change the summary object name

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* trigger create image

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* always update both cve manifest and filtered cve manifest

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

---------

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>
Co-authored-by: rcohencyberarmor <rcohen@armosec.io>
  • Loading branch information
rcohencyberarmor and rcohencyberarmor committed Aug 18, 2023
1 parent 3b2e952 commit 6d21144
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 69 deletions.
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

0 comments on commit 6d21144

Please sign in to comment.