0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-30 22:34:13 -05:00

fix(cve): cummulative fixes and improvements for CVE scanning logic (#1810)

1. Only scan CVEs for images returned by graphql calls
Since pagination was refactored to account for image indexes, we had started
to run the CVE scanner before pagination was applied, resulting in
decreased ZOT performance if CVE information was requested

2. Increase in medory-cache of cve results to 1m, from 10k digests.

3. Update CVE model to use CVSS severity values in our code.
Previously we relied upon the strings returned by trivy directly,
and the sorting they implemented.
Since CVE severities are standardized, we don't need to pass around
an adapter object just for pagination and sorting purposes anymore.
This also improves our testing since we don't mock the sorting functions anymore.

4. Fix a flaky CLI test not waiting for the zot service to start.

5. Add the search build label on search/cve tests which were missing it.

6. The boltdb update method was used in a few places where view was supposed to be called.

7. Add logs for start and finish of parsing MetaDB.

8. Avoid unmarshalling twice to obtain annotations for multiarch images.

Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
This commit is contained in:
Andrei Aaron 2023-09-18 01:12:20 +03:00 committed by GitHub
parent f58597ade9
commit bcdd9988f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 676 additions and 463 deletions

View file

@ -59,7 +59,7 @@ func TestSearchCVECmd(t *testing.T) {
ctlr := api.NewController(conf) ctlr := api.NewController(conf)
cm := test.NewControllerManager(ctlr) cm := test.NewControllerManager(ctlr)
cm.StartServer() cm.StartAndWait(port)
defer cm.StopServer() defer cm.StopServer()
Convey("Test CVE help", t, func() { Convey("Test CVE help", t, func() {
@ -947,21 +947,10 @@ func TestCVESort(t *testing.T) {
panic(err) panic(err)
} }
severities := map[string]int{
"UNKNOWN": 0,
"LOW": 1,
"MEDIUM": 2,
"HIGH": 3,
"CRITICAL": 4,
}
ctlr.CveInfo = cveinfo.BaseCveInfo{ ctlr.CveInfo = cveinfo.BaseCveInfo{
Log: ctlr.Log, Log: ctlr.Log,
MetaDB: mocks.MetaDBMock{}, MetaDB: mocks.MetaDBMock{},
Scanner: mocks.CveScannerMock{ Scanner: mocks.CveScannerMock{
CompareSeveritiesFn: func(severity1, severity2 string) int {
return severities[severity2] - severities[severity1]
},
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) { ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
return map[string]cvemodel.CVE{ return map[string]cvemodel.CVE{
"CVE-2023-1255": { "CVE-2023-1255": {
@ -1385,15 +1374,7 @@ func TestCVECommandErrors(t *testing.T) {
} }
func getMockCveInfo(metaDB mTypes.MetaDB, log log.Logger) cveinfo.CveInfo { func getMockCveInfo(metaDB mTypes.MetaDB, log log.Logger) cveinfo.CveInfo {
// MetaDB loaded with initial data, mock the scanner // MetaDB loaded with initial data now mock the scanner
severities := map[string]int{
"UNKNOWN": 0,
"LOW": 1,
"MEDIUM": 2,
"HIGH": 3,
"CRITICAL": 4,
}
// Setup test CVE data in mock scanner // Setup test CVE data in mock scanner
scanner := mocks.CveScannerMock{ scanner := mocks.CveScannerMock{
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) { ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
@ -1436,9 +1417,6 @@ func getMockCveInfo(metaDB mTypes.MetaDB, log log.Logger) cveinfo.CveInfo {
// By default the image has no vulnerabilities // By default the image has no vulnerabilities
return map[string]cvemodel.CVE{}, nil return map[string]cvemodel.CVE{}, nil
}, },
CompareSeveritiesFn: func(severity1, severity2 string) int {
return severities[severity2] - severities[severity1]
},
IsImageFormatScannableFn: func(repo string, reference string) (bool, error) { IsImageFormatScannableFn: func(repo string, reference string) (bool, error) {
// Almost same logic compared to actual Trivy specific implementation // Almost same logic compared to actual Trivy specific implementation
imageDir := repo imageDir := repo

View file

@ -70,7 +70,7 @@ func TestTrivyDBGenerator(t *testing.T) {
// Wait for trivy db to download // Wait for trivy db to download
found, err := ReadLogFileAndCountStringOccurence(logPath, found, err := ReadLogFileAndCountStringOccurence(logPath,
"DB update completed, next update scheduled", 120*time.Second, 2) "DB update completed, next update scheduled", 140*time.Second, 2)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(found, ShouldBeTrue) So(found, ShouldBeTrue)
}) })

View file

@ -134,9 +134,10 @@ func GetAnnotations(annotations, labels map[string]string) ImageAnnotations {
} }
} }
func GetIndexAnnotations(indexAnnotations, manifestAnnotations, manifestLabels map[string]string) ImageAnnotations { func GetIndexAnnotations(
annotationsFromManifest := GetAnnotations(manifestAnnotations, manifestLabels) indexAnnotations map[string]string,
annotationsFromManifest *ImageAnnotations,
) ImageAnnotations {
description := GetDescription(indexAnnotations) description := GetDescription(indexAnnotations)
if description == "" { if description == "" {
description = annotationsFromManifest.Description description = annotationsFromManifest.Description

View file

@ -0,0 +1,302 @@
//go:build search
package convert
import (
"context"
"encoding/json"
"errors"
"testing"
"github.com/99designs/gqlgen/graphql"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey"
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
"zotregistry.io/zot/pkg/extensions/search/gql_generated"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/meta/boltdb"
mTypes "zotregistry.io/zot/pkg/meta/types"
"zotregistry.io/zot/pkg/test/mocks"
)
var ErrTestError = errors.New("TestError")
func TestCVEConvert(t *testing.T) {
Convey("Test adding CVE information to Summary objects", t, func() {
params := boltdb.DBParameters{
RootDir: t.TempDir(),
}
boltDB, err := boltdb.GetBoltDriver(params)
So(err, ShouldBeNil)
metaDB, err := boltdb.New(boltDB, log.NewLogger("debug", ""))
So(err, ShouldBeNil)
configBlob, err := json.Marshal(ispec.Image{})
So(err, ShouldBeNil)
manifestBlob, err := json.Marshal(ispec.Manifest{
Layers: []ispec.Descriptor{
{
MediaType: ispec.MediaTypeImageLayerGzip,
Size: 0,
Digest: godigest.NewDigestFromEncoded(godigest.SHA256, "digest"),
},
},
})
So(err, ShouldBeNil)
repoMeta11 := mTypes.ManifestMetadata{
ManifestBlob: manifestBlob,
ConfigBlob: configBlob,
}
digest11 := godigest.FromString("abc1")
err = metaDB.SetManifestMeta("repo1", digest11, repoMeta11)
So(err, ShouldBeNil)
err = metaDB.SetRepoReference("repo1", "0.1.0", digest11, ispec.MediaTypeImageManifest)
So(err, ShouldBeNil)
reposMeta, manifestMetaMap, _, err := metaDB.SearchRepos(context.Background(), "")
So(err, ShouldBeNil)
ctx := graphql.WithResponseContext(context.Background(),
graphql.DefaultErrorPresenter, graphql.DefaultRecover)
Convey("Add CVE Summary to ImageSummary", func() {
var imageSummary *gql_generated.ImageSummary
So(imageSummary, ShouldBeNil)
updateImageSummaryVulnerabilities(ctx,
imageSummary,
SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{}, ErrTestError
},
},
)
So(imageSummary, ShouldBeNil)
So(graphql.GetErrors(ctx), ShouldBeNil)
imageSummary, _, err = ImageManifest2ImageSummary(ctx, "repo1", "0.1.0", digest11, reposMeta[0],
manifestMetaMap[digest11.String()])
So(err, ShouldBeNil)
So(imageSummary, ShouldNotBeNil)
So(imageSummary.Vulnerabilities, ShouldBeNil)
updateImageSummaryVulnerabilities(ctx,
imageSummary,
SkipQGLField{
Vulnerabilities: true,
},
mocks.CveInfoMock{},
)
So(imageSummary.Vulnerabilities, ShouldNotBeNil)
So(*imageSummary.Vulnerabilities.Count, ShouldEqual, 0)
So(*imageSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "")
So(graphql.GetErrors(ctx), ShouldBeNil)
imageSummary.Vulnerabilities = nil
updateImageSummaryVulnerabilities(ctx,
imageSummary,
SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
}, nil
},
},
)
So(imageSummary.Vulnerabilities, ShouldNotBeNil)
So(*imageSummary.Vulnerabilities.Count, ShouldEqual, 1)
So(*imageSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "HIGH")
So(graphql.GetErrors(ctx), ShouldBeNil)
So(len(imageSummary.Manifests), ShouldEqual, 1)
So(imageSummary.Manifests[0].Vulnerabilities, ShouldNotBeNil)
So(*imageSummary.Manifests[0].Vulnerabilities.Count, ShouldEqual, 1)
So(*imageSummary.Manifests[0].Vulnerabilities.MaxSeverity, ShouldEqual, "HIGH")
imageSummary.Vulnerabilities = nil
updateImageSummaryVulnerabilities(ctx,
imageSummary,
SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{}, ErrTestError
},
},
)
So(imageSummary.Vulnerabilities, ShouldNotBeNil)
So(*imageSummary.Vulnerabilities.Count, ShouldEqual, 0)
So(*imageSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "")
So(graphql.GetErrors(ctx).Error(), ShouldContainSubstring, "unable to run vulnerability scan on tag")
})
Convey("Add CVE Summary to RepoSummary", func() {
var repoSummary *gql_generated.RepoSummary
So(repoSummary, ShouldBeNil)
updateRepoSummaryVulnerabilities(ctx,
repoSummary,
SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
}, nil
},
},
)
So(repoSummary, ShouldBeNil)
So(graphql.GetErrors(ctx), ShouldBeNil)
imageSummary, _, err := ImageManifest2ImageSummary(ctx, "repo1", "0.1.0", digest11, reposMeta[0],
manifestMetaMap[digest11.String()])
So(err, ShouldBeNil)
So(imageSummary, ShouldNotBeNil)
repoSummary = &gql_generated.RepoSummary{}
repoSummary.NewestImage = imageSummary
So(repoSummary.NewestImage.Vulnerabilities, ShouldBeNil)
updateImageSummaryVulnerabilities(ctx,
imageSummary,
SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
}, nil
},
},
)
So(repoSummary.NewestImage.Vulnerabilities, ShouldNotBeNil)
So(*repoSummary.NewestImage.Vulnerabilities.Count, ShouldEqual, 1)
So(*repoSummary.NewestImage.Vulnerabilities.MaxSeverity, ShouldEqual, "HIGH")
So(graphql.GetErrors(ctx), ShouldBeNil)
})
Convey("Add CVE Summary to ManifestSummary", func() {
var manifestSummary *gql_generated.ManifestSummary
So(manifestSummary, ShouldBeNil)
updateManifestSummaryVulnerabilities(ctx,
manifestSummary,
"repo1",
SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
}, nil
},
},
)
So(manifestSummary, ShouldBeNil)
So(graphql.GetErrors(ctx), ShouldBeNil)
imageSummary, _, err := ImageManifest2ImageSummary(ctx, "repo1", "0.1.0", digest11, reposMeta[0],
manifestMetaMap[digest11.String()])
So(err, ShouldBeNil)
manifestSummary = imageSummary.Manifests[0]
updateManifestSummaryVulnerabilities(ctx,
manifestSummary,
"repo1",
SkipQGLField{
Vulnerabilities: true,
},
mocks.CveInfoMock{},
)
So(manifestSummary, ShouldNotBeNil)
So(manifestSummary.Vulnerabilities, ShouldNotBeNil)
So(*manifestSummary.Vulnerabilities.Count, ShouldEqual, 0)
So(*manifestSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "")
manifestSummary.Vulnerabilities = nil
updateManifestSummaryVulnerabilities(ctx,
manifestSummary,
"repo1",
SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{
Count: 1,
MaxSeverity: "HIGH",
}, nil
},
},
)
So(manifestSummary.Vulnerabilities, ShouldNotBeNil)
So(*manifestSummary.Vulnerabilities.Count, ShouldEqual, 1)
So(*manifestSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "HIGH")
manifestSummary.Vulnerabilities = nil
updateManifestSummaryVulnerabilities(ctx,
manifestSummary,
"repo1",
SkipQGLField{
Vulnerabilities: false,
},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{}, ErrTestError
},
},
)
So(manifestSummary.Vulnerabilities, ShouldNotBeNil)
So(*manifestSummary.Vulnerabilities.Count, ShouldEqual, 0)
So(*manifestSummary.Vulnerabilities.MaxSeverity, ShouldEqual, "")
So(graphql.GetErrors(ctx).Error(), ShouldContainSubstring, "unable to run vulnerability scan in repo")
})
})
}

