mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 20:38:08 +08: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:
committed by
GitHub
parent
4d0bbf1e00
commit
eea6f3f85a
@@ -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)
|
||||
|
||||
+8
-4
@@ -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
|
||||
}
|
||||
|
||||
+8
-8
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user