-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]>
- Loading branch information
Showing
23 changed files
with
675 additions
and
462 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
}) | ||
}) | ||
} |
Oops, something went wrong.