View file

@ -1,3 +1,5 @@
//go:build search
package convert_test package convert_test
import ( import (
@ -17,7 +19,6 @@ import (
"zotregistry.io/zot/pkg/extensions/search/gql_generated" "zotregistry.io/zot/pkg/extensions/search/gql_generated"
"zotregistry.io/zot/pkg/extensions/search/pagination" "zotregistry.io/zot/pkg/extensions/search/pagination"
"zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/meta/boltdb"
mTypes "zotregistry.io/zot/pkg/meta/types" mTypes "zotregistry.io/zot/pkg/meta/types"
"zotregistry.io/zot/pkg/test" "zotregistry.io/zot/pkg/test"
. "zotregistry.io/zot/pkg/test/image-utils" . "zotregistry.io/zot/pkg/test/image-utils"
@ -27,64 +28,6 @@ import (
var ErrTestError = errors.New("TestError") var ErrTestError = errors.New("TestError")
func TestConvertErrors(t *testing.T) { func TestConvertErrors(t *testing.T) {
Convey("Convert Errors", t, func() {
params := boltdb.DBParameters{
RootDir: t.TempDir(),
}
boltDB, err := boltdb.GetBoltDriver(params)
So(err, ShouldBeNil)
metaDB, err := boltdb.New(boltDB, log.NewLogger("debug", ""))
So(err, ShouldBeNil)
configBlob, err := json.Marshal(ispec.Image{})
So(err, ShouldBeNil)
manifestBlob, err := json.Marshal(ispec.Manifest{
Layers: []ispec.Descriptor{
{
MediaType: ispec.MediaTypeImageLayerGzip,
Size: 0,
Digest: godigest.NewDigestFromEncoded(godigest.SHA256, "digest"),
},
},
})
So(err, ShouldBeNil)
repoMeta11 := mTypes.ManifestMetadata{
ManifestBlob: manifestBlob,
ConfigBlob: configBlob,
}
digest11 := godigest.FromString("abc1")
err = metaDB.SetManifestMeta("repo1", digest11, repoMeta11)
So(err, ShouldBeNil)
err = metaDB.SetRepoReference("repo1", "0.1.0", digest11, ispec.MediaTypeImageManifest)
So(err, ShouldBeNil)
reposMeta, manifestMetaMap, _, err := metaDB.SearchRepos(context.Background(), "")
So(err, ShouldBeNil)
ctx := graphql.WithResponseContext(context.Background(),
graphql.DefaultErrorPresenter, graphql.DefaultRecover)
_ = convert.RepoMeta2RepoSummary(
ctx,
reposMeta[0],
manifestMetaMap,
map[string]mTypes.IndexData{},
convert.SkipQGLField{},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{}, ErrTestError
},
},
)
So(graphql.GetErrors(ctx).Error(), ShouldContainSubstring, "unable to run vulnerability scan on tag")
})
Convey("ImageIndex2ImageSummary errors", t, func() { Convey("ImageIndex2ImageSummary errors", t, func() {
ctx := graphql.WithResponseContext(context.Background(), ctx := graphql.WithResponseContext(context.Background(),
graphql.DefaultErrorPresenter, graphql.DefaultRecover) graphql.DefaultErrorPresenter, graphql.DefaultRecover)
@ -94,13 +37,11 @@ func TestConvertErrors(t *testing.T) {
"repo", "repo",
"tag", "tag",
godigest.FromString("indexDigest"), godigest.FromString("indexDigest"),
true,
mTypes.RepoMetadata{}, mTypes.RepoMetadata{},
mTypes.IndexData{ mTypes.IndexData{
IndexBlob: []byte("bad json"), IndexBlob: []byte("bad json"),
}, },
map[string]mTypes.ManifestMetadata{}, map[string]mTypes.ManifestMetadata{},
mocks.CveInfoMock{},
) )
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
@ -114,17 +55,11 @@ func TestConvertErrors(t *testing.T) {
"repo", "repo",
"tag", "tag",
godigest.FromString("indexDigest"), godigest.FromString("indexDigest"),
false,
mTypes.RepoMetadata{}, mTypes.RepoMetadata{},
mTypes.IndexData{ mTypes.IndexData{
IndexBlob: []byte("{}"), IndexBlob: []byte("{}"),
}, },
map[string]mTypes.ManifestMetadata{}, map[string]mTypes.ManifestMetadata{},
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{}, ErrTestError
},
},
) )
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
@ -146,17 +81,11 @@ func TestConvertErrors(t *testing.T) {
"repo", "repo",
"tag", "tag",
godigest.FromString("manifestDigest"), godigest.FromString("manifestDigest"),
false,
mTypes.RepoMetadata{}, mTypes.RepoMetadata{},
mTypes.ManifestMetadata{ mTypes.ManifestMetadata{
ManifestBlob: []byte("{}"), ManifestBlob: []byte("{}"),
ConfigBlob: configBlob, ConfigBlob: configBlob,
}, },
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{}, ErrTestError
},
},
) )
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
@ -166,7 +95,7 @@ func TestConvertErrors(t *testing.T) {
graphql.DefaultErrorPresenter, graphql.DefaultRecover) graphql.DefaultErrorPresenter, graphql.DefaultRecover)
// with bad config json, shouldn't error when unmarshaling // with bad config json, shouldn't error when unmarshaling
_, _, err := convert.ImageManifest2ManifestSummary( _, _, _, err := convert.ImageManifest2ManifestSummary(
ctx, ctx,
"repo", "repo",
"tag", "tag",
@ -174,7 +103,6 @@ func TestConvertErrors(t *testing.T) {
Digest: "dig", Digest: "dig",
MediaType: ispec.MediaTypeImageManifest, MediaType: ispec.MediaTypeImageManifest,
}, },
false,
mTypes.RepoMetadata{ mTypes.RepoMetadata{
Tags: map[string]mTypes.Descriptor{}, Tags: map[string]mTypes.Descriptor{},
Statistics: map[string]mTypes.DescriptorStatistics{}, Statistics: map[string]mTypes.DescriptorStatistics{},
@ -186,7 +114,6 @@ func TestConvertErrors(t *testing.T) {
ConfigBlob: []byte("bad json"), ConfigBlob: []byte("bad json"),
}, },
nil, nil,
mocks.CveInfoMock{},
) )
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -200,7 +127,7 @@ func TestConvertErrors(t *testing.T) {
}) })
So(err, ShouldBeNil) So(err, ShouldBeNil)
_, _, err = convert.ImageManifest2ManifestSummary( _, _, _, err = convert.ImageManifest2ManifestSummary(
ctx, ctx,
"repo", "repo",
"tag", "tag",
@ -208,7 +135,6 @@ func TestConvertErrors(t *testing.T) {
Digest: "dig", Digest: "dig",
MediaType: ispec.MediaTypeImageManifest, MediaType: ispec.MediaTypeImageManifest,
}, },
false,
mTypes.RepoMetadata{ mTypes.RepoMetadata{
Tags: map[string]mTypes.Descriptor{}, Tags: map[string]mTypes.Descriptor{},
Statistics: map[string]mTypes.DescriptorStatistics{}, Statistics: map[string]mTypes.DescriptorStatistics{},
@ -220,11 +146,6 @@ func TestConvertErrors(t *testing.T) {
ConfigBlob: configBlob, ConfigBlob: configBlob,
}, },
nil, nil,
mocks.CveInfoMock{
GetCVESummaryForImageMediaFn: func(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error) {
return cvemodel.ImageCVESummary{}, ErrTestError
},
},
) )
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
@ -772,37 +693,7 @@ func TestPaginatedConvert(t *testing.T) {
}) })
} }
func TestGetOneManifestAnnotations(t *testing.T) { func TestIndexAnnotations(t *testing.T) {
Convey("GetOneManifestAnnotations errors", t, func() {
manifestAnnotations, configLabels := convert.GetOneManifestAnnotations(
ispec.Index{Manifests: []ispec.Descriptor{
{Digest: "bad-manifest"}, {Digest: "dig2"},
}},
map[string]mTypes.ManifestMetadata{
"bad-manifest": {
ManifestBlob: []byte(`bad`),
ConfigBlob: []byte("{}"),
},
},
)
So(manifestAnnotations, ShouldBeEmpty)
So(configLabels, ShouldBeEmpty)
manifestAnnotations, configLabels = convert.GetOneManifestAnnotations(
ispec.Index{Manifests: []ispec.Descriptor{
{Digest: "bad-config"},
}},
map[string]mTypes.ManifestMetadata{
"bad-config": {
ManifestBlob: []byte("{}"),
ConfigBlob: []byte("bad"),
},
},
)
So(manifestAnnotations, ShouldBeEmpty)
So(configLabels, ShouldBeEmpty)
})
Convey("Test ImageIndex2ImageSummary annotations logic", t, func() { Convey("Test ImageIndex2ImageSummary annotations logic", t, func() {
ctx := context.Background() ctx := context.Background()
@ -864,8 +755,8 @@ func TestGetOneManifestAnnotations(t *testing.T) {
digest := indexWithAnnotations.Digest() digest := indexWithAnnotations.Digest()
imageSummary, _, err := convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest, true, repoMeta[0], imageSummary, _, err := convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest, repoMeta[0],
indexData[digest.String()], manifestMetadata, nil) indexData[digest.String()], manifestMetadata)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*imageSummary.Description, ShouldResemble, "IndexDescription") So(*imageSummary.Description, ShouldResemble, "IndexDescription")
So(*imageSummary.Licenses, ShouldResemble, "IndexLicenses") So(*imageSummary.Licenses, ShouldResemble, "IndexLicenses")
@ -887,7 +778,7 @@ func TestGetOneManifestAnnotations(t *testing.T) {
digest = indexWithManifestAndConfigAnnotations.Digest() digest = indexWithManifestAndConfigAnnotations.Digest()
imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest, imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest,
true, repoMeta[0], indexData[digest.String()], manifestMetadata, nil) repoMeta[0], indexData[digest.String()], manifestMetadata)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*imageSummary.Description, ShouldResemble, "ManifestDescription") So(*imageSummary.Description, ShouldResemble, "ManifestDescription")
So(*imageSummary.Licenses, ShouldResemble, "ManifestLicenses") So(*imageSummary.Licenses, ShouldResemble, "ManifestLicenses")
@ -908,7 +799,7 @@ func TestGetOneManifestAnnotations(t *testing.T) {
digest = indexWithConfigAnnotations.Digest() digest = indexWithConfigAnnotations.Digest()
imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest, imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest,
true, repoMeta[0], indexData[digest.String()], manifestMetadata, nil) repoMeta[0], indexData[digest.String()], manifestMetadata)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*imageSummary.Description, ShouldResemble, "ConfigDescription") So(*imageSummary.Description, ShouldResemble, "ConfigDescription")
So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses") So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses")
@ -950,7 +841,7 @@ func TestGetOneManifestAnnotations(t *testing.T) {
digest = indexWithMixAnnotations.Digest() digest = indexWithMixAnnotations.Digest()
imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest, imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest,
true, repoMeta[0], indexData[digest.String()], manifestMetadata, nil) repoMeta[0], indexData[digest.String()], manifestMetadata)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*imageSummary.Description, ShouldResemble, "ConfigDescription") So(*imageSummary.Description, ShouldResemble, "ConfigDescription")
So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses") So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses")
@ -970,7 +861,7 @@ func TestGetOneManifestAnnotations(t *testing.T) {
digest = indexWithNoAnnotations.Digest() digest = indexWithNoAnnotations.Digest()
imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest, imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest,
true, repoMeta[0], indexData[digest.String()], manifestMetadata, nil) repoMeta[0], indexData[digest.String()], manifestMetadata)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*imageSummary.Description, ShouldBeBlank) So(*imageSummary.Description, ShouldBeBlank)
So(*imageSummary.Licenses, ShouldBeBlank) So(*imageSummary.Licenses, ShouldBeBlank)
@ -999,8 +890,7 @@ func TestDownloadCount(t *testing.T) {
}, },
) )
repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap, repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap)
convert.SkipQGLField{}, nil)
So(*repoSummary.DownloadCount, ShouldEqual, 10) So(*repoSummary.DownloadCount, ShouldEqual, 10)
So(*repoSummary.NewestImage.DownloadCount, ShouldEqual, 10) So(*repoSummary.NewestImage.DownloadCount, ShouldEqual, 10)
}) })
@ -1027,8 +917,7 @@ func TestDownloadCount(t *testing.T) {
}, },
) )
repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap, repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap)
convert.SkipQGLField{}, nil)
So(*repoSummary.DownloadCount, ShouldEqual, 100) So(*repoSummary.DownloadCount, ShouldEqual, 100)
So(*repoSummary.NewestImage.DownloadCount, ShouldEqual, 100) So(*repoSummary.NewestImage.DownloadCount, ShouldEqual, 100)
}) })
@ -1067,8 +956,7 @@ func TestDownloadCount(t *testing.T) {
}, },
) )
repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap, repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap)
convert.SkipQGLField{}, nil)
So(*repoSummary.DownloadCount, ShouldEqual, 105) So(*repoSummary.DownloadCount, ShouldEqual, 105)
So(*repoSummary.NewestImage.DownloadCount, ShouldEqual, 100) So(*repoSummary.NewestImage.DownloadCount, ShouldEqual, 100)
}) })

