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

feat(cve): add option to exclude string from cve search (#2163)

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
This commit is contained in:
LaurentiuNiculae 2024-01-19 22:59:42 +02:00 committed by GitHub
parent 355b1eea4c
commit 3f97f878fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 135 additions and 62 deletions

View file

@ -78,7 +78,7 @@ func DerivedImageListQuery() GQLQuery {
func CVEListForImageQuery() GQLQuery { func CVEListForImageQuery() GQLQuery {
return GQLQuery{ return GQLQuery{
Name: "CVEListForImage", Name: "CVEListForImage",
Args: []string{"image", "requestedPage", "searchedCVE"}, Args: []string{"image", "requestedPage", "searchedCVE", "excludedCVE"},
ReturnType: CVEResultForImage(), ReturnType: CVEResultForImage(),
} }
} }

View file

@ -8,7 +8,6 @@ import (
godigest "github.com/opencontainers/go-digest" godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1" ispec "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/exp/slices"
zerr "zotregistry.io/zot/errors" zerr "zotregistry.io/zot/errors"
zcommon "zotregistry.io/zot/pkg/common" zcommon "zotregistry.io/zot/pkg/common"
@ -22,8 +21,8 @@ import (
type CveInfo interface { type CveInfo interface {
GetImageListForCVE(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error) GetImageListForCVE(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error)
GetImageListWithCVEFixed(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error) GetImageListWithCVEFixed(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error)
GetCVEListForImage(ctx context.Context, repo, tag string, searchedCVE string, pageinput cvemodel.PageInput, GetCVEListForImage(ctx context.Context, repo, tag string, searchedCVE string, excludedCVE string,
) ([]cvemodel.CVE, cvemodel.ImageCVESummary, zcommon.PageInfo, error) pageinput cvemodel.PageInput) ([]cvemodel.CVE, cvemodel.ImageCVESummary, zcommon.PageInfo, error)
GetCVESummaryForImageMedia(ctx context.Context, repo, digestStr, mediaType string) (cvemodel.ImageCVESummary, error) GetCVESummaryForImageMedia(ctx context.Context, repo, digestStr, mediaType string) (cvemodel.ImageCVESummary, error)
} }
@ -330,27 +329,22 @@ func getConfigAndDigest(metaDB mTypes.MetaDB, manifestDigestStr string) (ispec.I
return manifestData.Manifests[0].Config, manifestDigest, err return manifestData.Manifests[0].Config, manifestDigest, err
} }
func filterCVEList(cveMap map[string]cvemodel.CVE, searchedCVE string, pageFinder *CvePageFinder) { func filterCVEList(cveMap map[string]cvemodel.CVE, searchedCVE, excludedCVE string, pageFinder *CvePageFinder) {
searchedCVE = strings.ToUpper(searchedCVE) searchedCVE = strings.ToUpper(searchedCVE)
for _, cve := range cveMap { for _, cve := range cveMap {
if strings.Contains(strings.ToUpper(cve.Title), searchedCVE) || if excludedCVE != "" && cve.ContainsStr(excludedCVE) {
strings.Contains(strings.ToUpper(cve.ID), searchedCVE) || continue
strings.Contains(strings.ToUpper(cve.Description), searchedCVE) || }
strings.Contains(strings.ToUpper(cve.Reference), searchedCVE) ||
strings.Contains(strings.ToUpper(cve.Severity), searchedCVE) || if cve.ContainsStr(searchedCVE) {
slices.ContainsFunc(cve.PackageList, func(pack cvemodel.Package) bool {
return strings.Contains(strings.ToUpper(pack.Name), searchedCVE) ||
strings.Contains(strings.ToUpper(pack.FixedVersion), searchedCVE) ||
strings.Contains(strings.ToUpper(pack.InstalledVersion), searchedCVE)
}) {
pageFinder.Add(cve) pageFinder.Add(cve)
} }
} }
} }
func (cveinfo BaseCveInfo) GetCVEListForImage(ctx context.Context, repo, ref string, searchedCVE string, func (cveinfo BaseCveInfo) GetCVEListForImage(ctx context.Context, repo, ref string, searchedCVE string,
pageInput cvemodel.PageInput, excludedCVE string, pageInput cvemodel.PageInput,
) ( ) (
[]cvemodel.CVE, cvemodel.ImageCVESummary, zcommon.PageInfo, error, []cvemodel.CVE, cvemodel.ImageCVESummary, zcommon.PageInfo, error,
) { ) {
@ -379,7 +373,7 @@ func (cveinfo BaseCveInfo) GetCVEListForImage(ctx context.Context, repo, ref str
return []cvemodel.CVE{}, imageCVESummary, zcommon.PageInfo{}, err return []cvemodel.CVE{}, imageCVESummary, zcommon.PageInfo{}, err
} }
filterCVEList(cveMap, searchedCVE, pageFinder) filterCVEList(cveMap, searchedCVE, excludedCVE, pageFinder)
cveList, pageInfo := pageFinder.Page() cveList, pageInfo := pageFinder.Page()

View file

@ -20,6 +20,21 @@ var ErrTestError = errors.New("test error")
func TestUtils(t *testing.T) { func TestUtils(t *testing.T) {
Convey("Utils", t, func() { Convey("Utils", t, func() {
Convey("cve.ContainsStr for package list", func() {
cve := cvemodel.CVE{
PackageList: []cvemodel.Package{
{
Name: "NameTest",
FixedVersion: "FixedVersionTest",
InstalledVersion: "InstalledVersionTest",
},
},
}
So(cve.ContainsStr("NameTest"), ShouldBeTrue)
So(cve.ContainsStr("FixedVersionTest"), ShouldBeTrue)
So(cve.ContainsStr("InstalledVersionTest"), ShouldBeTrue)
})
Convey("getConfigAndDigest", func() { Convey("getConfigAndDigest", func() {
_, _, err := getConfigAndDigest(mocks.MetaDBMock{}, "bad-digest") _, _, err := getConfigAndDigest(mocks.MetaDBMock{}, "bad-digest")
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)

View file

@ -1192,7 +1192,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
ctx := context.Background() ctx := context.Background()
// Image is found // Image is found
cveList, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", pageInput) cveList, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", "", pageInput)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 1) So(len(cveList), ShouldEqual, 1)
So(cveList[0].ID, ShouldEqual, "CVE1") So(cveList[0].ID, ShouldEqual, "CVE1")
@ -1206,7 +1206,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.CriticalCount, ShouldEqual, 0) So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.0", "", "", pageInput)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 3) So(len(cveList), ShouldEqual, 3)
So(cveList[0].ID, ShouldEqual, "CVE2") So(cveList[0].ID, ShouldEqual, "CVE2")
@ -1222,7 +1222,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.CriticalCount, ShouldEqual, 0) So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "HIGH") So(cveSummary.MaxSeverity, ShouldEqual, "HIGH")
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.1", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.0.1", "", "", pageInput)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 2) So(len(cveList), ShouldEqual, 2)
So(cveList[0].ID, ShouldEqual, "CVE1") So(cveList[0].ID, ShouldEqual, "CVE1")
@ -1237,7 +1237,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.CriticalCount, ShouldEqual, 0) So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.1.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "1.1.0", "", "", pageInput)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 1) So(len(cveList), ShouldEqual, 1)
So(cveList[0].ID, ShouldEqual, "CVE3") So(cveList[0].ID, ShouldEqual, "CVE3")
@ -1251,7 +1251,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.CriticalCount, ShouldEqual, 0) So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "LOW") So(cveSummary.MaxSeverity, ShouldEqual, "LOW")
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo6, "1.0.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo6, "1.0.0", "", "", pageInput)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 0) So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0) So(pageInfo.ItemCount, ShouldEqual, 0)
@ -1264,7 +1264,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.CriticalCount, ShouldEqual, 0) So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "NONE") So(cveSummary.MaxSeverity, ShouldEqual, "NONE")
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo8, "1.0.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo8, "1.0.0", "", "", pageInput)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 7) So(len(cveList), ShouldEqual, 7)
So(pageInfo.ItemCount, ShouldEqual, 7) So(pageInfo.ItemCount, ShouldEqual, 7)
@ -1278,7 +1278,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL") So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
// Image is multiarch // Image is multiarch
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repoMultiarch, "tagIndex", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repoMultiarch, "tagIndex", "", "", pageInput)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cveList), ShouldEqual, 1) So(len(cveList), ShouldEqual, 1)
So(cveList[0].ID, ShouldEqual, "CVE1") So(cveList[0].ID, ShouldEqual, "CVE1")
@ -1293,7 +1293,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM") So(cveSummary.MaxSeverity, ShouldEqual, "MEDIUM")
// Image is not scannable // Image is not scannable
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo2, "1.0.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo2, "1.0.0", "", "", pageInput)
So(err, ShouldEqual, zerr.ErrScanNotSupported) So(err, ShouldEqual, zerr.ErrScanNotSupported)
So(len(cveList), ShouldEqual, 0) So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0) So(pageInfo.ItemCount, ShouldEqual, 0)
@ -1307,7 +1307,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.MaxSeverity, ShouldEqual, "") So(cveSummary.MaxSeverity, ShouldEqual, "")
// Tag is not found // Tag is not found
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo3, "1.0.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo3, "1.0.0", "", "", pageInput)
So(err, ShouldEqual, zerr.ErrTagMetaNotFound) So(err, ShouldEqual, zerr.ErrTagMetaNotFound)
So(len(cveList), ShouldEqual, 0) So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0) So(pageInfo.ItemCount, ShouldEqual, 0)
@ -1321,7 +1321,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.MaxSeverity, ShouldEqual, "") So(cveSummary.MaxSeverity, ShouldEqual, "")
// Scan failed // Scan failed
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo7, "1.0.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo7, "1.0.0", "", "", pageInput)
So(err, ShouldEqual, ErrFailedScan) So(err, ShouldEqual, ErrFailedScan)
So(len(cveList), ShouldEqual, 0) So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0) So(pageInfo.ItemCount, ShouldEqual, 0)
@ -1335,7 +1335,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.MaxSeverity, ShouldEqual, "") So(cveSummary.MaxSeverity, ShouldEqual, "")
// Tag is not found // Tag is not found
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo-with-bad-tag-digest", "tag", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo-with-bad-tag-digest", "tag", "", "", pageInput)
So(err, ShouldEqual, zerr.ErrImageMetaNotFound) So(err, ShouldEqual, zerr.ErrImageMetaNotFound)
So(len(cveList), ShouldEqual, 0) So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0) So(pageInfo.ItemCount, ShouldEqual, 0)
@ -1349,7 +1349,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.MaxSeverity, ShouldEqual, "") So(cveSummary.MaxSeverity, ShouldEqual, "")
// Repo is not found // Repo is not found
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo100, "1.0.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo100, "1.0.0", "", "", pageInput)
So(err, ShouldEqual, zerr.ErrRepoMetaNotFound) So(err, ShouldEqual, zerr.ErrRepoMetaNotFound)
So(len(cveList), ShouldEqual, 0) So(len(cveList), ShouldEqual, 0)
So(pageInfo.ItemCount, ShouldEqual, 0) So(pageInfo.ItemCount, ShouldEqual, 0)
@ -1580,7 +1580,7 @@ func TestCVEStruct(t *testing.T) { //nolint:gocyclo
So(cveSummary.CriticalCount, ShouldEqual, 0) So(cveSummary.CriticalCount, ShouldEqual, 0)
So(cveSummary.MaxSeverity, ShouldEqual, "") So(cveSummary.MaxSeverity, ShouldEqual, "")
cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", pageInput) cveList, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, repo1, "0.1.0", "", "", pageInput)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
So(cveList, ShouldBeEmpty) So(cveList, ShouldBeEmpty)
So(pageInfo.ItemCount, ShouldEqual, 0) So(pageInfo.ItemCount, ShouldEqual, 0)

View file

@ -1,9 +1,11 @@
package model package model
import ( import (
"strings"
"time" "time"
godigest "github.com/opencontainers/go-digest" godigest "github.com/opencontainers/go-digest"
"golang.org/x/exp/slices"
) )
type ImageCVESummary struct { type ImageCVESummary struct {
@ -26,6 +28,21 @@ type CVE struct {
PackageList []Package `json:"PackageList"` PackageList []Package `json:"PackageList"`
} }
func (cve *CVE) ContainsStr(str string) bool {
str = strings.ToUpper(str)
return strings.Contains(strings.ToUpper(cve.Title), str) ||
strings.Contains(strings.ToUpper(cve.ID), str) ||
strings.Contains(strings.ToUpper(cve.Severity), str) ||
strings.Contains(strings.ToUpper(cve.Reference), str) ||
strings.Contains(strings.ToUpper(cve.Description), str) ||
slices.ContainsFunc(cve.PackageList, func(pack Package) bool {
return strings.Contains(strings.ToUpper(pack.Name), str) ||
strings.Contains(strings.ToUpper(pack.FixedVersion), str) ||
strings.Contains(strings.ToUpper(pack.InstalledVersion), str)
})
}
//nolint:tagliatelle // graphQL schema //nolint:tagliatelle // graphQL schema
type Package struct { type Package struct {
Name string `json:"Name"` Name string `json:"Name"`

View file

@ -140,7 +140,7 @@ func TestCVEPagination(t *testing.T) {
Convey("Page", func() { Convey("Page", func() {
Convey("defaults", func() { Convey("defaults", func() {
// By default expect unlimitted results sorted by severity // By default expect unlimitted results sorted by severity
cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{}) cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", "", cvemodel.PageInput{})
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 5) So(len(cves), ShouldEqual, 5)
So(pageInfo.ItemCount, ShouldEqual, 5) So(pageInfo.ItemCount, ShouldEqual, 5)
@ -158,7 +158,7 @@ func TestCVEPagination(t *testing.T) {
previousSeverity = severityToInt[cve.Severity] previousSeverity = severityToInt[cve.Severity]
} }
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cvemodel.PageInput{}) cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", "", cvemodel.PageInput{})
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 30) So(len(cves), ShouldEqual, 30)
So(pageInfo.ItemCount, ShouldEqual, 30) So(pageInfo.ItemCount, ShouldEqual, 30)
@ -183,7 +183,7 @@ func TestCVEPagination(t *testing.T) {
cveIds = append(cveIds, fmt.Sprintf("CVE%d", i)) cveIds = append(cveIds, fmt.Sprintf("CVE%d", i))
} }
cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", "",
cvemodel.PageInput{SortBy: cveinfo.AlphabeticAsc}) cvemodel.PageInput{SortBy: cveinfo.AlphabeticAsc})
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 5) So(len(cves), ShouldEqual, 5)
@ -201,7 +201,7 @@ func TestCVEPagination(t *testing.T) {
} }
sort.Strings(cveIds) sort.Strings(cveIds)
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", "",
cvemodel.PageInput{SortBy: cveinfo.AlphabeticAsc}) cvemodel.PageInput{SortBy: cveinfo.AlphabeticAsc})
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 30) So(len(cves), ShouldEqual, 30)
@ -219,7 +219,7 @@ func TestCVEPagination(t *testing.T) {
} }
sort.Sort(sort.Reverse(sort.StringSlice(cveIds))) sort.Sort(sort.Reverse(sort.StringSlice(cveIds)))
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", "",
cvemodel.PageInput{SortBy: cveinfo.AlphabeticDsc}) cvemodel.PageInput{SortBy: cveinfo.AlphabeticDsc})
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 30) So(len(cves), ShouldEqual, 30)
@ -236,7 +236,7 @@ func TestCVEPagination(t *testing.T) {
So(cve.ID, ShouldEqual, cveIds[i]) So(cve.ID, ShouldEqual, cveIds[i])
} }
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", "",
cvemodel.PageInput{SortBy: cveinfo.SeverityDsc}) cvemodel.PageInput{SortBy: cveinfo.SeverityDsc})
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(cves), ShouldEqual, 30) So(len(cves), ShouldEqual, 30)
@ -262,7 +262,7 @@ func TestCVEPagination(t *testing.T) {
cveIds = append(cveIds, fmt.Sprintf("CVE%d", i)) cveIds = append(cveIds, fmt.Sprintf("CVE%d", i))
} }
cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", "", cvemodel.PageInput{
Limit: 3, Limit: 3,
Offset: 1, Offset: 1,
SortBy: cveinfo.AlphabeticAsc, SortBy: cveinfo.AlphabeticAsc,
@ -283,7 +283,7 @@ func TestCVEPagination(t *testing.T) {
So(cveSummary.CriticalCount, ShouldEqual, 1) So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL") So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", "", cvemodel.PageInput{
Limit: 2, Limit: 2,
Offset: 1, Offset: 1,
SortBy: cveinfo.AlphabeticDsc, SortBy: cveinfo.AlphabeticDsc,
@ -303,7 +303,7 @@ func TestCVEPagination(t *testing.T) {
So(cveSummary.CriticalCount, ShouldEqual, 1) So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL") So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", "", cvemodel.PageInput{
Limit: 3, Limit: 3,
Offset: 1, Offset: 1,
SortBy: cveinfo.SeverityDsc, SortBy: cveinfo.SeverityDsc,
@ -327,7 +327,7 @@ func TestCVEPagination(t *testing.T) {
} }
sort.Strings(cveIds) sort.Strings(cveIds)
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", cvemodel.PageInput{ cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "1.0.0", "", "", cvemodel.PageInput{
Limit: 5, Limit: 5,
Offset: 20, Offset: 20,
SortBy: cveinfo.AlphabeticAsc, SortBy: cveinfo.AlphabeticAsc,
@ -350,7 +350,7 @@ func TestCVEPagination(t *testing.T) {
}) })
Convey("limit > len(cves)", func() { Convey("limit > len(cves)", func() {
cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ cves, cveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", "", cvemodel.PageInput{
Limit: 6, Limit: 6,
Offset: 3, Offset: 3,
SortBy: cveinfo.AlphabeticAsc, SortBy: cveinfo.AlphabeticAsc,
@ -370,7 +370,7 @@ func TestCVEPagination(t *testing.T) {
So(cveSummary.CriticalCount, ShouldEqual, 1) So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL") So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", "", cvemodel.PageInput{
Limit: 6, Limit: 6,
Offset: 3, Offset: 3,
SortBy: cveinfo.AlphabeticDsc, SortBy: cveinfo.AlphabeticDsc,
@ -390,7 +390,7 @@ func TestCVEPagination(t *testing.T) {
So(cveSummary.CriticalCount, ShouldEqual, 1) So(cveSummary.CriticalCount, ShouldEqual, 1)
So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL") So(cveSummary.MaxSeverity, ShouldEqual, "CRITICAL")
cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", cvemodel.PageInput{ cves, cveSummary, pageInfo, err = cveInfo.GetCVEListForImage(ctx, "repo1", "0.1.0", "", "", cvemodel.PageInput{
Limit: 6, Limit: 6,
Offset: 3, Offset: 3,
SortBy: cveinfo.SeverityDsc, SortBy: cveinfo.SeverityDsc,

View file

@ -170,7 +170,7 @@ type ComplexityRoot struct {
Query struct { Query struct {
BaseImageList func(childComplexity int, image string, digest *string, requestedPage *PageInput) int BaseImageList func(childComplexity int, image string, digest *string, requestedPage *PageInput) int
BookmarkedRepos func(childComplexity int, requestedPage *PageInput) int BookmarkedRepos func(childComplexity int, requestedPage *PageInput) int
CVEListForImage func(childComplexity int, image string, requestedPage *PageInput, searchedCve *string) int CVEListForImage func(childComplexity int, image string, requestedPage *PageInput, searchedCve *string, excludedCve *string) int
DerivedImageList func(childComplexity int, image string, digest *string, requestedPage *PageInput) int DerivedImageList func(childComplexity int, image string, digest *string, requestedPage *PageInput) int
ExpandedRepoInfo func(childComplexity int, repo string) int ExpandedRepoInfo func(childComplexity int, repo string) int
GlobalSearch func(childComplexity int, query string, filter *Filter, requestedPage *PageInput) int GlobalSearch func(childComplexity int, query string, filter *Filter, requestedPage *PageInput) int
@ -219,7 +219,7 @@ type ComplexityRoot struct {
} }
type QueryResolver interface { type QueryResolver interface {
CVEListForImage(ctx context.Context, image string, requestedPage *PageInput, searchedCve *string) (*CVEResultForImage, error) CVEListForImage(ctx context.Context, image string, requestedPage *PageInput, searchedCve *string, excludedCve *string) (*CVEResultForImage, error)
ImageListForCve(ctx context.Context, id string, filter *Filter, requestedPage *PageInput) (*PaginatedImagesResult, error) ImageListForCve(ctx context.Context, id string, filter *Filter, requestedPage *PageInput) (*PaginatedImagesResult, error)
ImageListWithCVEFixed(ctx context.Context, id string, image string, filter *Filter, requestedPage *PageInput) (*PaginatedImagesResult, error) ImageListWithCVEFixed(ctx context.Context, id string, image string, filter *Filter, requestedPage *PageInput) (*PaginatedImagesResult, error)
ImageListForDigest(ctx context.Context, id string, requestedPage *PageInput) (*PaginatedImagesResult, error) ImageListForDigest(ctx context.Context, id string, requestedPage *PageInput) (*PaginatedImagesResult, error)
@ -827,7 +827,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false return 0, false
} }
return e.complexity.Query.CVEListForImage(childComplexity, args["image"].(string), args["requestedPage"].(*PageInput), args["searchedCVE"].(*string)), true return e.complexity.Query.CVEListForImage(childComplexity, args["image"].(string), args["requestedPage"].(*PageInput), args["searchedCVE"].(*string), args["excludedCVE"].(*string)), true
case "Query.DerivedImageList": case "Query.DerivedImageList":
if e.complexity.Query.DerivedImageList == nil { if e.complexity.Query.DerivedImageList == nil {
@ -1837,6 +1837,8 @@ type Query {
requestedPage: PageInput requestedPage: PageInput
"Search term for specific CVE by title/id" "Search term for specific CVE by title/id"
searchedCVE: String searchedCVE: String
"Search term that must not be present in the returned results"
excludedCVE: String
): CVEResultForImage! ): CVEResultForImage!
""" """
@ -2064,6 +2066,15 @@ func (ec *executionContext) field_Query_CVEListForImage_args(ctx context.Context
} }
} }
args["searchedCVE"] = arg2 args["searchedCVE"] = arg2
var arg3 *string
if tmp, ok := rawArgs["excludedCVE"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("excludedCVE"))
arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
if err != nil {
return nil, err
}
}
args["excludedCVE"] = arg3
return args, nil return args, nil
} }
@ -5925,7 +5936,7 @@ func (ec *executionContext) _Query_CVEListForImage(ctx context.Context, field gr
}() }()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().CVEListForImage(rctx, fc.Args["image"].(string), fc.Args["requestedPage"].(*PageInput), fc.Args["searchedCVE"].(*string)) return ec.resolvers.Query().CVEListForImage(rctx, fc.Args["image"].(string), fc.Args["requestedPage"].(*PageInput), fc.Args["searchedCVE"].(*string), fc.Args["excludedCVE"].(*string))
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)

View file

@ -196,6 +196,7 @@ func getCVEListForImage(
cveInfo cveinfo.CveInfo, cveInfo cveinfo.CveInfo,
requestedPage *gql_generated.PageInput, requestedPage *gql_generated.PageInput,
searchedCVE string, searchedCVE string,
excludedCVE string,
log log.Logger, //nolint:unparam // may be used by devs for debugging log log.Logger, //nolint:unparam // may be used by devs for debugging
) (*gql_generated.CVEResultForImage, error) { ) (*gql_generated.CVEResultForImage, error) {
if requestedPage == nil { if requestedPage == nil {
@ -217,7 +218,7 @@ func getCVEListForImage(
} }
cveList, imageCveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo, ref, cveList, imageCveSummary, pageInfo, err := cveInfo.GetCVEListForImage(ctx, repo, ref,
searchedCVE, pageInput) searchedCVE, excludedCVE, pageInput)
if err != nil { if err != nil {
return &gql_generated.CVEResultForImage{}, err return &gql_generated.CVEResultForImage{}, err
} }

View file

@ -1133,10 +1133,10 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
dig := godigest.FromString("dig") dig := godigest.FromString("dig")
repoWithDigestRef := fmt.Sprintf("repo@%s", dig) repoWithDigestRef := fmt.Sprintf("repo@%s", dig)
_, err := getCVEListForImage(responseContext, repoWithDigestRef, cveInfo, pageInput, "", log) _, err := getCVEListForImage(responseContext, repoWithDigestRef, cveInfo, pageInput, "", "", log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
cveResult, err := getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "", log) cveResult, err := getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "", "", log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.0.0") So(*cveResult.Tag, ShouldEqual, "1.0.0")
@ -1148,7 +1148,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
} }
// test searching CVE by id in results // test searching CVE by id in results
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE3", log) cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE3", "", log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.0.0") So(*cveResult.Tag, ShouldEqual, "1.0.0")
@ -1160,13 +1160,13 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
} }
// test searching CVE by id in results - no matches // test searching CVE by id in results - no matches
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE100", log) cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE100", "", log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.0.0") So(*cveResult.Tag, ShouldEqual, "1.0.0")
So(len(cveResult.CVEList), ShouldEqual, 0) So(len(cveResult.CVEList), ShouldEqual, 0)
// test searching CVE by id in results - partial name // test searching CVE by id in results - partial name
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "VE3", log) cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "VE3", "", log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.0.0") So(*cveResult.Tag, ShouldEqual, "1.0.0")
@ -1178,7 +1178,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
} }
// test searching CVE by title in results // test searching CVE by title in results
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Title CVE", log) cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Title CVE", "", log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.0.0") So(*cveResult.Tag, ShouldEqual, "1.0.0")
@ -1189,7 +1189,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
So(expectedCves, ShouldContain, *cve.ID) So(expectedCves, ShouldContain, *cve.ID)
} }
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.1", cveInfo, pageInput, "", log) cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.1", cveInfo, pageInput, "", "", log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.0.1") So(*cveResult.Tag, ShouldEqual, "1.0.1")
@ -1200,7 +1200,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
So(expectedCves, ShouldContain, *cve.ID) So(expectedCves, ShouldContain, *cve.ID)
} }
cveResult, err = getCVEListForImage(responseContext, "repo1:1.1.0", cveInfo, pageInput, "", log) cveResult, err = getCVEListForImage(responseContext, "repo1:1.1.0", cveInfo, pageInput, "", "", log)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.1.0") So(*cveResult.Tag, ShouldEqual, "1.1.0")
@ -1212,6 +1212,39 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
} }
}) })
Convey("Unpaginated request to get all CVEs in an image excluding some", func() {
pageInput := &gql_generated.PageInput{
SortBy: ref(gql_generated.SortCriteriaAlphabeticAsc),
}
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
graphql.DefaultRecover)
cveResult, err := getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Title CVE",
"Title CVE2", log)
So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.0.0")
expectedCves := []string{"CVE1", "CVE3"}
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
for _, cve := range cveResult.CVEList {
So(expectedCves, ShouldContain, *cve.ID)
}
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Description",
"Description CVE2", log)
So(err, ShouldBeNil)
So(*cveResult.Tag, ShouldEqual, "1.0.0")
expectedCves = []string{"CVE1", "CVE3", "CVE34"}
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
for _, cve := range cveResult.CVEList {
So(expectedCves, ShouldContain, *cve.ID)
}
})
Convey("paginated fail", func() { Convey("paginated fail", func() {
pageInput := &gql_generated.PageInput{ pageInput := &gql_generated.PageInput{
Limit: ref(-1), Limit: ref(-1),
@ -1220,7 +1253,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter, responseContext := graphql.WithResponseContext(ctx, graphql.DefaultErrorPresenter,
graphql.DefaultRecover) graphql.DefaultRecover)
_, err = getCVEListForImage(responseContext, "repo1:1.1.0", cveInfo, pageInput, "", log) _, err = getCVEListForImage(responseContext, "repo1:1.1.0", cveInfo, pageInput, "", "", log)
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
}) })
}) })

