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:
parent
f58597ade9
commit
bcdd9988f5
23 changed files with 676 additions and 463 deletions
|
@ -59,7 +59,7 @@ func TestSearchCVECmd(t *testing.T) {
|
|||
ctlr := api.NewController(conf)
|
||||
cm := test.NewControllerManager(ctlr)
|
||||
|
||||
cm.StartServer()
|
||||
cm.StartAndWait(port)
|
||||
defer cm.StopServer()
|
||||
|
||||
Convey("Test CVE help", t, func() {
|
||||
|
@ -947,21 +947,10 @@ func TestCVESort(t *testing.T) {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
severities := map[string]int{
|
||||
"UNKNOWN": 0,
|
||||
"LOW": 1,
|
||||
"MEDIUM": 2,
|
||||
"HIGH": 3,
|
||||
"CRITICAL": 4,
|
||||
}
|
||||
|
||||
ctlr.CveInfo = cveinfo.BaseCveInfo{
|
||||
Log: ctlr.Log,
|
||||
MetaDB: mocks.MetaDBMock{},
|
||||
Scanner: mocks.CveScannerMock{
|
||||
CompareSeveritiesFn: func(severity1, severity2 string) int {
|
||||
return severities[severity2] - severities[severity1]
|
||||
},
|
||||
ScanImageFn: func(image string) (map[string]cvemodel.CVE, error) {
|
||||
return map[string]cvemodel.CVE{
|
||||
"CVE-2023-1255": {
|
||||
|
@ -1385,15 +1374,7 @@ func TestCVECommandErrors(t *testing.T) {
|
|||
}
|
||||
|
||||
func getMockCveInfo(metaDB mTypes.MetaDB, log log.Logger) cveinfo.CveInfo {
|
||||
// MetaDB loaded with initial data, mock the scanner
|
||||
severities := map[string]int{
|
||||
"UNKNOWN": 0,
|
||||
"LOW": 1,
|
||||
"MEDIUM": 2,
|
||||
"HIGH": 3,
|
||||
"CRITICAL": 4,
|
||||
}
|
||||
|
||||
// MetaDB loaded with initial data now mock the scanner
|
||||
// Setup test CVE data in mock scanner
|
||||
scanner := mocks.CveScannerMock{
|
||||
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
|
||||
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) {
|
||||
// Almost same logic compared to actual Trivy specific implementation
|
||||
imageDir := repo
|
||||
|
|
|
@ -70,7 +70,7 @@ func TestTrivyDBGenerator(t *testing.T) {
|
|||
|
||||
// Wait for trivy db to download
|
||||
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(found, ShouldBeTrue)
|
||||
})
|
||||
|
|
|
@ -134,9 +134,10 @@ func GetAnnotations(annotations, labels map[string]string) ImageAnnotations {
|
|||
}
|
||||
}
|
||||
|
||||
func GetIndexAnnotations(indexAnnotations, manifestAnnotations, manifestLabels map[string]string) ImageAnnotations {
|
||||
annotationsFromManifest := GetAnnotations(manifestAnnotations, manifestLabels)
|
||||
|
||||
func GetIndexAnnotations(
|
||||
indexAnnotations map[string]string,
|
||||
annotationsFromManifest *ImageAnnotations,
|
||||
) ImageAnnotations {
|
||||
description := GetDescription(indexAnnotations)
|
||||
if description == "" {
|
||||
description = annotationsFromManifest.Description
|
||||
|
|
302
pkg/extensions/search/convert/convert_internal_test.go
Normal file
302
pkg/extensions/search/convert/convert_internal_test.go
Normal 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")
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
//go:build search
|
||||
|
||||
package convert_test
|
||||
|
||||
import (
|
||||
|
@ -17,7 +19,6 @@ import (
|
|||
"zotregistry.io/zot/pkg/extensions/search/gql_generated"
|
||||
"zotregistry.io/zot/pkg/extensions/search/pagination"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
"zotregistry.io/zot/pkg/meta/boltdb"
|
||||
mTypes "zotregistry.io/zot/pkg/meta/types"
|
||||
"zotregistry.io/zot/pkg/test"
|
||||
. "zotregistry.io/zot/pkg/test/image-utils"
|
||||
|
@ -27,64 +28,6 @@ import (
|
|||
var ErrTestError = errors.New("TestError")
|
||||
|
||||
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() {
|
||||
ctx := graphql.WithResponseContext(context.Background(),
|
||||
graphql.DefaultErrorPresenter, graphql.DefaultRecover)
|
||||
|
@ -94,13 +37,11 @@ func TestConvertErrors(t *testing.T) {
|
|||
"repo",
|
||||
"tag",
|
||||
godigest.FromString("indexDigest"),
|
||||
true,
|
||||
mTypes.RepoMetadata{},
|
||||
mTypes.IndexData{
|
||||
IndexBlob: []byte("bad json"),
|
||||
},
|
||||
map[string]mTypes.ManifestMetadata{},
|
||||
mocks.CveInfoMock{},
|
||||
)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
@ -114,17 +55,11 @@ func TestConvertErrors(t *testing.T) {
|
|||
"repo",
|
||||
"tag",
|
||||
godigest.FromString("indexDigest"),
|
||||
false,
|
||||
mTypes.RepoMetadata{},
|
||||
mTypes.IndexData{
|
||||
IndexBlob: []byte("{}"),
|
||||
},
|
||||
map[string]mTypes.ManifestMetadata{},
|
||||
mocks.CveInfoMock{
|
||||
GetCVESummaryForImageMediaFn: func(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error) {
|
||||
return cvemodel.ImageCVESummary{}, ErrTestError
|
||||
},
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
@ -146,17 +81,11 @@ func TestConvertErrors(t *testing.T) {
|
|||
"repo",
|
||||
"tag",
|
||||
godigest.FromString("manifestDigest"),
|
||||
false,
|
||||
mTypes.RepoMetadata{},
|
||||
mTypes.ManifestMetadata{
|
||||
ManifestBlob: []byte("{}"),
|
||||
ConfigBlob: configBlob,
|
||||
},
|
||||
mocks.CveInfoMock{
|
||||
GetCVESummaryForImageMediaFn: func(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error) {
|
||||
return cvemodel.ImageCVESummary{}, ErrTestError
|
||||
},
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
@ -166,7 +95,7 @@ func TestConvertErrors(t *testing.T) {
|
|||
graphql.DefaultErrorPresenter, graphql.DefaultRecover)
|
||||
|
||||
// with bad config json, shouldn't error when unmarshaling
|
||||
_, _, err := convert.ImageManifest2ManifestSummary(
|
||||
_, _, _, err := convert.ImageManifest2ManifestSummary(
|
||||
ctx,
|
||||
"repo",
|
||||
"tag",
|
||||
|
@ -174,7 +103,6 @@ func TestConvertErrors(t *testing.T) {
|
|||
Digest: "dig",
|
||||
MediaType: ispec.MediaTypeImageManifest,
|
||||
},
|
||||
false,
|
||||
mTypes.RepoMetadata{
|
||||
Tags: map[string]mTypes.Descriptor{},
|
||||
Statistics: map[string]mTypes.DescriptorStatistics{},
|
||||
|
@ -186,7 +114,6 @@ func TestConvertErrors(t *testing.T) {
|
|||
ConfigBlob: []byte("bad json"),
|
||||
},
|
||||
nil,
|
||||
mocks.CveInfoMock{},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
|
@ -200,7 +127,7 @@ func TestConvertErrors(t *testing.T) {
|
|||
})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, err = convert.ImageManifest2ManifestSummary(
|
||||
_, _, _, err = convert.ImageManifest2ManifestSummary(
|
||||
ctx,
|
||||
"repo",
|
||||
"tag",
|
||||
|
@ -208,7 +135,6 @@ func TestConvertErrors(t *testing.T) {
|
|||
Digest: "dig",
|
||||
MediaType: ispec.MediaTypeImageManifest,
|
||||
},
|
||||
false,
|
||||
mTypes.RepoMetadata{
|
||||
Tags: map[string]mTypes.Descriptor{},
|
||||
Statistics: map[string]mTypes.DescriptorStatistics{},
|
||||
|
@ -220,11 +146,6 @@ func TestConvertErrors(t *testing.T) {
|
|||
ConfigBlob: configBlob,
|
||||
},
|
||||
nil,
|
||||
mocks.CveInfoMock{
|
||||
GetCVESummaryForImageMediaFn: func(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error) {
|
||||
return cvemodel.ImageCVESummary{}, ErrTestError
|
||||
},
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
@ -772,37 +693,7 @@ func TestPaginatedConvert(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGetOneManifestAnnotations(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)
|
||||
})
|
||||
|
||||
func TestIndexAnnotations(t *testing.T) {
|
||||
Convey("Test ImageIndex2ImageSummary annotations logic", t, func() {
|
||||
ctx := context.Background()
|
||||
|
||||
|
@ -864,8 +755,8 @@ func TestGetOneManifestAnnotations(t *testing.T) {
|
|||
|
||||
digest := indexWithAnnotations.Digest()
|
||||
|
||||
imageSummary, _, err := convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest, true, repoMeta[0],
|
||||
indexData[digest.String()], manifestMetadata, nil)
|
||||
imageSummary, _, err := convert.ImageIndex2ImageSummary(ctx, "repo", "tag", digest, repoMeta[0],
|
||||
indexData[digest.String()], manifestMetadata)
|
||||
So(err, ShouldBeNil)
|
||||
So(*imageSummary.Description, ShouldResemble, "IndexDescription")
|
||||
So(*imageSummary.Licenses, ShouldResemble, "IndexLicenses")
|
||||
|
@ -887,7 +778,7 @@ func TestGetOneManifestAnnotations(t *testing.T) {
|
|||
digest = indexWithManifestAndConfigAnnotations.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(*imageSummary.Description, ShouldResemble, "ManifestDescription")
|
||||
So(*imageSummary.Licenses, ShouldResemble, "ManifestLicenses")
|
||||
|
@ -908,7 +799,7 @@ func TestGetOneManifestAnnotations(t *testing.T) {
|
|||
digest = indexWithConfigAnnotations.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(*imageSummary.Description, ShouldResemble, "ConfigDescription")
|
||||
So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses")
|
||||
|
@ -950,7 +841,7 @@ func TestGetOneManifestAnnotations(t *testing.T) {
|
|||
digest = indexWithMixAnnotations.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(*imageSummary.Description, ShouldResemble, "ConfigDescription")
|
||||
So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses")
|
||||
|
@ -970,7 +861,7 @@ func TestGetOneManifestAnnotations(t *testing.T) {
|
|||
digest = indexWithNoAnnotations.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(*imageSummary.Description, ShouldBeBlank)
|
||||
So(*imageSummary.Licenses, ShouldBeBlank)
|
||||
|
@ -999,8 +890,7 @@ func TestDownloadCount(t *testing.T) {
|
|||
},
|
||||
)
|
||||
|
||||
repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap,
|
||||
convert.SkipQGLField{}, nil)
|
||||
repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap)
|
||||
So(*repoSummary.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,
|
||||
convert.SkipQGLField{}, nil)
|
||||
repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap)
|
||||
So(*repoSummary.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,
|
||||
convert.SkipQGLField{}, nil)
|
||||
repoSummary := convert.RepoMeta2RepoSummary(context.Background(), repoMeta[0], manifestMetaMap, indexDataMap)
|
||||
So(*repoSummary.DownloadCount, ShouldEqual, 105)
|
||||
So(*repoSummary.NewestImage.DownloadCount, ShouldEqual, 100)
|
||||
})
|
||||
|
|
109
pkg/extensions/search/convert/cve.go
Normal file
109
pkg/extensions/search/convert/cve.go
Normal 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
|
||||
}
|
|
@ -17,7 +17,6 @@ import (
|
|||
zerr "zotregistry.io/zot/errors"
|
||||
zcommon "zotregistry.io/zot/pkg/common"
|
||||
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/pagination"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
|
@ -31,7 +30,6 @@ type SkipQGLField struct {
|
|||
|
||||
func RepoMeta2RepoSummary(ctx context.Context, repoMeta mTypes.RepoMetadata,
|
||||
manifestMetaMap map[string]mTypes.ManifestMetadata, indexDataMap map[string]mTypes.IndexData,
|
||||
skip SkipQGLField, cveInfo cveinfo.CveInfo,
|
||||
) *gql_generated.RepoSummary {
|
||||
var (
|
||||
repoName = repoMeta.Name
|
||||
|
@ -53,8 +51,9 @@ func RepoMeta2RepoSummary(ctx context.Context, repoMeta mTypes.RepoMetadata,
|
|||
)
|
||||
|
||||
for tag, descriptor := range repoMeta.Tags {
|
||||
imageSummary, imageBlobsMap, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag, true, repoMeta,
|
||||
manifestMetaMap, indexDataMap, cveInfo)
|
||||
imageSummary, imageBlobsMap, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name,
|
||||
tag, repoMeta, manifestMetaMap, indexDataMap,
|
||||
)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -101,28 +100,6 @@ func RepoMeta2RepoSummary(ctx context.Context, repoMeta mTypes.RepoMetadata,
|
|||
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{
|
||||
Name: &repoName,
|
||||
LastUpdated: &repoLastUpdatedTimestamp,
|
||||
|
@ -148,7 +125,7 @@ func PaginatedRepoMeta2RepoSummaries(ctx context.Context, reposMeta []mTypes.Rep
|
|||
}
|
||||
|
||||
for _, repoMeta := range reposMeta {
|
||||
repoSummary := RepoMeta2RepoSummary(ctx, repoMeta, manifestMetaMap, indexDataMap, skip, cveInfo)
|
||||
repoSummary := RepoMeta2RepoSummary(ctx, repoMeta, manifestMetaMap, indexDataMap)
|
||||
|
||||
if RepoSumAcceptedByFilter(repoSummary, filter) {
|
||||
reposPageFinder.Add(repoSummary)
|
||||
|
@ -157,6 +134,11 @@ func PaginatedRepoMeta2RepoSummaries(ctx context.Context, reposMeta []mTypes.Rep
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -177,25 +159,24 @@ func UpdateLastUpdatedTimestamp(repoLastUpdatedTimestamp *time.Time,
|
|||
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,
|
||||
indexDataMap map[string]mTypes.IndexData, cveInfo cveinfo.CveInfo,
|
||||
indexDataMap map[string]mTypes.IndexData,
|
||||
) (*gql_generated.ImageSummary, map[string]int64, error) {
|
||||
switch descriptor.MediaType {
|
||||
case ispec.MediaTypeImageManifest:
|
||||
return ImageManifest2ImageSummary(ctx, repo, tag, godigest.Digest(descriptor.Digest), skipCVE,
|
||||
repoMeta, manifestMetaMap[descriptor.Digest], cveInfo)
|
||||
return ImageManifest2ImageSummary(ctx, repo, tag, godigest.Digest(descriptor.Digest),
|
||||
repoMeta, manifestMetaMap[descriptor.Digest])
|
||||
case ispec.MediaTypeImageIndex:
|
||||
return ImageIndex2ImageSummary(ctx, repo, tag, godigest.Digest(descriptor.Digest), skipCVE,
|
||||
repoMeta, indexDataMap[descriptor.Digest], manifestMetaMap, cveInfo)
|
||||
return ImageIndex2ImageSummary(ctx, repo, tag, godigest.Digest(descriptor.Digest),
|
||||
repoMeta, indexDataMap[descriptor.Digest], manifestMetaMap)
|
||||
default:
|
||||
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,
|
||||
cveInfo cveinfo.CveInfo,
|
||||
) (*gql_generated.ImageSummary, map[string]int64, error) {
|
||||
var indexContent ispec.Index
|
||||
|
||||
|
@ -205,22 +186,22 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
|
|||
}
|
||||
|
||||
var (
|
||||
indexLastUpdated time.Time
|
||||
isSigned bool
|
||||
totalIndexSize int64
|
||||
indexSize string
|
||||
totalDownloadCount int
|
||||
maxSeverity string
|
||||
manifestSummaries = make([]*gql_generated.ManifestSummary, 0, len(indexContent.Manifests))
|
||||
indexBlobs = make(map[string]int64, 0)
|
||||
indexLastUpdated time.Time
|
||||
isSigned bool
|
||||
totalIndexSize int64
|
||||
indexSize string
|
||||
totalDownloadCount int
|
||||
manifestAnnotations *ImageAnnotations
|
||||
manifestSummaries = make([]*gql_generated.ManifestSummary, 0, len(indexContent.Manifests))
|
||||
indexBlobs = make(map[string]int64, 0)
|
||||
|
||||
indexDigestStr = indexDigest.String()
|
||||
indexMediaType = ispec.MediaTypeImageIndex
|
||||
)
|
||||
|
||||
for _, descriptor := range indexContent.Manifests {
|
||||
manifestSummary, manifestBlobs, err := ImageManifest2ManifestSummary(ctx, repo, tag, descriptor, false,
|
||||
repoMeta, manifestMetaMap[descriptor.Digest.String()], repoMeta.Referrers[descriptor.Digest.String()], cveInfo)
|
||||
manifestSummary, manifestBlobs, annotations, err := ImageManifest2ManifestSummary(ctx, repo, tag, descriptor,
|
||||
repoMeta, manifestMetaMap[descriptor.Digest.String()], repoMeta.Referrers[descriptor.Digest.String()])
|
||||
if err != nil {
|
||||
return &gql_generated.ImageSummary{}, map[string]int64{}, err
|
||||
}
|
||||
|
@ -236,13 +217,12 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
|
|||
indexLastUpdated = *manifestSummary.LastUpdated
|
||||
}
|
||||
|
||||
totalIndexSize += manifestSize
|
||||
|
||||
if cvemodel.SeverityValue(*manifestSummary.Vulnerabilities.MaxSeverity) >
|
||||
cvemodel.SeverityValue(maxSeverity) {
|
||||
maxSeverity = *manifestSummary.Vulnerabilities.MaxSeverity
|
||||
if manifestAnnotations == nil {
|
||||
manifestAnnotations = annotations
|
||||
}
|
||||
|
||||
totalIndexSize += manifestSize
|
||||
|
||||
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)
|
||||
|
||||
signaturesInfo := GetSignaturesInfo(isSigned, repoMeta, indexDigest)
|
||||
|
||||
manifestAnnotations, configLabels := GetOneManifestAnnotations(indexContent, manifestMetaMap)
|
||||
annotations := GetIndexAnnotations(indexContent.Annotations, manifestAnnotations, configLabels)
|
||||
if manifestAnnotations == nil {
|
||||
// The index doesn't have manifests
|
||||
manifestAnnotations = &ImageAnnotations{}
|
||||
}
|
||||
|
||||
annotations := GetIndexAnnotations(indexContent.Annotations, manifestAnnotations)
|
||||
|
||||
indexSummary := gql_generated.ImageSummary{
|
||||
RepoName: &repo,
|
||||
|
@ -292,42 +264,14 @@ func ImageIndex2ImageSummary(ctx context.Context, repo, tag string, indexDigest
|
|||
Source: &annotations.Source,
|
||||
Vendor: &annotations.Vendor,
|
||||
Authors: &annotations.Authors,
|
||||
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{
|
||||
MaxSeverity: &imageCveSummary.MaxSeverity,
|
||||
Count: &imageCveSummary.Count,
|
||||
},
|
||||
Referrers: getReferrers(repoMeta.Referrers[indexDigest.String()]),
|
||||
Referrers: getReferrers(repoMeta.Referrers[indexDigest.String()]),
|
||||
}
|
||||
|
||||
return &indexSummary, indexBlobs, nil
|
||||
}
|
||||
|
||||
func GetOneManifestAnnotations(indexContent ispec.Index, manifestMetaMap map[string]mTypes.ManifestMetadata,
|
||||
) (map[string]string, map[string]string) {
|
||||
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,
|
||||
func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest godigest.Digest,
|
||||
repoMeta mTypes.RepoMetadata, manifestMeta mTypes.ManifestMetadata,
|
||||
) (*gql_generated.ImageSummary, map[string]int64, error) {
|
||||
var (
|
||||
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()))
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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{
|
||||
RepoName: &repoName,
|
||||
Tag: &tag,
|
||||
Digest: &manifestDigest,
|
||||
MediaType: &mediaType,
|
||||
Manifests: []*gql_generated.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,
|
||||
},
|
||||
},
|
||||
RepoName: &repoName,
|
||||
Tag: &tag,
|
||||
Digest: &manifestDigest,
|
||||
MediaType: &mediaType,
|
||||
Manifests: []*gql_generated.ManifestSummary{&manifestSummary},
|
||||
LastUpdated: &imageLastUpdated,
|
||||
IsSigned: &isSigned,
|
||||
SignatureInfo: signaturesInfo,
|
||||
|
@ -442,11 +370,7 @@ func ImageManifest2ImageSummary(ctx context.Context, repo, tag string, digest go
|
|||
Source: &annotations.Source,
|
||||
Vendor: &annotations.Vendor,
|
||||
Authors: &authors,
|
||||
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{
|
||||
MaxSeverity: &imageCveSummary.MaxSeverity,
|
||||
Count: &imageCveSummary.Count,
|
||||
},
|
||||
Referrers: getReferrers(repoMeta.Referrers[manifestDigest]),
|
||||
Referrers: getReferrers(repoMeta.Referrers[manifestDigest]),
|
||||
}
|
||||
|
||||
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,
|
||||
skipCVE bool, repoMeta mTypes.RepoMetadata, manifestMeta mTypes.ManifestMetadata,
|
||||
referrersInfo []mTypes.ReferrerInfo, cveInfo cveinfo.CveInfo,
|
||||
) (*gql_generated.ManifestSummary, map[string]int64, error) {
|
||||
repoMeta mTypes.RepoMetadata, manifestMeta mTypes.ManifestMetadata,
|
||||
referrersInfo []mTypes.ReferrerInfo,
|
||||
) (*gql_generated.ManifestSummary, map[string]int64, *ImageAnnotations, error) {
|
||||
var (
|
||||
manifestContent ispec.Manifest
|
||||
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, "+
|
||||
"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)
|
||||
annotations := GetAnnotations(manifestContent.Annotations, configContent.Config.Labels)
|
||||
|
||||
var (
|
||||
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()))
|
||||
}
|
||||
|
||||
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] {
|
||||
if len(signatures) > 0 {
|
||||
isSigned = true
|
||||
|
@ -568,15 +481,11 @@ func ImageManifest2ManifestSummary(ctx context.Context, repo, tag string, descri
|
|||
History: historyEntries,
|
||||
IsSigned: &isSigned,
|
||||
SignatureInfo: signaturesInfo,
|
||||
Vulnerabilities: &gql_generated.ImageVulnerabilitySummary{
|
||||
MaxSeverity: &imageCveSummary.MaxSeverity,
|
||||
Count: &imageCveSummary.Count,
|
||||
},
|
||||
Referrers: getReferrers(referrersInfo),
|
||||
ArtifactType: &artifactType,
|
||||
Referrers: getReferrers(referrersInfo),
|
||||
ArtifactType: &artifactType,
|
||||
}
|
||||
|
||||
return &manifestSummary, imageBlobsMap, nil
|
||||
return &manifestSummary, imageBlobsMap, &annotations, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
descriptor := repoMeta.Tags[tag]
|
||||
|
||||
imageSummary, _, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag, skip.Vulnerabilities,
|
||||
repoMeta, manifestMetaMap, indexDataMap, cveInfo)
|
||||
imageSummary, _, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag,
|
||||
repoMeta, manifestMetaMap, indexDataMap)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// CVE scanning is expensive, only scan for final slice of results
|
||||
updateImageSummaryVulnerabilities(ctx, imageSummary, skip, cveInfo)
|
||||
|
||||
imageSummaries = append(imageSummaries, imageSummary)
|
||||
}
|
||||
|
||||
|
@ -647,8 +559,8 @@ func PaginatedRepoMeta2ImageSummaries(ctx context.Context, reposMeta []mTypes.Re
|
|||
for tag := range repoMeta.Tags {
|
||||
descriptor := repoMeta.Tags[tag]
|
||||
|
||||
imageSummary, _, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag, skip.Vulnerabilities,
|
||||
repoMeta, manifestMetaMap, indexDataMap, cveInfo)
|
||||
imageSummary, _, err := Descriptor2ImageSummary(ctx, descriptor, repoMeta.Name, tag,
|
||||
repoMeta, manifestMetaMap, indexDataMap)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -661,6 +573,11 @@ func PaginatedRepoMeta2ImageSummaries(ctx context.Context, reposMeta []mTypes.Re
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -691,7 +608,7 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMetadata
|
|||
|
||||
for tag, descriptor := range repoMeta.Tags {
|
||||
imageSummary, imageBlobs, err := Descriptor2ImageSummary(ctx, descriptor, repoName, tag,
|
||||
skip.Vulnerabilities, repoMeta, manifestMetaMap, indexDataMap, cveInfo)
|
||||
repoMeta, manifestMetaMap, indexDataMap)
|
||||
if err != nil {
|
||||
log.Error().Str("repository", repoName).Str("reference", tag).
|
||||
Msg("metadb: error while converting descriptor for image")
|
||||
|
@ -713,6 +630,8 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMetadata
|
|||
repoVendorsSet[*imageSummary.Vendor] = true
|
||||
}
|
||||
|
||||
updateImageSummaryVulnerabilities(ctx, imageSummary, skip, cveInfo)
|
||||
|
||||
lastUpdatedImageSummary = UpdateLastUpdatedTimestamp(&repoLastUpdatedTimestamp, lastUpdatedImageSummary, imageSummary)
|
||||
|
||||
repoDownloadCount += *imageSummary.DownloadCount
|
||||
|
@ -739,27 +658,6 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMetadata
|
|||
vendor := 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{
|
||||
Name: &repoName,
|
||||
|
@ -774,6 +672,8 @@ func RepoMeta2ExpandedRepoInfo(ctx context.Context, repoMeta mTypes.RepoMetadata
|
|||
IsStarred: &isStarred,
|
||||
}
|
||||
|
||||
updateRepoSummaryVulnerabilities(ctx, summary, skip, cveInfo)
|
||||
|
||||
return summary, imageSummaries
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ type CveInfo interface {
|
|||
) ([]cvemodel.CVE, zcommon.PageInfo, error)
|
||||
GetCVESummaryForImage(repo, ref string) (cvemodel.ImageCVESummary, error)
|
||||
GetCVESummaryForImageMedia(repo, digest, mediaType string) (cvemodel.ImageCVESummary, error)
|
||||
CompareSeverities(severity1, severity2 string) int
|
||||
UpdateDB() error
|
||||
}
|
||||
|
||||
|
@ -32,7 +31,6 @@ type Scanner interface {
|
|||
ScanImage(image string) (map[string]cvemodel.CVE, error)
|
||||
IsImageFormatScannable(repo, ref string) (bool, error)
|
||||
IsImageMediaScannable(repo, digestStr, mediaType string) (bool, error)
|
||||
CompareSeverities(severity1, severity2 string) int
|
||||
UpdateDB() error
|
||||
}
|
||||
|
||||
|
@ -347,7 +345,7 @@ func (cveinfo BaseCveInfo) GetCVEListForImage(repo, ref string, searchedCVE stri
|
|||
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 {
|
||||
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
|
||||
imageCVESummary := cvemodel.ImageCVESummary{
|
||||
Count: 0,
|
||||
MaxSeverity: "",
|
||||
MaxSeverity: cvemodel.SeverityNotScanned,
|
||||
}
|
||||
|
||||
isValidImage, err := cveinfo.Scanner.IsImageFormatScannable(repo, ref)
|
||||
|
@ -384,14 +382,14 @@ func (cveinfo BaseCveInfo) GetCVESummaryForImage(repo, ref string) (cvemodel.Ima
|
|||
imageCVESummary.Count = len(cveMap)
|
||||
|
||||
if imageCVESummary.Count == 0 {
|
||||
imageCVESummary.MaxSeverity = "NONE"
|
||||
imageCVESummary.MaxSeverity = cvemodel.SeverityNone
|
||||
|
||||
return imageCVESummary, nil
|
||||
}
|
||||
|
||||
imageCVESummary.MaxSeverity = "UNKNOWN"
|
||||
imageCVESummary.MaxSeverity = cvemodel.SeverityUnknown
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -401,9 +399,13 @@ func (cveinfo BaseCveInfo) GetCVESummaryForImage(repo, ref string) (cvemodel.Ima
|
|||
|
||||
func (cveinfo BaseCveInfo) GetCVESummaryForImageMedia(repo, digest, mediaType string,
|
||||
) (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{
|
||||
Count: 0,
|
||||
MaxSeverity: "",
|
||||
MaxSeverity: cvemodel.SeverityNotScanned,
|
||||
}
|
||||
|
||||
isValidImage, err := cveinfo.Scanner.IsImageMediaScannable(repo, digest, mediaType)
|
||||
|
@ -421,14 +423,14 @@ func (cveinfo BaseCveInfo) GetCVESummaryForImageMedia(repo, digest, mediaType st
|
|||
imageCVESummary.Count = len(cveMap)
|
||||
|
||||
if imageCVESummary.Count == 0 {
|
||||
imageCVESummary.MaxSeverity = "NONE"
|
||||
imageCVESummary.MaxSeverity = cvemodel.SeverityNone
|
||||
|
||||
return imageCVESummary, nil
|
||||
}
|
||||
|
||||
imageCVESummary.MaxSeverity = "UNKNOWN"
|
||||
imageCVESummary.MaxSeverity = cvemodel.SeverityUnknown
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -440,10 +442,6 @@ func (cveinfo BaseCveInfo) UpdateDB() error {
|
|||
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 {
|
||||
sort.Slice(allTags, func(i, j int) bool {
|
||||
return allTags[i].Timestamp.Before(allTags[j].Timestamp)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//go:build search
|
||||
|
||||
package cveinfo
|
||||
|
||||
import (
|
||||
|
|
|
@ -1029,15 +1029,7 @@ func TestCVEStruct(t *testing.T) {
|
|||
err = metaDB.SetRepoReference("repoIndex", "tagIndex", indexDigest, ispec.MediaTypeImageIndex)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// MetaDB loaded with initial data, mock the scanner
|
||||
severities := map[string]int{
|
||||
"UNKNOWN": 0,
|
||||
"LOW": 1,
|
||||
"MEDIUM": 2,
|
||||
"HIGH": 3,
|
||||
"CRITICAL": 4,
|
||||
}
|
||||
|
||||
// MetaDB loaded with initial data, now mock the scanner
|
||||
// Setup test CVE data in mock scanner
|
||||
scanner := mocks.CveScannerMock{
|
||||
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
|
||||
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) {
|
||||
if repo == "repoIndex" {
|
||||
return true, nil
|
||||
|
|
|
@ -28,23 +28,50 @@ type Package struct {
|
|||
}
|
||||
|
||||
const (
|
||||
None = iota
|
||||
Low
|
||||
Medium
|
||||
High
|
||||
Critical
|
||||
unScanned = iota
|
||||
none
|
||||
unknown
|
||||
low
|
||||
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{
|
||||
"NONE": None,
|
||||
"LOW": Low,
|
||||
"MEDIUM": Medium,
|
||||
"HIGH": High,
|
||||
"CRITICAL": Critical,
|
||||
SeverityNotScanned: unScanned,
|
||||
SeverityNone: none,
|
||||
SeverityUnknown: unknown,
|
||||
SeverityLow: low,
|
||||
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 {
|
||||
|
|
|
@ -15,33 +15,33 @@ const (
|
|||
SeverityDsc = cvemodel.SortCriteria("SEVERITY")
|
||||
)
|
||||
|
||||
func SortFunctions() map[cvemodel.SortCriteria]func(pageBuffer []cvemodel.CVE, cveInfo CveInfo) func(i, j int) bool {
|
||||
return 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) func(i, j int) bool{
|
||||
AlphabeticAsc: SortByAlphabeticAsc,
|
||||
AlphabeticDsc: SortByAlphabeticDsc,
|
||||
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 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 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 {
|
||||
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 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
|
||||
sortBy cvemodel.SortCriteria
|
||||
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 == "" {
|
||||
sortBy = SeverityDsc
|
||||
}
|
||||
|
@ -85,7 +84,6 @@ func NewCvePageFinder(limit, offset int, sortBy cvemodel.SortCriteria, cveInfo C
|
|||
offset: offset,
|
||||
sortBy: sortBy,
|
||||
pageBuffer: make([]cvemodel.CVE, 0, limit),
|
||||
cveInfo: cveInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -104,7 +102,7 @@ func (bpt *CvePageFinder) Page() ([]cvemodel.CVE, 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
|
||||
start := bpt.offset
|
||||
|
|
|
@ -146,30 +146,27 @@ func TestCVEPagination(t *testing.T) {
|
|||
// By default the image has no vulnerabilities
|
||||
return cveMap, nil
|
||||
},
|
||||
CompareSeveritiesFn: func(severity1, severity2 string) int {
|
||||
return severityToInt[severity2] - severityToInt[severity1]
|
||||
},
|
||||
}
|
||||
|
||||
log := log.NewLogger("debug", "")
|
||||
cveInfo := cveinfo.BaseCveInfo{Log: log, Scanner: scanner, MetaDB: metaDB}
|
||||
|
||||
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(err, ShouldNotBeNil)
|
||||
|
||||
paginator, err = cveinfo.NewCvePageFinder(2, -1, cveinfo.AlphabeticAsc, cveInfo)
|
||||
paginator, err = cveinfo.NewCvePageFinder(2, -1, cveinfo.AlphabeticAsc)
|
||||
So(paginator, ShouldBeNil)
|
||||
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(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Reset", func() {
|
||||
paginator, err := cveinfo.NewCvePageFinder(1, 0, cveinfo.AlphabeticAsc, cveInfo)
|
||||
paginator, err := cveinfo.NewCvePageFinder(1, 0, cveinfo.AlphabeticAsc)
|
||||
So(err, ShouldBeNil)
|
||||
So(paginator, ShouldNotBeNil)
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ import (
|
|||
"zotregistry.io/zot/pkg/storage"
|
||||
)
|
||||
|
||||
const cacheSize = 1000000
|
||||
|
||||
// getNewScanOptions sets trivy configuration values for our scans and returns them as
|
||||
// a trivy Options structure.
|
||||
func getNewScanOptions(dir, dbRepository, javaDBRepository string) *flag.Options {
|
||||
|
@ -118,7 +120,7 @@ func NewScanner(storeController storage.StoreController,
|
|||
cveController: cveController,
|
||||
storeController: storeController,
|
||||
dbLock: &sync.Mutex{},
|
||||
cache: NewCveCache(10000, log), //nolint:gomnd
|
||||
cache: NewCveCache(cacheSize, log),
|
||||
dbRepository: dbRepository,
|
||||
javaDBRepository: javaDBRepository,
|
||||
}
|
||||
|
@ -415,7 +417,7 @@ func (scanner Scanner) scanManifest(repo, digest string) (map[string]cvemodel.CV
|
|||
ID: vulnerability.VulnerabilityID,
|
||||
Title: vulnerability.Title,
|
||||
Description: vulnerability.Description,
|
||||
Severity: vulnerability.Severity,
|
||||
Severity: convertSeverity(vulnerability.Severity),
|
||||
PackageList: newPkgList,
|
||||
}
|
||||
}
|
||||
|
@ -552,6 +554,16 @@ func (scanner Scanner) checkDBPresence() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (scanner Scanner) CompareSeverities(severity1, severity2 string) int {
|
||||
return dbTypes.CompareSeverityString(severity1, severity2)
|
||||
func convertSeverity(detectedSeverity string) string {
|
||||
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]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//go:build search
|
||||
|
||||
package trivy_test
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//go:build search
|
||||
|
||||
package pagination_test
|
||||
|
||||
import (
|
||||
|
|
|
@ -152,8 +152,8 @@ func getImageListForDigest(ctx context.Context, digest string, metaDB mTypes.Met
|
|||
}, nil
|
||||
}
|
||||
|
||||
func getImageSummary(ctx context.Context, repo, tag string, digest *string, metaDB mTypes.MetaDB,
|
||||
cveInfo cveinfo.CveInfo, log log.Logger, //nolint:unparam
|
||||
func getImageSummary(ctx context.Context, repo, tag string, digest *string, skipCVE convert.SkipQGLField,
|
||||
metaDB mTypes.MetaDB, cveInfo cveinfo.CveInfo, log log.Logger, //nolint:unparam
|
||||
) (
|
||||
*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")
|
||||
}
|
||||
|
||||
skip := convert.SkipQGLField{
|
||||
Vulnerabilities: canSkipField(convert.GetPreloads(ctx), "Vulnerabilities"),
|
||||
}
|
||||
imageSummaries := convert.RepoMeta2ImageSummaries(ctx, repoMeta, manifestMetaMap, indexDataMap, skip, cveInfo)
|
||||
imageSummaries := convert.RepoMeta2ImageSummaries(ctx, repoMeta, manifestMetaMap, indexDataMap, skipCVE, cveInfo)
|
||||
|
||||
if len(imageSummaries) == 0 {
|
||||
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")
|
||||
}
|
||||
|
||||
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 errors.Is(err, zerr.ErrRepoMetaNotFound) {
|
||||
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")
|
||||
}
|
||||
|
||||
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 errors.Is(err, zerr.ErrRepoMetaNotFound) {
|
||||
return &gql_generated.PaginatedImagesResult{}, gqlerror.Errorf("repository: not found")
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//go:build search
|
||||
|
||||
package search //nolint
|
||||
|
||||
import (
|
||||
|
@ -16,6 +18,7 @@ import (
|
|||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"zotregistry.io/zot/pkg/common"
|
||||
"zotregistry.io/zot/pkg/extensions/search/convert"
|
||||
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"
|
||||
|
@ -1182,9 +1185,13 @@ func TestGetImageSummary(t *testing.T) {
|
|||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
|
@ -1201,9 +1208,13 @@ func TestGetImageSummary(t *testing.T) {
|
|||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
|
@ -1225,9 +1236,13 @@ func TestGetImageSummary(t *testing.T) {
|
|||
log = log.NewLogger("debug", "")
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
@ -1249,9 +1264,13 @@ func TestGetImageSummary(t *testing.T) {
|
|||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
|
@ -1273,9 +1292,13 @@ func TestGetImageSummary(t *testing.T) {
|
|||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
|
@ -1313,7 +1336,12 @@ func TestGetImageSummary(t *testing.T) {
|
|||
|
||||
Convey("digest not found", func() {
|
||||
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)
|
||||
})
|
||||
|
||||
|
@ -1322,7 +1350,11 @@ func TestGetImageSummary(t *testing.T) {
|
|||
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)
|
||||
})
|
||||
})
|
||||
|
@ -1341,9 +1373,13 @@ func TestGetImageSummary(t *testing.T) {
|
|||
}
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
@ -2042,17 +2078,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
|
|||
}
|
||||
}
|
||||
|
||||
// Create the repo metadata using previously defined manifests
|
||||
|
||||
// MetaDB loaded with initial data, mock the scanner
|
||||
severities := map[string]int{
|
||||
"UNKNOWN": 0,
|
||||
"LOW": 1,
|
||||
"MEDIUM": 2,
|
||||
"HIGH": 3,
|
||||
"CRITICAL": 4,
|
||||
}
|
||||
|
||||
// MetaDB loaded with initial data, now mock the scanner
|
||||
// Setup test CVE data in mock scanner
|
||||
scanner := mocks.CveScannerMock{
|
||||
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
|
||||
return map[string]cvemodel.CVE{}, nil
|
||||
},
|
||||
CompareSeveritiesFn: func(severity1, severity2 string) int {
|
||||
return severities[severity2] - severities[severity1]
|
||||
},
|
||||
}
|
||||
|
||||
cveInfo := &cveinfo.BaseCveInfo{
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
"zotregistry.io/zot/pkg/common"
|
||||
"zotregistry.io/zot/pkg/extensions/search/convert"
|
||||
"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 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.
|
||||
|
|
|
@ -222,14 +222,6 @@ func uploadNewRepoTag(tag string, repoName string, baseURL string, layers [][]by
|
|||
|
||||
func getMockCveInfo(metaDB mTypes.MetaDB, log log.Logger) cveinfo.CveInfo {
|
||||
// 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
|
||||
scanner := mocks.CveScannerMock{
|
||||
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
|
||||
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) {
|
||||
// Almost same logic compared to actual Trivy specific implementation
|
||||
imageDir := repo
|
||||
|
|
|
@ -465,7 +465,7 @@ func (bdw *BoltDB) SetRepoReference(repo string, reference string, manifestDiges
|
|||
func (bdw *BoltDB) GetRepoMeta(repo string) (mTypes.RepoMetadata, error) {
|
||||
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))
|
||||
|
||||
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) {
|
||||
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))
|
||||
userBookmarks := getUserBookmarks(ctx, tx)
|
||||
userStars := getUserStars(ctx, tx)
|
||||
|
|
|
@ -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 database.
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ type CveInfoMock struct {
|
|||
) (cvemodel.ImageCVESummary, error)
|
||||
GetCVESummaryForImageMediaFn func(repo string, digest, mediaType string,
|
||||
) (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) {
|
||||
|
@ -66,14 +65,6 @@ func (cveInfo CveInfoMock) GetCVESummaryForImageMedia(repo, digest, mediaType st
|
|||
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 {
|
||||
if cveInfo.UpdateDBFn != nil {
|
||||
return cveInfo.UpdateDBFn()
|
||||
|
@ -86,7 +77,6 @@ type CveScannerMock struct {
|
|||
IsImageFormatScannableFn func(repo string, reference string) (bool, error)
|
||||
IsImageMediaScannableFn func(repo string, digest, mediaType string) (bool, error)
|
||||
ScanImageFn func(image string) (map[string]cvemodel.CVE, error)
|
||||
CompareSeveritiesFn func(severity1, severity2 string) int
|
||||
UpdateDBFn func() error
|
||||
}
|
||||
|
||||
|
@ -114,14 +104,6 @@ func (scanner CveScannerMock) ScanImage(image string) (map[string]cvemodel.CVE,
|
|||
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 {
|
||||
if scanner.UpdateDBFn != nil {
|
||||
return scanner.UpdateDBFn()
|
||||
|
|
Loading…
Reference in a new issue