View file

@ -0,0 +1,109 @@
package convert
import (
"context"
"github.com/99designs/gqlgen/graphql"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/vektah/gqlparser/v2/gqlerror"
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
"zotregistry.io/zot/pkg/extensions/search/gql_generated"
)
func updateRepoSummaryVulnerabilities(
ctx context.Context,
repoSummary *gql_generated.RepoSummary,
skip SkipQGLField,
cveInfo cveinfo.CveInfo,
) {
if repoSummary == nil {
return
}
updateImageSummaryVulnerabilities(ctx, repoSummary.NewestImage, skip, cveInfo)
}
func updateImageSummaryVulnerabilities(
ctx context.Context,
imageSummary *gql_generated.ImageSummary,
skip SkipQGLField,
cveInfo cveinfo.CveInfo,
) {
if imageSummary == nil {
return
}
imageCveSummary := cvemodel.ImageCVESummary{}
imageSummary.Vulnerabilities = &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
}
// Check if vulnerability scanning is disabled
if cveInfo == nil || skip.Vulnerabilities {
return
}
imageCveSummary, err := cveInfo.GetCVESummaryForImageMedia(*imageSummary.RepoName, *imageSummary.Digest,
*imageSummary.MediaType)
if err != nil {
// Log the error, but we should still include the image in results
graphql.AddError(
ctx,
gqlerror.Errorf(
"unable to run vulnerability scan on tag %s in repo %s: error: %s",
*imageSummary.Tag, *imageSummary.RepoName, err.Error(),
),
)
}
imageSummary.Vulnerabilities.MaxSeverity = &imageCveSummary.MaxSeverity
imageSummary.Vulnerabilities.Count = &imageCveSummary.Count
for _, manifestSummary := range imageSummary.Manifests {
updateManifestSummaryVulnerabilities(ctx, manifestSummary, *imageSummary.RepoName, skip, cveInfo)
}
}
func updateManifestSummaryVulnerabilities(
ctx context.Context,
manifestSummary *gql_generated.ManifestSummary,
repoName string,
skip SkipQGLField,
cveInfo cveinfo.CveInfo,
) {
if manifestSummary == nil {
return
}
imageCveSummary := cvemodel.ImageCVESummary{}
manifestSummary.Vulnerabilities = &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
}
// Check if vulnerability scanning is disabled
if cveInfo == nil || skip.Vulnerabilities {
return
}
imageCveSummary, err := cveInfo.GetCVESummaryForImageMedia(repoName, *manifestSummary.Digest,
ispec.MediaTypeImageManifest)
if err != nil {
// Log the error, but we should still include the manifest in results
graphql.AddError(
ctx,
gqlerror.Errorf(
"unable to run vulnerability scan in repo %s: manifest digest: %s, error: %s",
repoName, *manifestSummary.Digest, err.Error(),
),
)
}
manifestSummary.Vulnerabilities.MaxSeverity = &imageCveSummary.MaxSeverity
manifestSummary.Vulnerabilities.Count = &imageCveSummary.Count
}

View file