View file

@ -635,6 +635,8 @@ type Query {
requestedPage: PageInput requestedPage: PageInput
"Search term for specific CVE by title/id" "Search term for specific CVE by title/id"
searchedCVE: String searchedCVE: String
"Search term that must not be present in the returned results"
excludedCVE: String
): CVEResultForImage! ): CVEResultForImage!
""" """

View file

@ -15,12 +15,12 @@ import (
) )
// CVEListForImage is the resolver for the CVEListForImage field. // CVEListForImage is the resolver for the CVEListForImage field.
func (r *queryResolver) CVEListForImage(ctx context.Context, image string, requestedPage *gql_generated.PageInput, searchedCve *string) (*gql_generated.CVEResultForImage, error) { func (r *queryResolver) CVEListForImage(ctx context.Context, image string, requestedPage *gql_generated.PageInput, searchedCve *string, excludedCve *string) (*gql_generated.CVEResultForImage, error) {
if r.cveInfo == nil { if r.cveInfo == nil {
return &gql_generated.CVEResultForImage{}, zerr.ErrCVESearchDisabled return &gql_generated.CVEResultForImage{}, zerr.ErrCVESearchDisabled
} }
return getCVEListForImage(ctx, image, r.cveInfo, requestedPage, deref(searchedCve, ""), r.log) return getCVEListForImage(ctx, image, r.cveInfo, requestedPage, deref(searchedCve, ""), deref(excludedCve, ""), r.log)
} }
// ImageListForCve is the resolver for the ImageListForCVE field. // ImageListForCve is the resolver for the ImageListForCVE field.

