mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
fix(cve): Search by CVE title/id (full or partial) when listing an image's CVEs (#1264)
Signed-off-by: Ana-Roberta Lisca <ana.kagome@yahoo.com>
This commit is contained in:
parent
4d0bbf1e00
commit
eea6f3f85a
14 changed files with 212 additions and 64 deletions
|
@ -117,6 +117,7 @@ func NewCveCommand(searchService SearchService) *cobra.Command {
|
|||
func setupCveFlags(cveCmd *cobra.Command, variables cveFlagVariables) {
|
||||
variables.searchCveParams["imageName"] = cveCmd.Flags().StringP("image", "I", "", "List CVEs by IMAGENAME[:TAG]")
|
||||
variables.searchCveParams["cveID"] = cveCmd.Flags().StringP("cve-id", "i", "", "List images affected by a CVE")
|
||||
variables.searchCveParams["searchedCVE"] = cveCmd.Flags().StringP("search", "s", "", "Search specific CVEs by name/id")
|
||||
|
||||
cveCmd.Flags().StringVar(variables.servURL, "url", "", "Specify zot server URL if config-name is not mentioned")
|
||||
cveCmd.Flags().StringVarP(variables.user, "user", "u", "", `User Credentials of `+
|
||||
|
|
|
@ -604,6 +604,61 @@ func TestServerCVEResponse(t *testing.T) {
|
|||
So(str, ShouldContainSubstring, "CVE")
|
||||
})
|
||||
|
||||
Convey("Test CVE by image name - GQL - search CVE by title in results", t, func() {
|
||||
args := []string{"cvetest", "--image", "zot-cve-test:0.0.1", "--search", "CVE-C1"}
|
||||
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
|
||||
defer os.Remove(configPath)
|
||||
cveCmd := NewCveCommand(new(searchService))
|
||||
buff := bytes.NewBufferString("")
|
||||
cveCmd.SetOut(buff)
|
||||
cveCmd.SetErr(buff)
|
||||
cveCmd.SetArgs(args)
|
||||
err = cveCmd.Execute()
|
||||
space := regexp.MustCompile(`\s+`)
|
||||
str := space.ReplaceAllString(buff.String(), " ")
|
||||
str = strings.TrimSpace(str)
|
||||
So(err, ShouldBeNil)
|
||||
So(str, ShouldContainSubstring, "ID SEVERITY TITLE")
|
||||
So(str, ShouldContainSubstring, "CVE-C1")
|
||||
So(str, ShouldNotContainSubstring, "CVE-2")
|
||||
})
|
||||
|
||||
Convey("Test CVE by image name - GQL - search CVE by id in results", t, func() {
|
||||
args := []string{"cvetest", "--image", "zot-cve-test:0.0.1", "--search", "CVE-2"}
|
||||
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
|
||||
defer os.Remove(configPath)
|
||||
cveCmd := NewCveCommand(new(searchService))
|
||||
buff := bytes.NewBufferString("")
|
||||
cveCmd.SetOut(buff)
|
||||
cveCmd.SetErr(buff)
|
||||
cveCmd.SetArgs(args)
|
||||
err = cveCmd.Execute()
|
||||
space := regexp.MustCompile(`\s+`)
|
||||
str := space.ReplaceAllString(buff.String(), " ")
|
||||
str = strings.TrimSpace(str)
|
||||
So(err, ShouldBeNil)
|
||||
So(str, ShouldContainSubstring, "ID SEVERITY TITLE")
|
||||
So(str, ShouldContainSubstring, "CVE-2")
|
||||
So(str, ShouldNotContainSubstring, "CVE-1")
|
||||
})
|
||||
|
||||
Convey("Test CVE by image name - GQL - search nonexistent CVE", t, func() {
|
||||
args := []string{"cvetest", "--image", "zot-cve-test:0.0.1", "--search", "CVE-100"}
|
||||
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
|
||||
defer os.Remove(configPath)
|
||||
cveCmd := NewCveCommand(new(searchService))
|
||||
buff := bytes.NewBufferString("")
|
||||
cveCmd.SetOut(buff)
|
||||
cveCmd.SetErr(buff)
|
||||
cveCmd.SetArgs(args)
|
||||
err = cveCmd.Execute()
|
||||
space := regexp.MustCompile(`\s+`)
|
||||
str := space.ReplaceAllString(buff.String(), " ")
|
||||
str = strings.TrimSpace(str)
|
||||
So(err, ShouldBeNil)
|
||||
So(str, ShouldContainSubstring, "No CVEs found for image")
|
||||
})
|
||||
|
||||
Convey("Test CVE by image name - GQL - invalid image", t, func() {
|
||||
args := []string{"cvetest", "--image", "invalid:0.0.1"}
|
||||
configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, url))
|
||||
|
|
|
@ -1691,7 +1691,7 @@ func (service mockService) getFixedTagsForCVEGQL(ctx context.Context, config sea
|
|||
}
|
||||
|
||||
func (service mockService) getCveByImageGQL(ctx context.Context, config searchConfig, username, password,
|
||||
imageName string,
|
||||
imageName, searchedCVE string,
|
||||
) (*cveResult, error) {
|
||||
cveRes := &cveResult{}
|
||||
cveRes.Data = cveData{
|
||||
|
@ -1797,7 +1797,7 @@ func (service mockService) getImageByName(ctx context.Context, config searchConf
|
|||
}
|
||||
|
||||
func (service mockService) getCveByImage(ctx context.Context, config searchConfig, username, password,
|
||||
imageName string, rch chan stringResult, wtgrp *sync.WaitGroup,
|
||||
imageName, searchedCVE string, rch chan stringResult, wtgrp *sync.WaitGroup,
|
||||
) {
|
||||
defer wtgrp.Done()
|
||||
defer close(rch)
|
||||
|
|
|
@ -302,7 +302,8 @@ func (search imagesByDigestSearcherGQL) search(config searchConfig) (bool, error
|
|||
type cveByImageSearcher struct{}
|
||||
|
||||
func (search cveByImageSearcher) search(config searchConfig) (bool, error) {
|
||||
if !canSearch(config.params, newSet("imageName")) || *config.fixedFlag {
|
||||
if (!canSearch(config.params, newSet("imageName")) &&
|
||||
!canSearch(config.params, newSet("imageName", "searchedCVE"))) || *config.fixedFlag {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
@ -318,7 +319,8 @@ func (search cveByImageSearcher) search(config searchConfig) (bool, error) {
|
|||
|
||||
wg.Add(1)
|
||||
|
||||
go config.searchService.getCveByImage(ctx, config, username, password, *config.params["imageName"], strErr, &wg)
|
||||
go config.searchService.getCveByImage(ctx, config, username, password, *config.params["imageName"],
|
||||
*config.params["searchedCVE"], strErr, &wg)
|
||||
wg.Add(1)
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
|
@ -337,7 +339,8 @@ func (search cveByImageSearcher) search(config searchConfig) (bool, error) {
|
|||
type cveByImageSearcherGQL struct{}
|
||||
|
||||
func (search cveByImageSearcherGQL) search(config searchConfig) (bool, error) {
|
||||
if !canSearch(config.params, newSet("imageName")) || *config.fixedFlag {
|
||||
if (!canSearch(config.params, newSet("imageName")) &&
|
||||
!canSearch(config.params, newSet("imageName", "searchedCVE"))) || *config.fixedFlag {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
@ -352,7 +355,8 @@ func (search cveByImageSearcherGQL) search(config searchConfig) (bool, error) {
|
|||
|
||||
defer cancel()
|
||||
|
||||
cveList, err := config.searchService.getCveByImageGQL(ctx, config, username, password, *config.params["imageName"])
|
||||
cveList, err := config.searchService.getCveByImageGQL(ctx, config, username, password,
|
||||
*config.params["imageName"], *config.params["searchedCVE"])
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ type SearchService interface { //nolint:interfacebloat
|
|||
getImagesByDigestGQL(ctx context.Context, config searchConfig, username, password string,
|
||||
digest string) (*imageListStructForDigestGQL, error)
|
||||
getCveByImageGQL(ctx context.Context, config searchConfig, username, password,
|
||||
imageName string) (*cveResult, error)
|
||||
imageName string, searchedCVE string) (*cveResult, error)
|
||||
getImagesByCveIDGQL(ctx context.Context, config searchConfig, username, password string,
|
||||
digest string) (*imagesForCve, error)
|
||||
getTagsForCVEGQL(ctx context.Context, config searchConfig, username, password, imageName,
|
||||
|
@ -43,7 +43,7 @@ type SearchService interface { //nolint:interfacebloat
|
|||
|
||||
getAllImages(ctx context.Context, config searchConfig, username, password string,
|
||||
channel chan stringResult, wtgrp *sync.WaitGroup)
|
||||
getCveByImage(ctx context.Context, config searchConfig, username, password, imageName string,
|
||||
getCveByImage(ctx context.Context, config searchConfig, username, password, imageName, searchedCVE string,
|
||||
channel chan stringResult, wtgrp *sync.WaitGroup)
|
||||
getImagesByCveID(ctx context.Context, config searchConfig, username, password, cvid string,
|
||||
channel chan stringResult, wtgrp *sync.WaitGroup)
|
||||
|
@ -226,11 +226,11 @@ func (service searchService) getImagesByCveIDGQL(ctx context.Context, config sea
|
|||
}
|
||||
|
||||
func (service searchService) getCveByImageGQL(ctx context.Context, config searchConfig, username, password,
|
||||
imageName string,
|
||||
imageName, searchedCVE string,
|
||||
) (*cveResult, error) {
|
||||
query := fmt.Sprintf(`{ CVEListForImage (image:"%s")`+
|
||||
query := fmt.Sprintf(`{ CVEListForImage (image:"%s", searchedCVE:"%s")`+
|
||||
` { Tag CVEList { Id Title Severity Description `+
|
||||
`PackageList {Name InstalledVersion FixedVersion}} } }`, imageName)
|
||||
`PackageList {Name InstalledVersion FixedVersion}} } }`, imageName, searchedCVE)
|
||||
result := &cveResult{}
|
||||
|
||||
err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
|
||||
|
@ -618,14 +618,14 @@ func (service searchService) getImageByNameAndCVEID(ctx context.Context, config
|
|||
}
|
||||
|
||||
func (service searchService) getCveByImage(ctx context.Context, config searchConfig, username, password,
|
||||
imageName string, rch chan stringResult, wtgrp *sync.WaitGroup,
|
||||
imageName, searchedCVE string, rch chan stringResult, wtgrp *sync.WaitGroup,
|
||||
) {
|
||||
defer wtgrp.Done()
|
||||
defer close(rch)
|
||||
|
||||
query := fmt.Sprintf(`{ CVEListForImage (image:"%s")`+
|
||||
query := fmt.Sprintf(`{ CVEListForImage (image:"%s", searchedCVE:"%s")`+
|
||||
` { Tag CVEList { Id Title Severity Description `+
|
||||
`PackageList {Name InstalledVersion FixedVersion}} } }`, imageName)
|
||||
`PackageList {Name InstalledVersion FixedVersion}} } }`, imageName, searchedCVE)
|
||||
result := &cveResult{}
|
||||
|
||||
err := service.makeGraphQLQuery(ctx, config, username, password, query, result)
|
||||
|
|
|
@ -3,6 +3,7 @@ package cveinfo
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
@ -19,7 +20,7 @@ import (
|
|||
type CveInfo interface {
|
||||
GetImageListForCVE(repo, cveID string) ([]common.TagInfo, error)
|
||||
GetImageListWithCVEFixed(repo, cveID string) ([]common.TagInfo, error)
|
||||
GetCVEListForImage(repo, tag string, pageinput PageInput) ([]cvemodel.CVE, PageInfo, error)
|
||||
GetCVEListForImage(repo, tag string, searchedCVE string, pageinput PageInput) ([]cvemodel.CVE, PageInfo, error)
|
||||
GetCVESummaryForImage(repo, tag string) (ImageCVESummary, error)
|
||||
CompareSeverities(severity1, severity2 string) int
|
||||
UpdateDB() error
|
||||
|
@ -215,7 +216,18 @@ func (cveinfo BaseCveInfo) GetImageListWithCVEFixed(repo, cveID string) ([]commo
|
|||
return fixedTags, nil
|
||||
}
|
||||
|
||||
func (cveinfo BaseCveInfo) GetCVEListForImage(repo, tag string, pageInput PageInput) (
|
||||
func filterCVEList(cveMap map[string]cvemodel.CVE, searchedCVE string, pageFinder *CvePageFinder) {
|
||||
searchedCVE = strings.ToUpper(searchedCVE)
|
||||
|
||||
for _, cve := range cveMap {
|
||||
if strings.Contains(strings.ToUpper(cve.Title), searchedCVE) ||
|
||||
strings.Contains(strings.ToUpper(cve.ID), searchedCVE) {
|
||||
pageFinder.Add(cve)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cveinfo BaseCveInfo) GetCVEListForImage(repo, tag string, searchedCVE string, pageInput PageInput) (
|
||||
[]cvemodel.CVE,
|
||||
PageInfo,
|
||||
error,
|
||||
|
@ -237,9 +249,7 @@ func (cveinfo BaseCveInfo) GetCVEListForImage(repo, tag string, pageInput PageIn
|
|||
return []cvemodel.CVE{}, PageInfo{}, err
|
||||
}
|
||||
|
||||
for _, cve := range cveMap {
|
||||
pageFinder.Add(cve)
|
||||
}
|
||||
filterCVEList(cveMap, searchedCVE, pageFinder)
|
||||
|
||||
cveList, pageInfo := pageFinder.Page()
|
||||
|
||||
|
|
|
@ -1206,14 +1206,14 @@ func TestCVEStruct(t *testing.T) {
|
|||
}
|
||||
|
||||
// Image is found
|
||||
cveList, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", pageInput)
|
||||
cveList, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "", pageInput)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cveList), ShouldEqual, 1)
|
||||
So(cveList[0].ID, ShouldEqual, "CVE1")
|
||||
So(pageInfo.ItemCount, ShouldEqual, 1)
|
||||
So(pageInfo.TotalCount, ShouldEqual, 1)
|
||||
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "", pageInput)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cveList), ShouldEqual, 3)
|
||||
So(cveList[0].ID, ShouldEqual, "CVE2")
|
||||
|
@ -1222,7 +1222,7 @@ func TestCVEStruct(t *testing.T) {
|
|||
So(pageInfo.ItemCount, ShouldEqual, 3)
|
||||
So(pageInfo.TotalCount, ShouldEqual, 3)
|
||||
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.1", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.1", "", pageInput)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cveList), ShouldEqual, 2)
|
||||
So(cveList[0].ID, ShouldEqual, "CVE1")
|
||||
|
@ -1230,42 +1230,42 @@ func TestCVEStruct(t *testing.T) {
|
|||
So(pageInfo.ItemCount, ShouldEqual, 2)
|
||||
So(pageInfo.TotalCount, ShouldEqual, 2)
|
||||
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.1.0", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.1.0", "", pageInput)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cveList), ShouldEqual, 1)
|
||||
So(cveList[0].ID, ShouldEqual, "CVE3")
|
||||
So(pageInfo.ItemCount, ShouldEqual, 1)
|
||||
So(pageInfo.TotalCount, ShouldEqual, 1)
|
||||
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo6", "1.0.0", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo6", "1.0.0", "", pageInput)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cveList), ShouldEqual, 0)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 0)
|
||||
So(pageInfo.TotalCount, ShouldEqual, 0)
|
||||
|
||||
// Image is not scannable
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo2", "1.0.0", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo2", "1.0.0", "", pageInput)
|
||||
So(err, ShouldEqual, zerr.ErrScanNotSupported)
|
||||
So(len(cveList), ShouldEqual, 0)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 0)
|
||||
So(pageInfo.TotalCount, ShouldEqual, 0)
|
||||
|
||||
// Tag is not found
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo3", "1.0.0", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo3", "1.0.0", "", pageInput)
|
||||
So(err, ShouldEqual, zerr.ErrTagMetaNotFound)
|
||||
So(len(cveList), ShouldEqual, 0)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 0)
|
||||
So(pageInfo.TotalCount, ShouldEqual, 0)
|
||||
|
||||
// Manifest is not found
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo5", "nonexitent-manifest", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo5", "nonexitent-manifest", "", pageInput)
|
||||
So(err, ShouldEqual, zerr.ErrManifestDataNotFound)
|
||||
So(len(cveList), ShouldEqual, 0)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 0)
|
||||
So(pageInfo.TotalCount, ShouldEqual, 0)
|
||||
|
||||
// Repo is not found
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo100", "1.0.0", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo100", "1.0.0", "", pageInput)
|
||||
So(err, ShouldEqual, zerr.ErrRepoMetaNotFound)
|
||||
So(len(cveList), ShouldEqual, 0)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 0)
|
||||
|
@ -1385,7 +1385,7 @@ func TestCVEStruct(t *testing.T) {
|
|||
So(cveSummary.Count, ShouldEqual, 0)
|
||||
So(cveSummary.MaxSeverity, ShouldEqual, "")
|
||||
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", pageInput)
|
||||
cveList, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", pageInput)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(cveList, ShouldBeEmpty)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 0)
|
||||
|
|
|
@ -182,7 +182,7 @@ func TestCVEPagination(t *testing.T) {
|
|||
Convey("Page", func() {
|
||||
Convey("defaults", func() {
|
||||
// By default expect unlimitted results sorted by severity
|
||||
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", cveinfo.PageInput{})
|
||||
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{})
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 5)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 5)
|
||||
|
@ -193,7 +193,7 @@ func TestCVEPagination(t *testing.T) {
|
|||
previousSeverity = severityToInt[cve.Severity]
|
||||
}
|
||||
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", cveinfo.PageInput{})
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "", cveinfo.PageInput{})
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 30)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 30)
|
||||
|
@ -211,7 +211,7 @@ func TestCVEPagination(t *testing.T) {
|
|||
cveIds = append(cveIds, fmt.Sprintf("CVE%d", i))
|
||||
}
|
||||
|
||||
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0",
|
||||
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "",
|
||||
cveinfo.PageInput{SortBy: cveinfo.AlphabeticAsc})
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 5)
|
||||
|
@ -222,7 +222,8 @@ func TestCVEPagination(t *testing.T) {
|
|||
}
|
||||
|
||||
sort.Strings(cveIds)
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", cveinfo.PageInput{SortBy: cveinfo.AlphabeticAsc})
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "",
|
||||
cveinfo.PageInput{SortBy: cveinfo.AlphabeticAsc})
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 30)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 30)
|
||||
|
@ -232,7 +233,8 @@ func TestCVEPagination(t *testing.T) {
|
|||
}
|
||||
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(cveIds)))
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", cveinfo.PageInput{SortBy: cveinfo.AlphabeticDsc})
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "",
|
||||
cveinfo.PageInput{SortBy: cveinfo.AlphabeticDsc})
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 30)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 30)
|
||||
|
@ -241,7 +243,8 @@ func TestCVEPagination(t *testing.T) {
|
|||
So(cve.ID, ShouldEqual, cveIds[i])
|
||||
}
|
||||
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", cveinfo.PageInput{SortBy: cveinfo.SeverityDsc})
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "",
|
||||
cveinfo.PageInput{SortBy: cveinfo.SeverityDsc})
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 30)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 30)
|
||||
|
@ -259,11 +262,12 @@ func TestCVEPagination(t *testing.T) {
|
|||
cveIds = append(cveIds, fmt.Sprintf("CVE%d", i))
|
||||
}
|
||||
|
||||
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", cveinfo.PageInput{
|
||||
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
||||
Limit: 3,
|
||||
Offset: 1,
|
||||
SortBy: cveinfo.AlphabeticAsc,
|
||||
})
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 3)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 3)
|
||||
|
@ -272,11 +276,12 @@ func TestCVEPagination(t *testing.T) {
|
|||
So(cves[1].ID, ShouldEqual, "CVE2")
|
||||
So(cves[2].ID, ShouldEqual, "CVE3")
|
||||
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", cveinfo.PageInput{
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
||||
Limit: 2,
|
||||
Offset: 1,
|
||||
SortBy: cveinfo.AlphabeticDsc,
|
||||
})
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 2)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 2)
|
||||
|
@ -284,11 +289,12 @@ func TestCVEPagination(t *testing.T) {
|
|||
So(cves[0].ID, ShouldEqual, "CVE3")
|
||||
So(cves[1].ID, ShouldEqual, "CVE2")
|
||||
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", cveinfo.PageInput{
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
||||
Limit: 3,
|
||||
Offset: 1,
|
||||
SortBy: cveinfo.SeverityDsc,
|
||||
})
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 3)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 3)
|
||||
|
@ -300,11 +306,12 @@ func TestCVEPagination(t *testing.T) {
|
|||
}
|
||||
|
||||
sort.Strings(cveIds)
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", cveinfo.PageInput{
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "1.0.0", "", cveinfo.PageInput{
|
||||
Limit: 5,
|
||||
Offset: 20,
|
||||
SortBy: cveinfo.AlphabeticAsc,
|
||||
})
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 5)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 5)
|
||||
|
@ -315,11 +322,12 @@ func TestCVEPagination(t *testing.T) {
|
|||
})
|
||||
|
||||
Convey("limit > len(cves)", func() {
|
||||
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", cveinfo.PageInput{
|
||||
cves, pageInfo, err := cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
||||
Limit: 6,
|
||||
Offset: 3,
|
||||
SortBy: cveinfo.AlphabeticAsc,
|
||||
})
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 2)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 2)
|
||||
|
@ -327,11 +335,12 @@ func TestCVEPagination(t *testing.T) {
|
|||
So(cves[0].ID, ShouldEqual, "CVE3")
|
||||
So(cves[1].ID, ShouldEqual, "CVE4")
|
||||
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", cveinfo.PageInput{
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
||||
Limit: 6,
|
||||
Offset: 3,
|
||||
SortBy: cveinfo.AlphabeticDsc,
|
||||
})
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 2)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 2)
|
||||
|
@ -339,11 +348,12 @@ func TestCVEPagination(t *testing.T) {
|
|||
So(cves[0].ID, ShouldEqual, "CVE1")
|
||||
So(cves[1].ID, ShouldEqual, "CVE0")
|
||||
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", cveinfo.PageInput{
|
||||
cves, pageInfo, err = cveInfo.GetCVEListForImage("repo1", "0.1.0", "", cveinfo.PageInput{
|
||||
Limit: 6,
|
||||
Offset: 3,
|
||||
SortBy: cveinfo.SeverityDsc,
|
||||
})
|
||||
},
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(cves), ShouldEqual, 2)
|
||||
So(pageInfo.ItemCount, ShouldEqual, 2)
|
||||
|
|
|
@ -152,7 +152,7 @@ type ComplexityRoot struct {
|
|||
|
||||
Query struct {
|
||||
BaseImageList func(childComplexity int, image string, digest *string, requestedPage *PageInput) int
|
||||
CVEListForImage func(childComplexity int, image string, requestedPage *PageInput) int
|
||||
CVEListForImage func(childComplexity int, image string, requestedPage *PageInput, searchedCve *string) int
|
||||
DerivedImageList func(childComplexity int, image string, digest *string, requestedPage *PageInput) int
|
||||
ExpandedRepoInfo func(childComplexity int, repo string) int
|
||||
GlobalSearch func(childComplexity int, query string, filter *Filter, requestedPage *PageInput) int
|
||||
|
@ -194,7 +194,7 @@ type ComplexityRoot struct {
|
|||
}
|
||||
|
||||
type QueryResolver interface {
|
||||
CVEListForImage(ctx context.Context, image string, requestedPage *PageInput) (*CVEResultForImage, error)
|
||||
CVEListForImage(ctx context.Context, image string, requestedPage *PageInput, searchedCve *string) (*CVEResultForImage, error)
|
||||
ImageListForCve(ctx context.Context, id string, requestedPage *PageInput) (*PaginatedImagesResult, error)
|
||||
ImageListWithCVEFixed(ctx context.Context, id string, image string, requestedPage *PageInput) (*PaginatedImagesResult, error)
|
||||
ImageListForDigest(ctx context.Context, id string, requestedPage *PageInput) (*PaginatedImagesResult, error)
|
||||
|
@ -686,7 +686,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.CVEListForImage(childComplexity, args["image"].(string), args["requestedPage"].(*PageInput)), true
|
||||
return e.complexity.Query.CVEListForImage(childComplexity, args["image"].(string), args["requestedPage"].(*PageInput), args["searchedCVE"].(*string)), true
|
||||
|
||||
case "Query.DerivedImageList":
|
||||
if e.complexity.Query.DerivedImageList == nil {
|
||||
|
@ -1541,6 +1541,8 @@ type Query {
|
|||
image: String!,
|
||||
"Sets the parameters of the requested page"
|
||||
requestedPage: PageInput
|
||||
"Search term for specific CVE by title/id"
|
||||
searchedCVE: String
|
||||
): CVEResultForImage!
|
||||
|
||||
"""
|
||||
|
@ -1724,6 +1726,15 @@ func (ec *executionContext) field_Query_CVEListForImage_args(ctx context.Context
|
|||
}
|
||||
}
|
||||
args["requestedPage"] = arg1
|
||||
var arg2 *string
|
||||
if tmp, ok := rawArgs["searchedCVE"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("searchedCVE"))
|
||||
arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["searchedCVE"] = arg2
|
||||
return args, nil
|
||||
}
|
||||
|
||||
|
@ -4882,7 +4893,7 @@ func (ec *executionContext) _Query_CVEListForImage(ctx context.Context, field gr
|
|||
}()
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().CVEListForImage(rctx, fc.Args["image"].(string), fc.Args["requestedPage"].(*PageInput))
|
||||
return ec.resolvers.Query().CVEListForImage(rctx, fc.Args["image"].(string), fc.Args["requestedPage"].(*PageInput), fc.Args["searchedCVE"].(*string))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
|
|
|
@ -292,6 +292,7 @@ func getCVEListForImage(
|
|||
image string,
|
||||
cveInfo cveinfo.CveInfo,
|
||||
requestedPage *gql_generated.PageInput,
|
||||
searchedCVE string,
|
||||
log log.Logger, //nolint:unparam // may be used by devs for debugging
|
||||
) (*gql_generated.CVEResultForImage, error) {
|
||||
if requestedPage == nil {
|
||||
|
@ -316,7 +317,7 @@ func getCVEListForImage(
|
|||
return &gql_generated.CVEResultForImage{}, gqlerror.Errorf("reference by digest not supported")
|
||||
}
|
||||
|
||||
cveList, pageInfo, err := cveInfo.GetCVEListForImage(repo, ref, pageInput)
|
||||
cveList, pageInfo, err := cveInfo.GetCVEListForImage(repo, ref, searchedCVE, pageInput)
|
||||
if err != nil {
|
||||
return &gql_generated.CVEResultForImage{}, err
|
||||
}
|
||||
|
|
|
@ -2080,6 +2080,12 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
|
|||
Title: "Title CVE3",
|
||||
Description: "Description CVE3",
|
||||
},
|
||||
"CVE34": {
|
||||
ID: "CVE34",
|
||||
Severity: "LOW",
|
||||
Title: "Title for CVE34",
|
||||
Description: "Description CVE34",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -2140,21 +2146,63 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
|
|||
dig := godigest.FromString("dig")
|
||||
repoWithDigestRef := fmt.Sprintf("repo@%s", dig)
|
||||
|
||||
_, err := getCVEListForImage(responseContext, repoWithDigestRef, cveInfo, pageInput, log)
|
||||
_, err := getCVEListForImage(responseContext, repoWithDigestRef, cveInfo, pageInput, "", log)
|
||||
So(err.Error(), ShouldContainSubstring, "reference by digest not supported")
|
||||
|
||||
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(*cveResult.Tag, ShouldEqual, "1.0.0")
|
||||
|
||||
expectedCves := []string{"CVE1", "CVE2", "CVE3"}
|
||||
expectedCves := []string{"CVE1", "CVE2", "CVE3", "CVE34"}
|
||||
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
||||
|
||||
for _, cve := range cveResult.CVEList {
|
||||
So(expectedCves, ShouldContain, *cve.ID)
|
||||
}
|
||||
|
||||
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.1", cveInfo, pageInput, log)
|
||||
// test searching CVE by id in results
|
||||
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE3", log)
|
||||
So(err, ShouldBeNil)
|
||||
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
||||
|
||||
expectedCves = []string{"CVE3", "CVE34"}
|
||||
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
||||
|
||||
for _, cve := range cveResult.CVEList {
|
||||
So(expectedCves, ShouldContain, *cve.ID)
|
||||
}
|
||||
|
||||
// test searching CVE by id in results - no matches
|
||||
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "CVE100", log)
|
||||
So(err, ShouldBeNil)
|
||||
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
||||
So(len(cveResult.CVEList), ShouldEqual, 0)
|
||||
|
||||
// test searching CVE by id in results - partial name
|
||||
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "VE3", log)
|
||||
So(err, ShouldBeNil)
|
||||
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
||||
|
||||
expectedCves = []string{"CVE3", "CVE34"}
|
||||
So(len(cveResult.CVEList), ShouldEqual, len(expectedCves))
|
||||
|
||||
for _, cve := range cveResult.CVEList {
|
||||
So(expectedCves, ShouldContain, *cve.ID)
|
||||
}
|
||||
|
||||
// test searching CVE by title in results
|
||||
cveResult, err = getCVEListForImage(responseContext, "repo1:1.0.0", cveInfo, pageInput, "Title CVE", log)
|
||||
So(err, ShouldBeNil)
|
||||
So(*cveResult.Tag, ShouldEqual, "1.0.0")
|
||||
|
||||
expectedCves = []string{"CVE1", "CVE2", "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.1", cveInfo, pageInput, "", log)
|
||||
So(err, ShouldBeNil)
|
||||
So(*cveResult.Tag, ShouldEqual, "1.0.1")
|
||||
|
||||
|
@ -2165,7 +2213,7 @@ func TestCVEResolvers(t *testing.T) { //nolint:gocyclo
|
|||
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(*cveResult.Tag, ShouldEqual, "1.1.0")
|
||||
|
||||
|
|
|
@ -550,6 +550,8 @@ type Query {
|
|||
image: String!,
|
||||
"Sets the parameters of the requested page"
|
||||
requestedPage: PageInput
|
||||
"Search term for specific CVE by title/id"
|
||||
searchedCVE: String
|
||||
): CVEResultForImage!
|
||||
|
||||
"""
|
||||
|
|
|
@ -14,12 +14,16 @@ import (
|
|||
)
|
||||
|
||||
// CVEListForImage is the resolver for the CVEListForImage field.
|
||||
func (r *queryResolver) CVEListForImage(ctx context.Context, image string, requestedPage *gql_generated.PageInput) (*gql_generated.CVEResultForImage, error) {
|
||||
func (r *queryResolver) CVEListForImage(ctx context.Context, image string, requestedPage *gql_generated.PageInput, searchedCve *string) (*gql_generated.CVEResultForImage, error) {
|
||||
if r.cveInfo == nil {
|
||||
return &gql_generated.CVEResultForImage{}, zerr.ErrCVESearchDisabled
|
||||
}
|
||||
|
||||
return getCVEListForImage(ctx, image, r.cveInfo, requestedPage, r.log)
|
||||
if searchedCve == nil {
|
||||
return getCVEListForImage(ctx, image, r.cveInfo, requestedPage, "", r.log)
|
||||
}
|
||||
|
||||
return getCVEListForImage(ctx, image, r.cveInfo, requestedPage, *searchedCve, r.log)
|
||||
}
|
||||
|
||||
// ImageListForCve is the resolver for the ImageListForCVE field.
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
type CveInfoMock struct {
|
||||
GetImageListForCVEFn func(repo, cveID string) ([]common.TagInfo, error)
|
||||
GetImageListWithCVEFixedFn func(repo, cveID string) ([]common.TagInfo, error)
|
||||
GetCVEListForImageFn func(repo string, reference string, pageInput cveinfo.PageInput,
|
||||
GetCVEListForImageFn func(repo string, reference string, searchedCVE string, pageInput cveinfo.PageInput,
|
||||
) ([]cvemodel.CVE, cveinfo.PageInfo, error)
|
||||
GetCVESummaryForImageFn func(repo string, reference string,
|
||||
) (cveinfo.ImageCVESummary, error)
|
||||
|
@ -33,13 +33,15 @@ func (cveInfo CveInfoMock) GetImageListWithCVEFixed(repo, cveID string) ([]commo
|
|||
return []common.TagInfo{}, nil
|
||||
}
|
||||
|
||||
func (cveInfo CveInfoMock) GetCVEListForImage(repo string, reference string, pageInput cveinfo.PageInput) (
|
||||
func (cveInfo CveInfoMock) GetCVEListForImage(repo string, reference string,
|
||||
searchedCVE string, pageInput cveinfo.PageInput,
|
||||
) (
|
||||
[]cvemodel.CVE,
|
||||
cveinfo.PageInfo,
|
||||
error,
|
||||
) {
|
||||
if cveInfo.GetCVEListForImageFn != nil {
|
||||
return cveInfo.GetCVEListForImageFn(repo, reference, pageInput)
|
||||
return cveInfo.GetCVEListForImageFn(repo, reference, searchedCVE, pageInput)
|
||||
}
|
||||
|
||||
return []cvemodel.CVE{}, cveinfo.PageInfo{}, nil
|
||||
|
|
Loading…
Reference in a new issue