@ -17,7 +17,6 @@ import (
zerr "zotregistry.io/zot/errors" zerr "zotregistry.io/zot/errors"
zcommon "zotregistry.io/zot/pkg/common" zcommon "zotregistry.io/zot/pkg/common"
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve" cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
"zotregistry.io/zot/pkg/extensions/search/gql_generated" "zotregistry.io/zot/pkg/extensions/search/gql_generated"
"zotregistry.io/zot/pkg/extensions/search/pagination" "zotregistry.io/zot/pkg/extensions/search/pagination"
"zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/log"
@ -31,7 +30,6 @@ type SkipQGLField struct {
func RepoMeta2RepoSummary(ctx context.Context, repoMeta mTypes.RepoMetadata, func RepoMeta2RepoSummary(ctx context.Context, repoMeta mTypes.RepoMetadata,
manifestMetaMap map[string]mTypes.ManifestMetadata, indexDataMap map[string]mTypes.IndexData, manifestMetaMap map[string]mTypes.ManifestMetadata, indexDataMap map[string]mTypes.IndexData,
skip SkipQGLField, cveInfo cveinfo.CveInfo,
) *gql_generated.RepoSummary { ) *gql_generated.RepoSummary {
var ( var (
repoName = repoMeta.Name repoName = repoMeta.Name
@ -53,8 +51,9 @@ func RepoMeta2RepoSummary(ctx context.Context, repoMeta mTypes.RepoMetadata,
) )
for tag, descriptor := range repoMeta.Tags { for tag, descriptor := range repoMeta.Tags {
imageSummary, imageBlobsMap, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag, true, repoMeta, imageSummary, imageBlobsMap, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name,
manifestMetaMap, indexDataMap, cveInfo) tag, repoMeta, manifestMetaMap, indexDataMap,
)
if err != nil { if err != nil {
continue continue
} }
@ -101,28 +100,6 @@ func RepoMeta2RepoSummary(ctx context.Context, repoMeta mTypes.RepoMetadata,
repoVendors = append(repoVendors, &vendor) repoVendors = append(repoVendors, &vendor)
} }
// We only scan the latest image on the repo for performance reasons
// Check if vulnerability scanning is disabled
if cveInfo != nil && lastUpdatedImageSummary != nil && !skip.Vulnerabilities {
imageCveSummary, err := cveInfo.GetCVESummaryForImageMedia(repoMeta.Name, *lastUpdatedImageSummary.Digest,
*lastUpdatedImageSummary.MediaType)
if err != nil {
// Log the error, but we should still include the image in results
graphql.AddError(
ctx,
gqlerror.Errorf(
"unable to run vulnerability scan on tag %s in repo %s: error: %s",
*lastUpdatedImageSummary.Tag, repoMeta.Name, err.Error(),
),
)
}
lastUpdatedImageSummary.Vulnerabilities = &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
}
}
return &gql_generated.RepoSummary{ return &gql_generated.RepoSummary{
Name: &repoName, Name: &repoName,
LastUpdated: &repoLastUpdatedTimestamp, LastUpdated: &repoLastUpdatedTimestamp,
@ -148,7 +125,7 @@ func PaginatedRepoMeta2RepoSummaries(ctx context.Context, reposMeta []mTypes.Rep
} }
for _, repoMeta := range reposMeta { for _, repoMeta := range reposMeta {
repoSummary := RepoMeta2RepoSummary(ctx, repoMeta, manifestMetaMap, indexDataMap, skip, cveInfo) repoSummary := RepoMeta2RepoSummary(ctx, repoMeta, manifestMetaMap, indexDataMap)
if RepoSumAcceptedByFilter(repoSummary, filter) { if RepoSumAcceptedByFilter(repoSummary, filter) {
reposPageFinder.Add(repoSummary) reposPageFinder.Add(repoSummary)
@ -157,6 +134,11 @@ func PaginatedRepoMeta2RepoSummaries(ctx context.Context, reposMeta []mTypes.Rep
page, pageInfo := reposPageFinder.Page() page, pageInfo := reposPageFinder.Page()
// CVE scanning is expensive, only scan for the current page
for _, repoSummary := range page {
updateRepoSummaryVulnerabilities(ctx, repoSummary, skip, cveInfo)
}
return page, pageInfo, nil return page, pageInfo, nil
} }
@ -177,25 +159,24 @@ func UpdateLastUpdatedTimestamp(repoLastUpdatedTimestamp *time.Time,
return newLastUpdatedImageSummary return newLastUpdatedImageSummary
} }
func Descriptor2ImageSummary(ctx context.Context, descriptor mTypes.Descriptor, repo, tag string, skipCVE bool, func Descriptor2ImageSummary(ctx context.Context, descriptor mTypes.Descriptor, repo, tag string,
repoMeta mTypes.RepoMetadata, manifestMetaMap map[string]mTypes.ManifestMetadata, repoMeta mTypes.RepoMetadata, manifestMetaMap map[string]mTypes.ManifestMetadata,
indexDataMap map[string]mTypes.IndexData, cveInfo cveinfo.CveInfo, indexDataMap map[string]mTypes.IndexData,
) (*gql_generated.ImageSummary, map[string]int64, error) { ) (*gql_generated.ImageSummary, map[string]int64, error) {
switch descriptor.MediaType { switch descriptor.MediaType {
case ispec.MediaTypeImageManifest: case ispec.MediaTypeImageManifest:
return ImageManifest2ImageSummary(ctx, repo, tag, godigest.Digest(descriptor.Digest), skipCVE, return ImageManifest2ImageSummary(ctx, repo, tag, godigest.Digest(descriptor.Digest),
repoMeta, manifestMetaMap[descriptor.Digest], cveInfo) repoMeta, manifestMetaMap[descriptor.Digest])
case ispec.MediaTypeImageIndex: case ispec.MediaTypeImageIndex:
return ImageIndex2ImageSummary(ctx, repo, tag, godigest.Digest(descriptor.Digest), skipCVE, return ImageIndex2ImageSummary(ctx, repo, tag, godigest.Digest(descriptor.Digest),
repoMeta, indexDataMap[descriptor.Digest], manifestMetaMap, cveInfo) repoMeta, indexDataMap[descriptor.Digest], manifestMetaMap)
default: default:
return &gql_generated.ImageSummary{}, map[string]int64{}, zerr.ErrMediaTypeNotSupported return &gql_generated.ImageSummary{}, map[string]int64{}, zerr.ErrMediaTypeNotSupported
} }
} }
func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest godigest.Digest, skipCVE bool, func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest godigest.Digest,
repoMeta mTypes.RepoMetadata, indexData mTypes.IndexData, manifestMetaMap map[string]mTypes.ManifestMetadata, repoMeta mTypes.RepoMetadata, indexData mTypes.IndexData, manifestMetaMap map[string]mTypes.ManifestMetadata,
cveInfo cveinfo.CveInfo,
) (*gql_generated.ImageSummary, map[string]int64, error) { ) (*gql_generated.ImageSummary, map[string]int64, error) {
var indexContent ispec.Index var indexContent ispec.Index
@ -205,22 +186,22 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
} }
var ( var (
indexLastUpdated time.Time indexLastUpdated time.Time
isSigned bool isSigned bool
totalIndexSize int64 totalIndexSize int64
indexSize string indexSize string
totalDownloadCount int totalDownloadCount int
maxSeverity string manifestAnnotations *ImageAnnotations
manifestSummaries = make([]*gql_generated.ManifestSummary, 0, len(indexContent.Manifests)) manifestSummaries = make([]*gql_generated.ManifestSummary, 0, len(indexContent.Manifests))
indexBlobs = make(map[string]int64, 0) indexBlobs = make(map[string]int64, 0)
indexDigestStr = indexDigest.String() indexDigestStr = indexDigest.String()
indexMediaType = ispec.MediaTypeImageIndex indexMediaType = ispec.MediaTypeImageIndex
) )
for _, descriptor := range indexContent.Manifests { for _, descriptor := range indexContent.Manifests {
manifestSummary, manifestBlobs, err := ImageManifest2ManifestSummary(ctx, repo, tag, descriptor, false, manifestSummary, manifestBlobs, annotations, err := ImageManifest2ManifestSummary(ctx, repo, tag, descriptor,
repoMeta, manifestMetaMap[descriptor.Digest.String()], repoMeta.Referrers[descriptor.Digest.String()], cveInfo) repoMeta, manifestMetaMap[descriptor.Digest.String()], repoMeta.Referrers[descriptor.Digest.String()])
if err != nil { if err != nil {
return &gql_generated.ImageSummary{}, map[string]int64{}, err return &gql_generated.ImageSummary{}, map[string]int64{}, err
} }
@ -236,13 +217,12 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
indexLastUpdated = *manifestSummary.LastUpdated indexLastUpdated = *manifestSummary.LastUpdated
} }
totalIndexSize += manifestSize if manifestAnnotations == nil {
manifestAnnotations = annotations
if cvemodel.SeverityValue(*manifestSummary.Vulnerabilities.MaxSeverity) >
cvemodel.SeverityValue(maxSeverity) {
maxSeverity = *manifestSummary.Vulnerabilities.MaxSeverity
} }
totalIndexSize += manifestSize
manifestSummaries = append(manifestSummaries, manifestSummary) manifestSummaries = append(manifestSummaries, manifestSummary)
} }
@ -254,24 +234,16 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
} }
} }
imageCveSummary := cvemodel.ImageCVESummary{}
if cveInfo != nil && !skipCVE {
imageCveSummary, err = cveInfo.GetCVESummaryForImageMedia(repo, indexDigestStr, ispec.MediaTypeImageIndex)
if err != nil {
// Log the error, but we should still include the manifest in results
graphql.AddError(ctx, gqlerror.Errorf("unable to run vulnerability scan on tag %s in repo %s: "+
"manifest digest: %s, error: %s", tag, repo, indexDigest, err.Error()))
}
}
indexSize = strconv.FormatInt(totalIndexSize, 10) indexSize = strconv.FormatInt(totalIndexSize, 10)
signaturesInfo := GetSignaturesInfo(isSigned, repoMeta, indexDigest) signaturesInfo := GetSignaturesInfo(isSigned, repoMeta, indexDigest)
manifestAnnotations, configLabels := GetOneManifestAnnotations(indexContent, manifestMetaMap) if manifestAnnotations == nil {
annotations := GetIndexAnnotations(indexContent.Annotations, manifestAnnotations, configLabels) // The index doesn't have manifests
manifestAnnotations = &ImageAnnotations{}
}
annotations := GetIndexAnnotations(indexContent.Annotations, manifestAnnotations)
indexSummary := gql_generated.ImageSummary{ indexSummary := gql_generated.ImageSummary{
RepoName: &repo, RepoName: &repo,
@ -292,42 +264,14 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
Source: &annotations.Source, Source: &annotations.Source,
Vendor: &annotations.Vendor, Vendor: &annotations.Vendor,
Authors: &annotations.Authors, Authors: &annotations.Authors,
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{ Referrers: getReferrers(repoMeta.Referrers[indexDigest.String()]),
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
},
Referrers: getReferrers(repoMeta.Referrers[indexDigest.String()]),
} }
return &indexSummary, indexBlobs, nil return &indexSummary, indexBlobs, nil
} }
func GetOneManifestAnnotations(indexContent ispec.Index, manifestMetaMap map[string]mTypes.ManifestMetadata, func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest godigest.Digest,
) (map[string]string, map[string]string) { repoMeta mTypes.RepoMetadata, manifestMeta mTypes.ManifestMetadata,
if len(manifestMetaMap) == 0 || len(indexContent.Manifests) == 0 {
return map[string]string{}, map[string]string{}
}
manifestMeta := manifestMetaMap[indexContent.Manifests[0].Digest.String()]
manifestContent := ispec.Manifest{}
configContent := ispec.Image{}
err := json.Unmarshal(manifestMeta.ManifestBlob, &manifestContent)
if err != nil {
return map[string]string{}, map[string]string{}
}
err = json.Unmarshal(manifestMeta.ConfigBlob, &configContent)
if err != nil {
return map[string]string{}, map[string]string{}
}
return manifestContent.Annotations, configContent.Config.Labels
}
func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest godigest.Digest, skipCVE bool,
repoMeta mTypes.RepoMetadata, manifestMeta mTypes.ManifestMetadata, cveInfo cveinfo.CveInfo,
) (*gql_generated.ImageSummary, map[string]int64, error) { ) (*gql_generated.ImageSummary, map[string]int64, error) {
var ( var (
manifestContent ispec.Manifest manifestContent ispec.Manifest
@ -390,45 +334,29 @@ func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest go
"manifest digest: %s, error: %s", tag, repo, manifestDigest, err.Error())) "manifest digest: %s, error: %s", tag, repo, manifestDigest, err.Error()))
} }
imageCveSummary := cvemodel.ImageCVESummary{}
if cveInfo != nil && !skipCVE {
imageCveSummary, err = cveInfo.GetCVESummaryForImageMedia(repo, manifestDigest, ispec.MediaTypeImageManifest)
if err != nil {
// Log the error, but we should still include the manifest in results
graphql.AddError(ctx, gqlerror.Errorf("unable to run vulnerability scan on tag %s in repo %s: "+
"manifest digest: %s, error: %s", tag, repo, manifestDigest, err.Error()))
}
}
signaturesInfo := GetSignaturesInfo(isSigned, repoMeta, digest) signaturesInfo := GetSignaturesInfo(isSigned, repoMeta, digest)
manifestSummary := gql_generated.ManifestSummary{
Digest: &manifestDigest,
ConfigDigest: &configDigest,
LastUpdated: &imageLastUpdated,
Size: &imageSize,
IsSigned: &isSigned,
SignatureInfo: signaturesInfo,
Platform: &platform,
DownloadCount: &downloadCount,
Layers: getLayersSummaries(manifestContent),
History: historyEntries,
Referrers: getReferrers(repoMeta.Referrers[manifestDigest]),
ArtifactType: &artifactType,
}
imageSummary := gql_generated.ImageSummary{ imageSummary := gql_generated.ImageSummary{
RepoName: &repoName, RepoName: &repoName,
Tag: &tag, Tag: &tag,
Digest: &manifestDigest, Digest: &manifestDigest,
MediaType: &mediaType, MediaType: &mediaType,
Manifests: []*gql_generated.ManifestSummary{ Manifests: []*gql_generated.ManifestSummary{&manifestSummary},
{
Digest: &manifestDigest,
ConfigDigest: &configDigest,
LastUpdated: &imageLastUpdated,
Size: &imageSize,
IsSigned: &isSigned,
SignatureInfo: signaturesInfo,
Platform: &platform,
DownloadCount: &downloadCount,
Layers: getLayersSummaries(manifestContent),
History: historyEntries,
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
},
Referrers: getReferrers(repoMeta.Referrers[manifestDigest]),
ArtifactType: &artifactType,
},
},
LastUpdated: &imageLastUpdated, LastUpdated: &imageLastUpdated,
IsSigned: &isSigned, IsSigned: &isSigned,
SignatureInfo: signaturesInfo, SignatureInfo: signaturesInfo,
@ -442,11 +370,7 @@ func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest go
Source: &annotations.Source, Source: &annotations.Source,
Vendor: &annotations.Vendor, Vendor: &annotations.Vendor,
Authors: &authors, Authors: &authors,
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{ Referrers: getReferrers(repoMeta.Referrers[manifestDigest]),
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
},
Referrers: getReferrers(repoMeta.Referrers[manifestDigest]),
} }
return &imageSummary, imageBlobsMap, nil return &imageSummary, imageBlobsMap, nil
@ -487,9 +411,9 @@ func getAnnotationsFromMap(annotationsMap map[string]string) []*gql_generated.An
} }
func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descriptor ispec.Descriptor, func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descriptor ispec.Descriptor,
skipCVE bool, repoMeta mTypes.RepoMetadata, manifestMeta mTypes.ManifestMetadata, repoMeta mTypes.RepoMetadata, manifestMeta mTypes.ManifestMetadata,
referrersInfo []mTypes.ReferrerInfo, cveInfo cveinfo.CveInfo, referrersInfo []mTypes.ReferrerInfo,
) (*gql_generated.ManifestSummary, map[string]int64, error) { ) (*gql_generated.ManifestSummary, map[string]int64, *ImageAnnotations, error) {
var ( var (
manifestContent ispec.Manifest manifestContent ispec.Manifest
digest = descriptor.Digest digest = descriptor.Digest
@ -500,10 +424,11 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
graphql.AddError(ctx, gqlerror.Errorf("can't unmarshal manifest blob for image: %s:%s, manifest digest: %s, "+ graphql.AddError(ctx, gqlerror.Errorf("can't unmarshal manifest blob for image: %s:%s, manifest digest: %s, "+
"error: %s", repo, tag, digest, err.Error())) "error: %s", repo, tag, digest, err.Error()))
return &gql_generated.ManifestSummary{}, map[string]int64{}, err return &gql_generated.ManifestSummary{}, map[string]int64{}, &ImageAnnotations{}, err
} }
configContent := mcommon.InitializeImageConfig(manifestMeta.ConfigBlob) configContent := mcommon.InitializeImageConfig(manifestMeta.ConfigBlob)
annotations := GetAnnotations(manifestContent.Annotations, configContent.Config.Labels)
var ( var (
manifestDigestStr = digest.String() manifestDigestStr = digest.String()
@ -537,18 +462,6 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
"manifest digest: %s, error: %s", tag, repo, manifestDigestStr, err.Error())) "manifest digest: %s, error: %s", tag, repo, manifestDigestStr, err.Error()))
} }
imageCveSummary := cvemodel.ImageCVESummary{}
if cveInfo != nil && !skipCVE {
imageCveSummary, err = cveInfo.GetCVESummaryForImageMedia(repo, manifestDigestStr, ispec.MediaTypeImageManifest)
if err != nil {
// Log the error, but we should still include the manifest in results
graphql.AddError(ctx, gqlerror.Errorf("unable to run vulnerability scan on tag %s in repo %s: "+
"manifest digest: %s, error: %s", tag, repo, manifestDigestStr, err.Error()))
}
}
for _, signatures := range repoMeta.Signatures[manifestDigestStr] { for _, signatures := range repoMeta.Signatures[manifestDigestStr] {
if len(signatures) > 0 { if len(signatures) > 0 {
isSigned = true isSigned = true
@ -568,15 +481,11 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
History: historyEntries, History: historyEntries,
IsSigned: &isSigned, IsSigned: &isSigned,
SignatureInfo: signaturesInfo, SignatureInfo: signaturesInfo,
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{ Referrers: getReferrers(referrersInfo),
MaxSeverity: &imageCveSummary.MaxSeverity, ArtifactType: &artifactType,
Count: &imageCveSummary.Count,
},
Referrers: getReferrers(referrersInfo),
ArtifactType: &artifactType,
} }
return &manifestSummary, imageBlobsMap, nil return &manifestSummary, imageBlobsMap, &annotations, nil
} }
func getImageBlobsInfo(manifestDigest string, manifestSize int64, configDigest string, configSize int64, func getImageBlobsInfo(manifestDigest string, manifestSize int64, configDigest string, configSize int64,
@ -622,12 +531,15 @@ func RepoMeta2ImageSummaries(ctx context.Context, repoMeta mTypes.RepoMetadata,
for _, tag := range tags { for _, tag := range tags {
descriptor := repoMeta.Tags[tag] descriptor := repoMeta.Tags[tag]
imageSummary, _, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag, skip.Vulnerabilities, imageSummary, _, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag,
repoMeta, manifestMetaMap, indexDataMap, cveInfo) repoMeta, manifestMetaMap, indexDataMap)
if err != nil { if err != nil {
continue continue
} }
// CVE scanning is expensive, only scan for final slice of results
updateImageSummaryVulnerabilities(ctx, imageSummary, skip, cveInfo)
imageSummaries = append(imageSummaries, imageSummary) imageSummaries = append(imageSummaries, imageSummary)
} }
@ -647,8 +559,8 @@ func PaginatedRepoMeta2ImageSummaries(ctx context.Context, reposMeta []mTypes.Re
for tag := range repoMeta.Tags { for tag := range repoMeta.Tags {
descriptor := repoMeta.Tags[tag] descriptor := repoMeta.Tags[tag]
imageSummary, _, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag, skip.Vulnerabilities, imageSummary, _, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag,
repoMeta, manifestMetaMap, indexDataMap, cveInfo) repoMeta, manifestMetaMap, indexDataMap)
if err != nil { if err != nil {
continue continue
} }
@ -661,6 +573,11 @@ func PaginatedRepoMeta2ImageSummaries(ctx context.Context, reposMeta []mTypes.Re
page, pageInfo := imagePageFinder.Page() page, pageInfo := imagePageFinder.Page()
for _, imageSummary := range page {
// CVE scanning is expensive, only scan for this page
updateImageSummaryVulnerabilities(ctx, imageSummary, skip, cveInfo)
}
return page, pageInfo, nil return page, pageInfo, nil
} }
@ -691,7 +608,7 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMetadata
for tag, descriptor := range repoMeta.Tags { for tag, descriptor := range repoMeta.Tags {
imageSummary, imageBlobs, err := Descriptor2ImageSummary(ctx, descriptor, repoName, tag, imageSummary, imageBlobs, err := Descriptor2ImageSummary(ctx, descriptor, repoName, tag,
skip.Vulnerabilities, repoMeta, manifestMetaMap, indexDataMap, cveInfo) repoMeta, manifestMetaMap, indexDataMap)
if err != nil { if err != nil {
log.Error().Str("repository", repoName).Str("reference", tag). log.Error().Str("repository", repoName).Str("reference", tag).
Msg("metadb: error while converting descriptor for image") Msg("metadb: error while converting descriptor for image")
@ -713,6 +630,8 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMetadata
repoVendorsSet[*imageSummary.Vendor] = true repoVendorsSet[*imageSummary.Vendor] = true
} }
updateImageSummaryVulnerabilities(ctx, imageSummary, skip, cveInfo)
lastUpdatedImageSummary = UpdateLastUpdatedTimestamp(&repoLastUpdatedTimestamp, lastUpdatedImageSummary, imageSummary) lastUpdatedImageSummary = UpdateLastUpdatedTimestamp(&repoLastUpdatedTimestamp, lastUpdatedImageSummary, imageSummary)
repoDownloadCount += *imageSummary.DownloadCount repoDownloadCount += *imageSummary.DownloadCount
@ -739,27 +658,6 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMetadata
vendor := vendor vendor := vendor
repoVendors = append(repoVendors, &vendor) repoVendors = append(repoVendors, &vendor)
} }
// We only scan the latest image on the repo for performance reasons
// Check if vulnerability scanning is disabled
if cveInfo != nil && lastUpdatedImageSummary != nil && !skip.Vulnerabilities {
imageCveSummary, err := cveInfo.GetCVESummaryForImageMedia(repoMeta.Name, *lastUpdatedImageSummary.Digest,
*lastUpdatedImageSummary.MediaType)
if err != nil {
// Log the error, but we should still include the image in results
graphql.AddError(
ctx,
gqlerror.Errorf(
"unable to run vulnerability scan on tag %s in repo %s: error: %s",
*lastUpdatedImageSummary.Tag, repoMeta.Name, err.Error(),
),
)
}
lastUpdatedImageSummary.Vulnerabilities = &gql_generated.ImageVulnerabilitySummary{
MaxSeverity: &imageCveSummary.MaxSeverity,
Count: &imageCveSummary.Count,
}
}
summary := &gql_generated.RepoSummary{ summary := &gql_generated.RepoSummary{
Name: &repoName, Name: &repoName,
@ -774,6 +672,8 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMetadata
IsStarred: &isStarred, IsStarred: &isStarred,
} }
updateRepoSummaryVulnerabilities(ctx, summary, skip, cveInfo)
return summary, imageSummaries return summary, imageSummaries
} }