View file

@ -10,7 +10,7 @@ import (
type CveInfoMock struct { type CveInfoMock struct {
GetImageListForCVEFn func(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error) GetImageListForCVEFn func(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error)
GetImageListWithCVEFixedFn func(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error) GetImageListWithCVEFixedFn func(ctx context.Context, repo, cveID string) ([]cvemodel.TagInfo, error)
GetCVEListForImageFn func(ctx context.Context, repo string, reference string, searchedCVE string, GetCVEListForImageFn func(ctx context.Context, repo, reference, searchedCVE, excludedCVE string,
pageInput cvemodel.PageInput) ([]cvemodel.CVE, cvemodel.ImageCVESummary, common.PageInfo, error) pageInput cvemodel.PageInput) ([]cvemodel.CVE, cvemodel.ImageCVESummary, common.PageInfo, error)
GetCVESummaryForImageMediaFn func(ctx context.Context, repo string, digest, mediaType string, GetCVESummaryForImageMediaFn func(ctx context.Context, repo string, digest, mediaType string,
) (cvemodel.ImageCVESummary, error) ) (cvemodel.ImageCVESummary, error)
@ -34,7 +34,7 @@ func (cveInfo CveInfoMock) GetImageListWithCVEFixed(ctx context.Context, repo, c
} }
func (cveInfo CveInfoMock) GetCVEListForImage(ctx context.Context, repo string, reference string, func (cveInfo CveInfoMock) GetCVEListForImage(ctx context.Context, repo string, reference string,
searchedCVE string, pageInput cvemodel.PageInput, searchedCVE string, excludedCVE string, pageInput cvemodel.PageInput,
) ( ) (
[]cvemodel.CVE, []cvemodel.CVE,
cvemodel.ImageCVESummary, cvemodel.ImageCVESummary,
@ -42,7 +42,7 @@ func (cveInfo CveInfoMock) GetCVEListForImage(ctx context.Context, repo string,
error, error,
) { ) {
if cveInfo.GetCVEListForImageFn != nil { if cveInfo.GetCVEListForImageFn != nil {
return cveInfo.GetCVEListForImageFn(ctx, repo, reference, searchedCVE, pageInput) return cveInfo.GetCVEListForImageFn(ctx, repo, reference, searchedCVE, excludedCVE, pageInput)
} }
return []cvemodel.CVE{}, cvemodel.ImageCVESummary{}, common.PageInfo{}, nil return []cvemodel.CVE{}, cvemodel.ImageCVESummary{}, common.PageInfo{}, nil