View file

@ -24,7 +24,6 @@ type CveInfo interface {
) ([]cvemodel.CVE, zcommon.PageInfo, error) ) ([]cvemodel.CVE, zcommon.PageInfo, error)
GetCVESummaryForImage(repo, ref string) (cvemodel.ImageCVESummary, error) GetCVESummaryForImage(repo, ref string) (cvemodel.ImageCVESummary, error)
GetCVESummaryForImageMedia(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error) GetCVESummaryForImageMedia(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error)
CompareSeverities(severity1, severity2 string) int
UpdateDB() error UpdateDB() error
} }
@ -32,7 +31,6 @@ type Scanner interface {
ScanImage(image string) (map[string]cvemodel.CVE, error) ScanImage(image string) (map[string]cvemodel.CVE, error)
IsImageFormatScannable(repo, ref string) (bool, error) IsImageFormatScannable(repo, ref string) (bool, error)
IsImageMediaScannable(repo, digestStr, mediaType string) (bool, error) IsImageMediaScannable(repo, digestStr, mediaType string) (bool, error)
CompareSeverities(severity1, severity2 string) int
UpdateDB() error UpdateDB() error
} }
@ -347,7 +345,7 @@ func (cveinfo BaseCveInfo) GetCVEListForImage(repo, ref string, searchedCVE stri
return []cvemodel.CVE{}, zcommon.PageInfo{}, err return []cvemodel.CVE{}, zcommon.PageInfo{}, err
} }
pageFinder, err := NewCvePageFinder(pageInput.Limit, pageInput.Offset, pageInput.SortBy, cveinfo) pageFinder, err := NewCvePageFinder(pageInput.Limit, pageInput.Offset, pageInput.SortBy)
if err != nil { if err != nil {
return []cvemodel.CVE{}, zcommon.PageInfo{}, err return []cvemodel.CVE{}, zcommon.PageInfo{}, err
} }
@ -366,7 +364,7 @@ func (cveinfo BaseCveInfo) GetCVESummaryForImage(repo, ref string) (cvemodel.Ima
// scannable issues found - max severity from Scanner - cve count >0 - no Errors // scannable issues found - max severity from Scanner - cve count >0 - no Errors
imageCVESummary := cvemodel.ImageCVESummary{ imageCVESummary := cvemodel.ImageCVESummary{
Count: 0, Count: 0,
MaxSeverity: "", MaxSeverity: cvemodel.SeverityNotScanned,
} }
isValidImage, err := cveinfo.Scanner.IsImageFormatScannable(repo, ref) isValidImage, err := cveinfo.Scanner.IsImageFormatScannable(repo, ref)
@ -384,14 +382,14 @@ func (cveinfo BaseCveInfo) GetCVESummaryForImage(repo, ref string) (cvemodel.Ima
imageCVESummary.Count = len(cveMap) imageCVESummary.Count = len(cveMap)
if imageCVESummary.Count == 0 { if imageCVESummary.Count == 0 {
imageCVESummary.MaxSeverity = "NONE" imageCVESummary.MaxSeverity = cvemodel.SeverityNone
return imageCVESummary, nil return imageCVESummary, nil
} }
imageCVESummary.MaxSeverity = "UNKNOWN" imageCVESummary.MaxSeverity = cvemodel.SeverityUnknown
for _, cve := range cveMap { for _, cve := range cveMap {
if cveinfo.Scanner.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 { if cvemodel.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 {
imageCVESummary.MaxSeverity = cve.Severity imageCVESummary.MaxSeverity = cve.Severity
} }
} }
@ -401,9 +399,13 @@ func (cveinfo BaseCveInfo) GetCVESummaryForImage(repo, ref string) (cvemodel.Ima
func (cveinfo BaseCveInfo) GetCVESummaryForImageMedia(repo, digest, mediaType string, func (cveinfo BaseCveInfo) GetCVESummaryForImageMedia(repo, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) { ) (cvemodel.ImageCVESummary, error) {
// There are several cases, expected returned values below:
// not scannable / error during scan - max severity "" - cve count 0 - Errors
// scannable no issues found - max severity "NONE" - cve count 0 - no Errors
// scannable issues found - max severity from Scanner - cve count >0 - no Errors
imageCVESummary := cvemodel.ImageCVESummary{ imageCVESummary := cvemodel.ImageCVESummary{
Count: 0, Count: 0,
MaxSeverity: "", MaxSeverity: cvemodel.SeverityNotScanned,
} }
isValidImage, err := cveinfo.Scanner.IsImageMediaScannable(repo, digest, mediaType) isValidImage, err := cveinfo.Scanner.IsImageMediaScannable(repo, digest, mediaType)
@ -421,14 +423,14 @@ func (cveinfo BaseCveInfo) GetCVESummaryForImageMedia(repo, digest, mediaType st
imageCVESummary.Count = len(cveMap) imageCVESummary.Count = len(cveMap)
if imageCVESummary.Count == 0 { if imageCVESummary.Count == 0 {
imageCVESummary.MaxSeverity = "NONE" imageCVESummary.MaxSeverity = cvemodel.SeverityNone
return imageCVESummary, nil return imageCVESummary, nil
} }
imageCVESummary.MaxSeverity = "UNKNOWN" imageCVESummary.MaxSeverity = cvemodel.SeverityUnknown
for _, cve := range cveMap { for _, cve := range cveMap {
if cveinfo.Scanner.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 { if cvemodel.CompareSeverities(imageCVESummary.MaxSeverity, cve.Severity) > 0 {
imageCVESummary.MaxSeverity = cve.Severity imageCVESummary.MaxSeverity = cve.Severity
} }
} }
@ -440,10 +442,6 @@ func (cveinfo BaseCveInfo) UpdateDB() error {
return cveinfo.Scanner.UpdateDB() return cveinfo.Scanner.UpdateDB()
} }
func (cveinfo BaseCveInfo) CompareSeverities(severity1, severity2 string) int {
return cveinfo.Scanner.CompareSeverities(severity1, severity2)
}
func GetFixedTags(allTags, vulnerableTags []cvemodel.TagInfo) []cvemodel.TagInfo { func GetFixedTags(allTags, vulnerableTags []cvemodel.TagInfo) []cvemodel.TagInfo {
sort.Slice(allTags, func(i, j int) bool { sort.Slice(allTags, func(i, j int) bool {
return allTags[i].Timestamp.Before(allTags[j].Timestamp) return allTags[i].Timestamp.Before(allTags[j].Timestamp)

View file

@ -1,3 +1,5 @@
//go:build search
package cveinfo package cveinfo
import ( import (

View file

@ -1029,15 +1029,7 @@ func TestCVEStruct(t *testing.T) {
err = metaDB.SetRepoReference("repoIndex", "tagIndex", indexDigest, ispec.MediaTypeImageIndex) err = metaDB.SetRepoReference("repoIndex", "tagIndex", indexDigest, ispec.MediaTypeImageIndex)
So(err, ShouldBeNil) So(err, ShouldBeNil)
// MetaDB loaded with initial data, mock the scanner // MetaDB loaded with initial data, now mock the scanner
severities := map[string]int{
"UNKNOWN": 0,
"LOW": 1,
"MEDIUM": 2,
"HIGH": 3,
"CRITICAL": 4,
}
// Setup test CVE data in mock scanner // Setup test CVE data in mock scanner
scanner := mocks.CveScannerMock{ scanner := mocks.CveScannerMock{
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) { ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
@ -1124,9 +1116,6 @@ func TestCVEStruct(t *testing.T) {
// By default the image has no vulnerabilities // By default the image has no vulnerabilities
return map[string]cvemodel.CVE{}, nil return map[string]cvemodel.CVE{}, nil
}, },
CompareSeveritiesFn: func(severity1, severity2 string) int {
return severities[severity2] - severities[severity1]
},
IsImageFormatScannableFn: func(repo string, reference string) (bool, error) { IsImageFormatScannableFn: func(repo string, reference string) (bool, error) {
if repo == "repoIndex" { if repo == "repoIndex" {
return true, nil return true, nil

View file

@ -28,23 +28,50 @@ type Package struct {
} }
const ( const (
None = iota unScanned = iota
Low none
Medium unknown
High low
Critical medium
high
critical
) )
func SeverityValue(severity string) int { // Values from https://www.first.org/cvss/v3.0/specification-document
const (
SeverityNotScanned = "" // scanning was not done or was not complete
SeverityNone = "NONE" // no vulnerabilities were detected at all
SeverityUnknown = "UNKNOWN" // coresponds to CVSS 3 score NONE
SeverityLow = "LOW" // coresponds to CVSS 3 score LOW
SeverityMedium = "MEDIUM" // coresponds to CVSS 3 score MEDIUM
SeverityHigh = "HIGH" // coresponds to CVSS 3 score HIGH
SeverityCritical = "CRITICAL" // coresponds to CVSS 3 score CRITICAL
)
func severityInt(severity string) int {
sevMap := map[string]int{ sevMap := map[string]int{
"NONE": None, SeverityNotScanned: unScanned,
"LOW": Low, SeverityNone: none,
"MEDIUM": Medium, SeverityUnknown: unknown,
"HIGH": High, SeverityLow: low,
"CRITICAL": Critical, SeverityMedium: medium,
SeverityHigh: high,
SeverityCritical: critical,
} }
return sevMap[severity] severityInt, ok := sevMap[severity]
if !ok {
// In the unlikely case the key is not in the map we
// return the unknown severity level
return unknown
}
return severityInt
}
func CompareSeverities(sev1, sev2 string) int {
return severityInt(sev2) - severityInt(sev1)
} }
type Descriptor struct { type Descriptor struct {

View file

@ -15,33 +15,33 @@ const (
SeverityDsc = cvemodel.SortCriteria("SEVERITY") SeverityDsc = cvemodel.SortCriteria("SEVERITY")
) )
func SortFunctions() map[cvemodel.SortCriteria]func(pageBuffer []cvemodel.CVE, cveInfo CveInfo) func(i, j int) bool { func SortFunctions() map[cvemodel.SortCriteria]func(pageBuffer []cvemodel.CVE) func(i, j int) bool {
return map[cvemodel.SortCriteria]func(pageBuffer []cvemodel.CVE, cveInfo CveInfo) func(i, j int) bool{ return map[cvemodel.SortCriteria]func(pageBuffer []cvemodel.CVE) func(i, j int) bool{
AlphabeticAsc: SortByAlphabeticAsc, AlphabeticAsc: SortByAlphabeticAsc,
AlphabeticDsc: SortByAlphabeticDsc, AlphabeticDsc: SortByAlphabeticDsc,
SeverityDsc: SortBySeverity, SeverityDsc: SortBySeverity,
} }
} }
func SortByAlphabeticAsc(pageBuffer []cvemodel.CVE, cveInfo CveInfo) func(i, j int) bool { func SortByAlphabeticAsc(pageBuffer []cvemodel.CVE) func(i, j int) bool {
return func(i, j int) bool { return func(i, j int) bool {
return pageBuffer[i].ID < pageBuffer[j].ID return pageBuffer[i].ID < pageBuffer[j].ID
} }
} }
func SortByAlphabeticDsc(pageBuffer []cvemodel.CVE, cveInfo CveInfo) func(i, j int) bool { func SortByAlphabeticDsc(pageBuffer []cvemodel.CVE) func(i, j int) bool {
return func(i, j int) bool { return func(i, j int) bool {
return pageBuffer[i].ID > pageBuffer[j].ID return pageBuffer[i].ID > pageBuffer[j].ID
} }
} }
func SortBySeverity(pageBuffer []cvemodel.CVE, cveInfo CveInfo) func(i, j int) bool { func SortBySeverity(pageBuffer []cvemodel.CVE) func(i, j int) bool {
return func(i, j int) bool { return func(i, j int) bool {
if cveInfo.CompareSeverities(pageBuffer[i].Severity, pageBuffer[j].Severity) == 0 { if cvemodel.CompareSeverities(pageBuffer[i].Severity, pageBuffer[j].Severity) == 0 {
return pageBuffer[i].ID < pageBuffer[j].ID return pageBuffer[i].ID < pageBuffer[j].ID
} }
return cveInfo.CompareSeverities(pageBuffer[i].Severity, pageBuffer[j].Severity) < 0 return cvemodel.CompareSeverities(pageBuffer[i].Severity, pageBuffer[j].Severity) < 0
} }
} }
@ -60,10 +60,9 @@ type CvePageFinder struct {
offset int offset int
sortBy cvemodel.SortCriteria sortBy cvemodel.SortCriteria
pageBuffer []cvemodel.CVE pageBuffer []cvemodel.CVE
cveInfo CveInfo
} }
func NewCvePageFinder(limit, offset int, sortBy cvemodel.SortCriteria, cveInfo CveInfo) (*CvePageFinder, error) { func NewCvePageFinder(limit, offset int, sortBy cvemodel.SortCriteria) (*CvePageFinder, error) {
if sortBy == "" { if sortBy == "" {
sortBy = SeverityDsc sortBy = SeverityDsc
} }
@ -85,7 +84,6 @@ func NewCvePageFinder(limit, offset int, sortBy cvemodel.SortCriteria, cveInfo C
offset: offset, offset: offset,
sortBy: sortBy, sortBy: sortBy,
pageBuffer: make([]cvemodel.CVE, 0, limit), pageBuffer: make([]cvemodel.CVE, 0, limit),
cveInfo: cveInfo,
}, nil }, nil
} }
@ -104,7 +102,7 @@ func (bpt *CvePageFinder) Page() ([]cvemodel.CVE, common.PageInfo) {
pageInfo := &common.PageInfo{} pageInfo := &common.PageInfo{}
sort.Slice(bpt.pageBuffer, SortFunctions()[bpt.sortBy](bpt.pageBuffer, bpt.cveInfo)) sort.Slice(bpt.pageBuffer, SortFunctions()[bpt.sortBy](bpt.pageBuffer))
// the offset and limit are calculated in terms of CVEs counted // the offset and limit are calculated in terms of CVEs counted
start := bpt.offset start := bpt.offset

View file

@ -146,30 +146,27 @@ func TestCVEPagination(t *testing.T) {
// By default the image has no vulnerabilities // By default the image has no vulnerabilities
return cveMap, nil return cveMap, nil
}, },
CompareSeveritiesFn: func(severity1, severity2 string) int {
return severityToInt[severity2] - severityToInt[severity1]
},
} }
log := log.NewLogger("debug", "") log := log.NewLogger("debug", "")
cveInfo := cveinfo.BaseCveInfo{Log: log, Scanner: scanner, MetaDB: metaDB} cveInfo := cveinfo.BaseCveInfo{Log: log, Scanner: scanner, MetaDB: metaDB}
Convey("create new paginator errors", func() { Convey("create new paginator errors", func() {
paginator, err := cveinfo.NewCvePageFinder(-1, 10, cveinfo.AlphabeticAsc, cveInfo) paginator, err := cveinfo.NewCvePageFinder(-1, 10, cveinfo.AlphabeticAsc)
So(paginator, ShouldBeNil) So(paginator, ShouldBeNil)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
paginator, err = cveinfo.NewCvePageFinder(2, -1, cveinfo.AlphabeticAsc, cveInfo) paginator, err = cveinfo.NewCvePageFinder(2, -1, cveinfo.AlphabeticAsc)
So(paginator, ShouldBeNil) So(paginator, ShouldBeNil)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
paginator, err = cveinfo.NewCvePageFinder(2, 1, "wrong sorting criteria", cveInfo) paginator, err = cveinfo.NewCvePageFinder(2, 1, "wrong sorting criteria")
So(paginator, ShouldBeNil) So(paginator, ShouldBeNil)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
Convey("Reset", func() { Convey("Reset", func() {
paginator, err := cveinfo.NewCvePageFinder(1, 0, cveinfo.AlphabeticAsc, cveInfo) paginator, err := cveinfo.NewCvePageFinder(1, 0, cveinfo.AlphabeticAsc)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(paginator, ShouldNotBeNil) So(paginator, ShouldNotBeNil)

View file

@ -30,6 +30,8 @@ import (
"zotregistry.io/zot/pkg/storage" "zotregistry.io/zot/pkg/storage"
) )
const cacheSize = 1000000
// getNewScanOptions sets trivy configuration values for our scans and returns them as // getNewScanOptions sets trivy configuration values for our scans and returns them as
// a trivy Options structure. // a trivy Options structure.
func getNewScanOptions(dir, dbRepository, javaDBRepository string) *flag.Options { func getNewScanOptions(dir, dbRepository, javaDBRepository string) *flag.Options {
@ -118,7 +120,7 @@ func NewScanner(storeController storage.StoreController,
cveController: cveController, cveController: cveController,
storeController: storeController, storeController: storeController,
dbLock: &sync.Mutex{}, dbLock: &sync.Mutex{},
cache: NewCveCache(10000, log), //nolint:gomnd cache: NewCveCache(cacheSize, log),
dbRepository: dbRepository, dbRepository: dbRepository,
javaDBRepository: javaDBRepository, javaDBRepository: javaDBRepository,
} }
@ -415,7 +417,7 @@ func (scanner Scanner) scanManifest(repo, digest string) (map[string]cvemodel.CV
ID: vulnerability.VulnerabilityID, ID: vulnerability.VulnerabilityID,
Title: vulnerability.Title, Title: vulnerability.Title,
Description: vulnerability.Description, Description: vulnerability.Description,
Severity: vulnerability.Severity, Severity: convertSeverity(vulnerability.Severity),
PackageList: newPkgList, PackageList: newPkgList,
} }
} }
@ -552,6 +554,16 @@ func (scanner Scanner) checkDBPresence() error {
return nil return nil
} }
func (scanner Scanner) CompareSeverities(severity1, severity2 string) int { func convertSeverity(detectedSeverity string) string {
return dbTypes.CompareSeverityString(severity1, severity2) trivySeverity, _ := dbTypes.NewSeverity(detectedSeverity)
sevMap := map[dbTypes.Severity]string{
dbTypes.SeverityUnknown: cvemodel.SeverityUnknown,
dbTypes.SeverityLow: cvemodel.SeverityLow,
dbTypes.SeverityMedium: cvemodel.SeverityMedium,
dbTypes.SeverityHigh: cvemodel.SeverityHigh,
dbTypes.SeverityCritical: cvemodel.SeverityCritical,
}
return sevMap[trivySeverity]
} }

View file

@ -1,3 +1,5 @@
//go:build search
package trivy_test package trivy_test
import ( import (

View file

@ -1,3 +1,5 @@
//go:build search
package pagination_test package pagination_test
import ( import (

View file

@ -152,8 +152,8 @@ func getImageListForDigest(ctx context.Context, digest string, metaDB mTypes.Met
}, nil }, nil
} }
func getImageSummary(ctx context.Context, repo, tag string, digest *string, metaDB mTypes.MetaDB, func getImageSummary(ctx context.Context, repo, tag string, digest *string, skipCVE convert.SkipQGLField,
cveInfo cveinfo.CveInfo, log log.Logger, //nolint:unparam metaDB mTypes.MetaDB, cveInfo cveinfo.CveInfo, log log.Logger, //nolint:unparam
) ( ) (
*gql_generated.ImageSummary, error, *gql_generated.ImageSummary, error,
) { ) {
@ -268,10 +268,7 @@ func getImageSummary(ctx context.Context, repo, tag string, digest *string, meta
log.Error().Str("mediaType", manifestDescriptor.MediaType).Msg("resolver: media type not supported") log.Error().Str("mediaType", manifestDescriptor.MediaType).Msg("resolver: media type not supported")
} }
skip := convert.SkipQGLField{ imageSummaries := convert.RepoMeta2ImageSummaries(ctx, repoMeta, manifestMetaMap, indexDataMap, skipCVE, cveInfo)
Vulnerabilities: canSkipField(convert.GetPreloads(ctx), "Vulnerabilities"),
}
imageSummaries := convert.RepoMeta2ImageSummaries(ctx, repoMeta, manifestMetaMap, indexDataMap, skip, cveInfo)
if len(imageSummaries) == 0 { if len(imageSummaries) == 0 {
return &gql_generated.ImageSummary{}, nil return &gql_generated.ImageSummary{}, nil
@ -809,7 +806,11 @@ func derivedImageList(ctx context.Context, image string, digest *string, metaDB
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("no reference provided") return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("no reference provided")
} }
searchedImage, err := getImageSummary(ctx, imageRepo, imageTag, digest, metaDB, cveInfo, log) skipReferenceImage := convert.SkipQGLField{
Vulnerabilities: true,
}
searchedImage, err := getImageSummary(ctx, imageRepo, imageTag, digest, skipReferenceImage, metaDB, cveInfo, log)
if err != nil { if err != nil {
if errors.Is(err, zerr.ErrRepoMetaNotFound) { if errors.Is(err, zerr.ErrRepoMetaNotFound) {
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("repository: not found") return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("repository: not found")
@ -911,7 +912,11 @@ func baseImageList(ctx context.Context, image string, digest *string, metaDB mTy
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("no reference provided") return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("no reference provided")
} }
searchedImage, err := getImageSummary(ctx, imageRepo, imageTag, digest, metaDB, cveInfo, log) skipReferenceImage := convert.SkipQGLField{
Vulnerabilities: true,
}
searchedImage, err := getImageSummary(ctx, imageRepo, imageTag, digest, skipReferenceImage, metaDB, cveInfo, log)
if err != nil { if err != nil {
if errors.Is(err, zerr.ErrRepoMetaNotFound) { if errors.Is(err, zerr.ErrRepoMetaNotFound) {
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("repository: not found") return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("repository: not found")

View file

@ -1,3 +1,5 @@
//go:build search
package search //nolint package search //nolint
import ( import (
@ -16,6 +18,7 @@ import (
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
"zotregistry.io/zot/pkg/common" "zotregistry.io/zot/pkg/common"
"zotregistry.io/zot/pkg/extensions/search/convert"
cveinfo "zotregistry.io/zot/pkg/extensions/search/cve" cveinfo "zotregistry.io/zot/pkg/extensions/search/cve"
cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model" cvemodel "zotregistry.io/zot/pkg/extensions/search/cve/model"
"zotregistry.io/zot/pkg/extensions/search/gql_generated" "zotregistry.io/zot/pkg/extensions/search/gql_generated"
@ -1182,9 +1185,13 @@ func TestGetImageSummary(t *testing.T) {
} }
log = log.NewLogger("debug", "") log = log.NewLogger("debug", "")
skip = convert.SkipQGLField{
Vulnerabilities: true,
}
) )
_, err := getImageSummary(responseContext, "repo", "tag", nil, metaDB, mocks.CveInfoMock{}, log) _, err := getImageSummary(responseContext, "repo", "tag", nil, skip, metaDB, mocks.CveInfoMock{}, log)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
@ -1201,9 +1208,13 @@ func TestGetImageSummary(t *testing.T) {
} }
log = log.NewLogger("debug", "") log = log.NewLogger("debug", "")
skip = convert.SkipQGLField{
Vulnerabilities: true,
}
) )
_, err := getImageSummary(responseContext, "repo", "tag", nil, metaDB, mocks.CveInfoMock{}, log) _, err := getImageSummary(responseContext, "repo", "tag", nil, skip, metaDB, mocks.CveInfoMock{}, log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
@ -1225,9 +1236,13 @@ func TestGetImageSummary(t *testing.T) {
log = log.NewLogger("debug", "") log = log.NewLogger("debug", "")
digest = "wrongDigest" digest = "wrongDigest"
skip = convert.SkipQGLField{
Vulnerabilities: true,
}
) )
_, err := getImageSummary(responseContext, "repo", "tag", &digest, metaDB, mocks.CveInfoMock{}, log) _, err := getImageSummary(responseContext, "repo", "tag", &digest, skip, metaDB, mocks.CveInfoMock{}, log)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
}) })
@ -1249,9 +1264,13 @@ func TestGetImageSummary(t *testing.T) {
} }
log = log.NewLogger("debug", "") log = log.NewLogger("debug", "")
skip = convert.SkipQGLField{
Vulnerabilities: true,
}
) )
_, err := getImageSummary(responseContext, "repo", "tag", nil, metaDB, mocks.CveInfoMock{}, log) _, err := getImageSummary(responseContext, "repo", "tag", nil, skip, metaDB, mocks.CveInfoMock{}, log)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
@ -1273,9 +1292,13 @@ func TestGetImageSummary(t *testing.T) {
} }
log = log.NewLogger("debug", "") log = log.NewLogger("debug", "")
skip = convert.SkipQGLField{
Vulnerabilities: true,
}
) )
_, err := getImageSummary(responseContext, "repo", "tag", nil, metaDB, mocks.CveInfoMock{}, log) _, err := getImageSummary(responseContext, "repo", "tag", nil, skip, metaDB, mocks.CveInfoMock{}, log)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
@ -1313,7 +1336,12 @@ func TestGetImageSummary(t *testing.T) {
Convey("digest not found", func() { Convey("digest not found", func() {
wrongDigest := "wrongDigest" wrongDigest := "wrongDigest"
_, err = getImageSummary(responseContext, "repo", "tag", &wrongDigest, metaDB, mocks.CveInfoMock{}, log)
skip := convert.SkipQGLField{
Vulnerabilities: true,
}
_, err = getImageSummary(responseContext, "repo", "tag", &wrongDigest, skip, metaDB, mocks.CveInfoMock{}, log)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
@ -1322,7 +1350,11 @@ func TestGetImageSummary(t *testing.T) {
return mTypes.ManifestData{}, ErrTestError return mTypes.ManifestData{}, ErrTestError
} }
_, err = getImageSummary(responseContext, "repo", "tag", &goodDigest, metaDB, mocks.CveInfoMock{}, log) skip := convert.SkipQGLField{
Vulnerabilities: true,
}
_, err = getImageSummary(responseContext, "repo", "tag", &goodDigest, skip, metaDB, mocks.CveInfoMock{}, log)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
}) })
@ -1341,9 +1373,13 @@ func TestGetImageSummary(t *testing.T) {
} }
log = log.NewLogger("debug", "") log = log.NewLogger("debug", "")
skip = convert.SkipQGLField{
Vulnerabilities: true,
}
) )
_, err := getImageSummary(responseContext, "repo", "tag", nil, metaDB, mocks.CveInfoMock{}, log) _, err := getImageSummary(responseContext, "repo", "tag", nil, skip, metaDB, mocks.CveInfoMock{}, log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
}) })
}) })
@ -2042,17 +2078,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
} }
} }
// Create the repo metadata using previously defined manifests // MetaDB loaded with initial data, now mock the scanner
// MetaDB loaded with initial data, mock the scanner
severities := map[string]int{
"UNKNOWN": 0,
"LOW": 1,
"MEDIUM": 2,
"HIGH": 3,
"CRITICAL": 4,
}
// Setup test CVE data in mock scanner // Setup test CVE data in mock scanner
scanner := mocks.CveScannerMock{ scanner := mocks.CveScannerMock{
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) { ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
@ -2126,9 +2152,6 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
// By default the image has no vulnerabilities // By default the image has no vulnerabilities
return map[string]cvemodel.CVE{}, nil return map[string]cvemodel.CVE{}, nil
}, },
CompareSeveritiesFn: func(severity1, severity2 string) int {
return severities[severity2] - severities[severity1]
},
} }
cveInfo := &cveinfo.BaseCveInfo{ cveInfo := &cveinfo.BaseCveInfo{

View file

@ -10,6 +10,7 @@ import (
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
zerr "zotregistry.io/zot/errors" zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/common" "zotregistry.io/zot/pkg/common"
"zotregistry.io/zot/pkg/extensions/search/convert"
"zotregistry.io/zot/pkg/extensions/search/gql_generated" "zotregistry.io/zot/pkg/extensions/search/gql_generated"
) )
@ -133,7 +134,11 @@ func (r *queryResolver) Image(ctx context.Context, image string) (*gql_generated
return &gql_generated.ImageSummary{}, gqlerror.Errorf("no reference provided") return &gql_generated.ImageSummary{}, gqlerror.Errorf("no reference provided")
} }
return getImageSummary(ctx, repo, tag, nil, r.metaDB, r.cveInfo, r.log) skip := convert.SkipQGLField{
Vulnerabilities: canSkipField(convert.GetPreloads(ctx), "Vulnerabilities"),
}
return getImageSummary(ctx, repo, tag, nil, skip, r.metaDB, r.cveInfo, r.log)
} }
// Referrers is the resolver for the Referrers field. // Referrers is the resolver for the Referrers field.

View file

@ -222,14 +222,6 @@ func uploadNewRepoTag(tag string, repoName string, baseURL string, layers [][]by
func getMockCveInfo(metaDB mTypes.MetaDB, log log.Logger) cveinfo.CveInfo { func getMockCveInfo(metaDB mTypes.MetaDB, log log.Logger) cveinfo.CveInfo {
// MetaDB loaded with initial data, mock the scanner // MetaDB loaded with initial data, mock the scanner
severities := map[string]int{
"UNKNOWN": 0,
"LOW": 1,
"MEDIUM": 2,
"HIGH": 3,
"CRITICAL": 4,
}
// Setup test CVE data in mock scanner // Setup test CVE data in mock scanner
scanner := mocks.CveScannerMock{ scanner := mocks.CveScannerMock{
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) { ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
@ -310,9 +302,6 @@ func getMockCveInfo(metaDB mTypes.MetaDB, log log.Logger) cveinfo.CveInfo {
// By default the image has no vulnerabilities // By default the image has no vulnerabilities
return map[string]cvemodel.CVE{}, nil return map[string]cvemodel.CVE{}, nil
}, },
CompareSeveritiesFn: func(severity1, severity2 string) int {
return severities[severity2] - severities[severity1]
},
IsImageFormatScannableFn: func(repo string, reference string) (bool, error) { IsImageFormatScannableFn: func(repo string, reference string) (bool, error) {
// Almost same logic compared to actual Trivy specific implementation // Almost same logic compared to actual Trivy specific implementation
imageDir := repo imageDir := repo

View file

@ -465,7 +465,7 @@ func (bdw *BoltDB) SetRepoReference(repo string, reference string, manifestDiges
func (bdw *BoltDB) GetRepoMeta(repo string) (mTypes.RepoMetadata, error) { func (bdw *BoltDB) GetRepoMeta(repo string) (mTypes.RepoMetadata, error) {
var repoMeta mTypes.RepoMetadata var repoMeta mTypes.RepoMetadata
err := bdw.DB.Update(func(tx *bbolt.Tx) error { err := bdw.DB.View(func(tx *bbolt.Tx) error {
buck := tx.Bucket([]byte(RepoMetadataBucket)) buck := tx.Bucket([]byte(RepoMetadataBucket))
repoMetaBlob := buck.Get([]byte(repo)) repoMetaBlob := buck.Get([]byte(repo))
@ -490,7 +490,7 @@ func (bdw *BoltDB) GetRepoMeta(repo string) (mTypes.RepoMetadata, error) {
func (bdw *BoltDB) GetUserRepoMeta(ctx context.Context, repo string) (mTypes.RepoMetadata, error) { func (bdw *BoltDB) GetUserRepoMeta(ctx context.Context, repo string) (mTypes.RepoMetadata, error) {
var repoMeta mTypes.RepoMetadata var repoMeta mTypes.RepoMetadata
err := bdw.DB.Update(func(tx *bbolt.Tx) error { err := bdw.DB.View(func(tx *bbolt.Tx) error {
buck := tx.Bucket([]byte(RepoMetadataBucket)) buck := tx.Bucket([]byte(RepoMetadataBucket))
userBookmarks := getUserBookmarks(ctx, tx) userBookmarks := getUserBookmarks(ctx, tx)
userStars := getUserStars(ctx, tx) userStars := getUserStars(ctx, tx)

View file

@ -20,6 +20,8 @@ import (
// ParseStorage will sync all repos found in the rootdirectory of the oci layout that zot was deployed on with the // ParseStorage will sync all repos found in the rootdirectory of the oci layout that zot was deployed on with the
// ParseStorage database. // ParseStorage database.
func ParseStorage(metaDB mTypes.MetaDB, storeController storage.StoreController, log log.Logger) error { func ParseStorage(metaDB mTypes.MetaDB, storeController storage.StoreController, log log.Logger) error {
log.Info().Msg("Started parsing storage and updating MetaDB")
allRepos, err := getAllRepos(storeController) allRepos, err := getAllRepos(storeController)
if err != nil { if err != nil {
rootDir := storeController.DefaultStore.RootDir() rootDir := storeController.DefaultStore.RootDir()
@ -38,6 +40,8 @@ func ParseStorage(metaDB mTypes.MetaDB, storeController storage.StoreController,
} }
} }
log.Info().Msg("Done parsing storage and updating MetaDB")
return nil return nil
} }

View file

@ -14,8 +14,7 @@ type CveInfoMock struct {
) (cvemodel.ImageCVESummary, error) ) (cvemodel.ImageCVESummary, error)
GetCVESummaryForImageMediaFn func(repo string, digest, mediaType string, GetCVESummaryForImageMediaFn func(repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) ) (cvemodel.ImageCVESummary, error)
CompareSeveritiesFn func(severity1, severity2 string) int UpdateDBFn func() error
UpdateDBFn func() error
} }
func (cveInfo CveInfoMock) GetImageListForCVE(repo, cveID string) ([]cvemodel.TagInfo, error) { func (cveInfo CveInfoMock) GetImageListForCVE(repo, cveID string) ([]cvemodel.TagInfo, error) {
@ -66,14 +65,6 @@ func (cveInfo CveInfoMock) GetCVESummaryForImageMedia(repo, digest, mediaType st
return cvemodel.ImageCVESummary{}, nil return cvemodel.ImageCVESummary{}, nil
} }
func (cveInfo CveInfoMock) CompareSeverities(severity1, severity2 string) int {
if cveInfo.CompareSeveritiesFn != nil {
return cveInfo.CompareSeveritiesFn(severity1, severity2)
}
return 0
}
func (cveInfo CveInfoMock) UpdateDB() error { func (cveInfo CveInfoMock) UpdateDB() error {
if cveInfo.UpdateDBFn != nil { if cveInfo.UpdateDBFn != nil {
return cveInfo.UpdateDBFn() return cveInfo.UpdateDBFn()
@ -86,7 +77,6 @@ type CveScannerMock struct {
IsImageFormatScannableFn func(repo string, reference string) (bool, error) IsImageFormatScannableFn func(repo string, reference string) (bool, error)
IsImageMediaScannableFn func(repo string, digest, mediaType string) (bool, error) IsImageMediaScannableFn func(repo string, digest, mediaType string) (bool, error)
ScanImageFn func(image string) (map[string]cvemodel.CVE, error) ScanImageFn func(image string) (map[string]cvemodel.CVE, error)
CompareSeveritiesFn func(severity1, severity2 string) int
UpdateDBFn func() error UpdateDBFn func() error
} }
@ -114,14 +104,6 @@ func (scanner CveScannerMock) ScanImage(image string) (map[string]cvemodel.CVE,
return map[string]cvemodel.CVE{}, nil return map[string]cvemodel.CVE{}, nil
} }
func (scanner CveScannerMock) CompareSeverities(severity1, severity2 string) int {
if scanner.CompareSeveritiesFn != nil {
return scanner.CompareSeveritiesFn(severity1, severity2)
}
return 0
}
func (scanner CveScannerMock) UpdateDB() error { func (scanner CveScannerMock) UpdateDB() error {
if scanner.UpdateDBFn != nil { if scanner.UpdateDBFn != nil {
return scanner.UpdateDBFn() return scanner.UpdateDBFn()