zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/cli/client/cve_cmd_internal_test.go (about)

     1  //go:build search
     2  // +build search
     3  
     4  package client
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"regexp"
    13  	"strconv"
    14  	"strings"
    15  	"testing"
    16  
    17  	. "github.com/smartystreets/goconvey/convey"
    18  
    19  	zerr "zotregistry.io/zot/errors"
    20  	"zotregistry.io/zot/pkg/api"
    21  	"zotregistry.io/zot/pkg/api/config"
    22  	zcommon "zotregistry.io/zot/pkg/common"
    23  	extconf "zotregistry.io/zot/pkg/extensions/config"
    24  	test "zotregistry.io/zot/pkg/test/common"
    25  )
    26  
    27  func TestSearchCVECmd(t *testing.T) {
    28  	port := test.GetFreePort()
    29  	baseURL := test.GetBaseURL(port)
    30  	conf := config.New()
    31  	conf.HTTP.Port = port
    32  	rootDir := t.TempDir()
    33  	conf.Storage.RootDirectory = rootDir
    34  
    35  	defaultVal := true
    36  	conf.Extensions = &extconf.ExtensionConfig{
    37  		Search: &extconf.SearchConfig{
    38  			BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
    39  		},
    40  	}
    41  
    42  	ctlr := api.NewController(conf)
    43  	cm := test.NewControllerManager(ctlr)
    44  
    45  	cm.StartAndWait(port)
    46  	defer cm.StopServer()
    47  
    48  	Convey("Test CVE help", t, func() {
    49  		args := []string{"--help"}
    50  		configPath := makeConfigFile("")
    51  		defer os.Remove(configPath)
    52  		cmd := NewCVECommand(new(mockService))
    53  		buff := bytes.NewBufferString("")
    54  		cmd.SetOut(buff)
    55  		cmd.SetErr(buff)
    56  		cmd.SetArgs(args)
    57  		err := cmd.Execute()
    58  		So(buff.String(), ShouldContainSubstring, "Usage")
    59  		So(err, ShouldBeNil)
    60  	})
    61  
    62  	Convey("Test CVE help - with the shorthand", t, func() {
    63  		args := []string{"-h"}
    64  		configPath := makeConfigFile("")
    65  		defer os.Remove(configPath)
    66  		cmd := NewCVECommand(new(mockService))
    67  		buff := bytes.NewBufferString("")
    68  		cmd.SetOut(buff)
    69  		cmd.SetErr(buff)
    70  		cmd.SetArgs(args)
    71  		err := cmd.Execute()
    72  		So(buff.String(), ShouldContainSubstring, "Usage")
    73  		So(err, ShouldBeNil)
    74  	})
    75  
    76  	Convey("Test CVE no url", t, func() {
    77  		args := []string{"affected", "CVE-cveIdRandom", "--config", "cvetest"}
    78  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
    79  		defer os.Remove(configPath)
    80  		cmd := NewCVECommand(new(mockService))
    81  		buff := bytes.NewBufferString("")
    82  		cmd.SetOut(buff)
    83  		cmd.SetErr(buff)
    84  		cmd.SetArgs(args)
    85  		err := cmd.Execute()
    86  		So(err, ShouldNotBeNil)
    87  		So(errors.Is(err, zerr.ErrNoURLProvided), ShouldBeTrue)
    88  	})
    89  
    90  	Convey("Test CVE invalid url", t, func() {
    91  		args := []string{"list", "dummyImageName:tag", "--url", "invalidUrl"}
    92  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
    93  		defer os.Remove(configPath)
    94  		cmd := NewCVECommand(new(searchService))
    95  		buff := bytes.NewBufferString("")
    96  		cmd.SetOut(buff)
    97  		cmd.SetErr(buff)
    98  		cmd.SetArgs(args)
    99  		err := cmd.Execute()
   100  		So(err, ShouldNotBeNil)
   101  		So(errors.Is(err, zerr.ErrInvalidURL), ShouldBeTrue)
   102  		So(buff.String(), ShouldContainSubstring, "invalid URL format")
   103  	})
   104  
   105  	Convey("Test CVE invalid url port", t, func() {
   106  		args := []string{"list", "dummyImageName:tag", "--url", "http://localhost:99999"}
   107  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   108  		defer os.Remove(configPath)
   109  		cmd := NewCVECommand(new(searchService))
   110  		buff := bytes.NewBufferString("")
   111  		cmd.SetOut(buff)
   112  		cmd.SetErr(buff)
   113  		cmd.SetArgs(args)
   114  		err := cmd.Execute()
   115  		So(err, ShouldNotBeNil)
   116  		So(buff.String(), ShouldContainSubstring, "invalid port")
   117  	})
   118  
   119  	Convey("Test CVE unreachable", t, func() {
   120  		args := []string{"list", "dummyImageName:tag", "--url", "http://localhost:9999"}
   121  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   122  		defer os.Remove(configPath)
   123  		cmd := NewCVECommand(new(searchService))
   124  		buff := bytes.NewBufferString("")
   125  		cmd.SetOut(buff)
   126  		cmd.SetErr(buff)
   127  		cmd.SetArgs(args)
   128  		err := cmd.Execute()
   129  		So(err, ShouldNotBeNil)
   130  	})
   131  
   132  	Convey("Test CVE url from config", t, func() {
   133  		args := []string{"list", "dummyImageName:tag", "--config", "cvetest"}
   134  		configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL))
   135  		defer os.Remove(configPath)
   136  		cmd := NewCVECommand(new(mockService))
   137  		buff := bytes.NewBufferString("")
   138  		cmd.SetOut(buff)
   139  		cmd.SetErr(buff)
   140  		cmd.SetArgs(args)
   141  		err := cmd.Execute()
   142  		space := regexp.MustCompile(`\s+`)
   143  		str := space.ReplaceAllString(buff.String(), " ")
   144  		So(strings.TrimSpace(str), ShouldEqual, "ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE")
   145  		So(err, ShouldBeNil)
   146  	})
   147  
   148  	Convey("Test debug flag", t, func() {
   149  		args := []string{"list", "dummyImageName:tag", "--debug", "--config", "cvetest"}
   150  		configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL))
   151  		defer os.Remove(configPath)
   152  		cmd := NewCVECommand(new(searchService))
   153  		buff := bytes.NewBufferString("")
   154  		cmd.SetOut(buff)
   155  		cmd.SetErr(buff)
   156  		cmd.SetArgs(args)
   157  		err := cmd.Execute()
   158  		space := regexp.MustCompile(`\s+`)
   159  		str := space.ReplaceAllString(buff.String(), " ")
   160  		So(strings.TrimSpace(str), ShouldContainSubstring, "GET")
   161  		So(err, ShouldNotBeNil)
   162  	})
   163  
   164  	Convey("Test CVE by name and CVE ID - long option", t, func() {
   165  		args := []string{"affected", "CVE-CVEID", "--repo", "dummyImageName", "--url", baseURL}
   166  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   167  		defer os.Remove(configPath)
   168  		cveCmd := NewCVECommand(new(mockService))
   169  		buff := bytes.NewBufferString("")
   170  		cveCmd.SetOut(buff)
   171  		cveCmd.SetErr(buff)
   172  		cveCmd.SetArgs(args)
   173  		err := cveCmd.Execute()
   174  		So(err, ShouldBeNil)
   175  		space := regexp.MustCompile(`\s+`)
   176  		str := space.ReplaceAllString(buff.String(), " ")
   177  		So(strings.TrimSpace(str), ShouldEqual,
   178  			"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
   179  	})
   180  
   181  	Convey("Test CVE by name and CVE ID - using shorthand", t, func() {
   182  		args := []string{"affected", "CVE-CVEID", "--repo", "dummyImageName", "--url", baseURL}
   183  		buff := bytes.NewBufferString("")
   184  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   185  		defer os.Remove(configPath)
   186  		cveCmd := NewCVECommand(new(mockService))
   187  		cveCmd.SetOut(buff)
   188  		cveCmd.SetErr(buff)
   189  		cveCmd.SetArgs(args)
   190  		err := cveCmd.Execute()
   191  		So(err, ShouldBeNil)
   192  		space := regexp.MustCompile(`\s+`)
   193  		str := space.ReplaceAllString(buff.String(), " ")
   194  		So(strings.TrimSpace(str), ShouldEqual,
   195  			"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB")
   196  	})
   197  
   198  	Convey("Test CVE by image name - in text format", t, func() {
   199  		args := []string{"list", "dummyImageName:tag", "--url", baseURL}
   200  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   201  		defer os.Remove(configPath)
   202  		cveCmd := NewCVECommand(new(mockService))
   203  		buff := bytes.NewBufferString("")
   204  		cveCmd.SetOut(buff)
   205  		cveCmd.SetErr(buff)
   206  		cveCmd.SetArgs(args)
   207  		err := cveCmd.Execute()
   208  		space := regexp.MustCompile(`\s+`)
   209  		str := space.ReplaceAllString(buff.String(), " ")
   210  		So(strings.TrimSpace(str), ShouldEqual, "ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE")
   211  		So(err, ShouldBeNil)
   212  	})
   213  
   214  	Convey("Test CVE by image name - in json format", t, func() {
   215  		args := []string{"list", "dummyImageName:tag", "--url", baseURL, "-f", "json"}
   216  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   217  		defer os.Remove(configPath)
   218  		cveCmd := NewCVECommand(new(mockService))
   219  		buff := bytes.NewBufferString("")
   220  		cveCmd.SetOut(buff)
   221  		cveCmd.SetErr(buff)
   222  		cveCmd.SetArgs(args)
   223  		err := cveCmd.Execute()
   224  		// Output is supposed to be in json lines format, keep all spaces as is for verification
   225  		So(buff.String(), ShouldEqual, `{"Tag":"dummyImageName:tag","CVEList":`+
   226  			`[{"Id":"dummyCVEID","Severity":"HIGH","Title":"Title of that CVE",`+
   227  			`"Description":"Description of the CVE","PackageList":[{"Name":"packagename",`+
   228  			`"InstalledVersion":"installedver","FixedVersion":"fixedver"}]}]}`+"\n")
   229  		So(err, ShouldBeNil)
   230  	})
   231  
   232  	Convey("Test CVE by image name - in yaml format", t, func() {
   233  		args := []string{"list", "dummyImageName:tag", "--url", baseURL, "-f", "yaml"}
   234  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   235  		defer os.Remove(configPath)
   236  		cveCmd := NewCVECommand(new(mockService))
   237  		buff := bytes.NewBufferString("")
   238  		cveCmd.SetOut(buff)
   239  		cveCmd.SetErr(buff)
   240  		cveCmd.SetArgs(args)
   241  		err := cveCmd.Execute()
   242  		space := regexp.MustCompile(`\s+`)
   243  		str := space.ReplaceAllString(buff.String(), " ")
   244  		So(strings.TrimSpace(str), ShouldEqual, `--- tag: dummyImageName:tag cvelist: - id: dummyCVEID`+
   245  			` severity: HIGH title: Title of that CVE description: Description of the CVE packagelist: `+
   246  			`- name: packagename installedversion: installedver fixedversion: fixedver`)
   247  		So(err, ShouldBeNil)
   248  	})
   249  	Convey("Test CVE by image name - invalid format", t, func() {
   250  		args := []string{"list", "dummyImageName:tag", "--url", baseURL, "-f", "random"}
   251  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   252  		defer os.Remove(configPath)
   253  		cveCmd := NewCVECommand(new(mockService))
   254  		buff := bytes.NewBufferString("")
   255  		cveCmd.SetOut(buff)
   256  		cveCmd.SetErr(buff)
   257  		cveCmd.SetArgs(args)
   258  		err := cveCmd.Execute()
   259  		space := regexp.MustCompile(`\s+`)
   260  		str := space.ReplaceAllString(buff.String(), " ")
   261  		So(err, ShouldNotBeNil)
   262  		So(strings.TrimSpace(str), ShouldContainSubstring, zerr.ErrInvalidOutputFormat.Error())
   263  	})
   264  
   265  	Convey("Test images by CVE ID - positive", t, func() {
   266  		args := []string{"affected", "CVE-CVEID", "--repo", "anImage", "--url", baseURL}
   267  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   268  		defer os.Remove(configPath)
   269  		cveCmd := NewCVECommand(new(mockService))
   270  		buff := bytes.NewBufferString("")
   271  		cveCmd.SetOut(buff)
   272  		cveCmd.SetErr(buff)
   273  		cveCmd.SetArgs(args)
   274  		err := cveCmd.Execute()
   275  		space := regexp.MustCompile(`\s+`)
   276  		str := space.ReplaceAllString(buff.String(), " ")
   277  		So(strings.TrimSpace(str), ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB") //nolint:lll
   278  		So(err, ShouldBeNil)
   279  	})
   280  
   281  	Convey("Test images by CVE ID - positive with retries", t, func() {
   282  		args := []string{"affected", "CVE-CVEID", "--repo", "anImage", "--url", baseURL}
   283  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   284  		defer os.Remove(configPath)
   285  		mockService := mockServiceForRetry{succeedOn: 2} // CVE info will be provided in 2nd attempt
   286  		cveCmd := NewCVECommand(&mockService)
   287  		buff := bytes.NewBufferString("")
   288  		cveCmd.SetOut(buff)
   289  		cveCmd.SetErr(buff)
   290  		cveCmd.SetArgs(args)
   291  		err := cveCmd.Execute()
   292  		space := regexp.MustCompile(`\s+`)
   293  		str := space.ReplaceAllString(buff.String(), " ")
   294  		t.Logf("Output: %s", str)
   295  		So(strings.TrimSpace(str), ShouldContainSubstring,
   296  			"[warning] CVE DB is not ready [1] - retry in "+strconv.Itoa(CveDBRetryInterval)+" seconds")
   297  		So(strings.TrimSpace(str), ShouldContainSubstring,
   298  			"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB")
   299  		So(err, ShouldBeNil)
   300  	})
   301  
   302  	Convey("Test images by CVE ID - failed after retries", t, func() {
   303  		args := []string{"affected", "CVE-CVEID", "--url", baseURL}
   304  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   305  		defer os.Remove(configPath)
   306  		mockService := mockServiceForRetry{succeedOn: -1} // CVE info will be unavailable on all retries
   307  		cveCmd := NewCVECommand(&mockService)
   308  		buff := bytes.NewBufferString("")
   309  		cveCmd.SetOut(buff)
   310  		cveCmd.SetErr(buff)
   311  		cveCmd.SetArgs(args)
   312  		err := cveCmd.Execute()
   313  		space := regexp.MustCompile(`\s+`)
   314  		str := space.ReplaceAllString(buff.String(), " ")
   315  		t.Logf("Output: %s", str)
   316  		So(strings.TrimSpace(str), ShouldContainSubstring,
   317  			"[warning] CVE DB is not ready [1] - retry in "+strconv.Itoa(CveDBRetryInterval)+" seconds")
   318  		So(strings.TrimSpace(str), ShouldNotContainSubstring,
   319  			"REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB")
   320  		So(err, ShouldNotBeNil)
   321  	})
   322  
   323  	Convey("Test images by CVE ID - invalid CVE ID", t, func() {
   324  		args := []string{"affected", "CVE-invalidCVEID", "--config", "cvetest"}
   325  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   326  		defer os.Remove(configPath)
   327  		cveCmd := NewCVECommand(new(mockService))
   328  		buff := bytes.NewBufferString("")
   329  		cveCmd.SetOut(buff)
   330  		cveCmd.SetErr(buff)
   331  		cveCmd.SetArgs(args)
   332  		err := cveCmd.Execute()
   333  		So(err, ShouldNotBeNil)
   334  	})
   335  
   336  	Convey("Test images by CVE ID - invalid url", t, func() {
   337  		args := []string{"affected", "CVE-CVEID", "--url", "invalidURL"}
   338  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   339  		defer os.Remove(configPath)
   340  		cveCmd := NewCVECommand(NewSearchService())
   341  		buff := bytes.NewBufferString("")
   342  		cveCmd.SetOut(buff)
   343  		cveCmd.SetErr(buff)
   344  		cveCmd.SetArgs(args)
   345  		err := cveCmd.Execute()
   346  		So(err, ShouldNotBeNil)
   347  		So(errors.Is(err, zerr.ErrInvalidURL), ShouldBeTrue)
   348  		So(buff.String(), ShouldContainSubstring, "invalid URL format")
   349  	})
   350  
   351  	Convey("Test fixed tags by and image name CVE ID - positive", t, func() {
   352  		args := []string{"fixed", "fixedImage", "CVE-CVEID", "--url", baseURL}
   353  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   354  		defer os.Remove(configPath)
   355  		cveCmd := NewCVECommand(new(mockService))
   356  		buff := bytes.NewBufferString("")
   357  		cveCmd.SetOut(buff)
   358  		cveCmd.SetErr(buff)
   359  		cveCmd.SetArgs(args)
   360  		err := cveCmd.Execute()
   361  		space := regexp.MustCompile(`\s+`)
   362  		str := space.ReplaceAllString(buff.String(), " ")
   363  		So(err, ShouldBeNil)
   364  		So(strings.TrimSpace(str), ShouldEqual, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE fixedImage tag os/arch 6e2f80bf false 123kB") //nolint:lll
   365  	})
   366  
   367  	Convey("Test fixed tags by and image name CVE ID - invalid image name", t, func() {
   368  		args := []string{"affected", "CVE-CVEID", "--image", "invalidImageName", "--config", "cvetest"}
   369  		configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`)
   370  		defer os.Remove(configPath)
   371  		cveCmd := NewCVECommand(NewSearchService())
   372  		buff := bytes.NewBufferString("")
   373  		cveCmd.SetOut(buff)
   374  		cveCmd.SetErr(buff)
   375  		cveCmd.SetArgs(args)
   376  		err := cveCmd.Execute()
   377  		So(err, ShouldNotBeNil)
   378  	})
   379  }
   380  
   381  func TestCVECommandGQL(t *testing.T) {
   382  	port := test.GetFreePort()
   383  	baseURL := test.GetBaseURL(port)
   384  	conf := config.New()
   385  	conf.HTTP.Port = port
   386  
   387  	defaultVal := true
   388  	conf.Extensions = &extconf.ExtensionConfig{
   389  		Search: &extconf.SearchConfig{
   390  			BaseConfig: extconf.BaseConfig{Enable: &defaultVal},
   391  		},
   392  	}
   393  
   394  	ctlr := api.NewController(conf)
   395  	ctlr.Config.Storage.RootDirectory = t.TempDir()
   396  	cm := test.NewControllerManager(ctlr)
   397  
   398  	cm.StartAndWait(conf.HTTP.Port)
   399  	defer cm.StopServer()
   400  
   401  	Convey("commands without gql", t, func() {
   402  		configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL))
   403  		defer os.Remove(configPath)
   404  
   405  		Convey("cveid", func() {
   406  			args := []string{"affected", "CVE-1942", "--config", "cvetest"}
   407  			cmd := NewCVECommand(mockService{})
   408  			buff := bytes.NewBufferString("")
   409  			cmd.SetOut(buff)
   410  			cmd.SetErr(buff)
   411  			cmd.SetArgs(args)
   412  			err := cmd.Execute()
   413  			So(err, ShouldBeNil)
   414  			space := regexp.MustCompile(`\s+`)
   415  			str := space.ReplaceAllString(buff.String(), " ")
   416  			actual := strings.TrimSpace(str)
   417  			So(actual, ShouldContainSubstring, "image-name tag os/arch 6e2f80bf false 123kB")
   418  		})
   419  
   420  		Convey("cveid db download wait", func() {
   421  			count := 0
   422  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`,
   423  				baseURL))
   424  			args := []string{"affected", "CVE-12345", "--config", "cvetest"}
   425  			defer os.Remove(configPath)
   426  			cmd := NewCVECommand(mockService{
   427  				getTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password,
   428  					imageName, cveID string) (*zcommon.ImagesForCve, error,
   429  				) {
   430  					if count == 0 {
   431  						count++
   432  						fmt.Println("Count:", count)
   433  
   434  						return &zcommon.ImagesForCve{}, zerr.ErrCVEDBNotFound
   435  					}
   436  
   437  					return &zcommon.ImagesForCve{}, zerr.ErrInjected
   438  				},
   439  			})
   440  			buff := bytes.NewBufferString("")
   441  			cmd.SetOut(buff)
   442  			cmd.SetErr(buff)
   443  			cmd.SetArgs(args)
   444  			err := cmd.Execute()
   445  			So(err, ShouldNotBeNil)
   446  			space := regexp.MustCompile(`\s+`)
   447  			str := space.ReplaceAllString(buff.String(), " ")
   448  			actual := strings.TrimSpace(str)
   449  			So(actual, ShouldContainSubstring, "[warning] CVE DB is not ready")
   450  		})
   451  
   452  		Convey("fixed", func() {
   453  			args := []string{"fixed", "image-name", "CVE-123", "--config", "cvetest"}
   454  			cmd := NewCVECommand(mockService{})
   455  			buff := bytes.NewBufferString("")
   456  			cmd.SetOut(buff)
   457  			cmd.SetErr(buff)
   458  			cmd.SetArgs(args)
   459  			err := cmd.Execute()
   460  			So(err, ShouldBeNil)
   461  			space := regexp.MustCompile(`\s+`)
   462  			str := space.ReplaceAllString(buff.String(), " ")
   463  			actual := strings.TrimSpace(str)
   464  			So(actual, ShouldContainSubstring, "image-name tag os/arch 6e2f80bf false 123kB")
   465  		})
   466  
   467  		Convey("fixed db download wait", func() {
   468  			count := 0
   469  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`,
   470  				baseURL))
   471  			args := []string{"fixed", "repo", "CVE-2222", "--config", "cvetest"}
   472  			defer os.Remove(configPath)
   473  			cmd := NewCVECommand(mockService{
   474  				getFixedTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password,
   475  					imageName, cveID string) (*zcommon.ImageListWithCVEFixedResponse, error,
   476  				) {
   477  					if count == 0 {
   478  						count++
   479  						fmt.Println("Count:", count)
   480  
   481  						return &zcommon.ImageListWithCVEFixedResponse{}, zerr.ErrCVEDBNotFound
   482  					}
   483  
   484  					return &zcommon.ImageListWithCVEFixedResponse{}, zerr.ErrInjected
   485  				},
   486  			})
   487  			buff := bytes.NewBufferString("")
   488  			cmd.SetOut(buff)
   489  			cmd.SetErr(buff)
   490  			cmd.SetArgs(args)
   491  			err := cmd.Execute()
   492  			So(err, ShouldNotBeNil)
   493  			space := regexp.MustCompile(`\s+`)
   494  			str := space.ReplaceAllString(buff.String(), " ")
   495  			actual := strings.TrimSpace(str)
   496  			So(actual, ShouldContainSubstring, "[warning] CVE DB is not ready")
   497  		})
   498  
   499  		Convey("image", func() {
   500  			args := []string{"list", "repo:tag", "--config", "cvetest"}
   501  			cmd := NewCVECommand(mockService{})
   502  			buff := bytes.NewBufferString("")
   503  			cmd.SetOut(buff)
   504  			cmd.SetErr(buff)
   505  			cmd.SetArgs(args)
   506  			err := cmd.Execute()
   507  			So(err, ShouldBeNil)
   508  			space := regexp.MustCompile(`\s+`)
   509  			str := space.ReplaceAllString(buff.String(), " ")
   510  			actual := strings.TrimSpace(str)
   511  			So(actual, ShouldContainSubstring, "dummyCVEID HIGH Title of that CVE")
   512  		})
   513  
   514  		Convey("image db download wait", func() {
   515  			count := 0
   516  			configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`,
   517  				baseURL))
   518  			args := []string{"list", "repo:vuln", "--config", "cvetest"}
   519  			defer os.Remove(configPath)
   520  			cmd := NewCVECommand(mockService{
   521  				getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username, password,
   522  					imageName, searchedCVE string) (*cveResult, error,
   523  				) {
   524  					if count == 0 {
   525  						count++
   526  						fmt.Println("Count:", count)
   527  
   528  						return &cveResult{}, zerr.ErrCVEDBNotFound
   529  					}
   530  
   531  					return &cveResult{}, zerr.ErrInjected
   532  				},
   533  			})
   534  			buff := bytes.NewBufferString("")
   535  			cmd.SetOut(buff)
   536  			cmd.SetErr(buff)
   537  			cmd.SetArgs(args)
   538  			err := cmd.Execute()
   539  			So(err, ShouldNotBeNil)
   540  			space := regexp.MustCompile(`\s+`)
   541  			str := space.ReplaceAllString(buff.String(), " ")
   542  			actual := strings.TrimSpace(str)
   543  			So(actual, ShouldContainSubstring, "[warning] CVE DB is not ready")
   544  		})
   545  	})
   546  }
   547  
   548  func TestCVECommandErrors(t *testing.T) {
   549  	port := test.GetFreePort()
   550  	baseURL := test.GetBaseURL(port)
   551  	conf := config.New()
   552  	conf.HTTP.Port = port
   553  
   554  	conf.Extensions = &extconf.ExtensionConfig{
   555  		Search: &extconf.SearchConfig{
   556  			BaseConfig: extconf.BaseConfig{Enable: ref(true)},
   557  		},
   558  	}
   559  
   560  	ctlr := api.NewController(conf)
   561  	ctlr.Config.Storage.RootDirectory = t.TempDir()
   562  	cm := test.NewControllerManager(ctlr)
   563  
   564  	cm.StartAndWait(conf.HTTP.Port)
   565  	defer cm.StopServer()
   566  
   567  	Convey("commands without gql", t, func() {
   568  		configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL))
   569  		defer os.Remove(configPath)
   570  
   571  		Convey("cveid", func() {
   572  			args := []string{"affected", "CVE-1942"}
   573  			cmd := NewCVECommand(mockService{})
   574  			buff := bytes.NewBufferString("")
   575  			cmd.SetOut(buff)
   576  			cmd.SetErr(buff)
   577  			cmd.SetArgs(args)
   578  			err := cmd.Execute()
   579  			So(err, ShouldNotBeNil)
   580  		})
   581  
   582  		Convey("cveid error", func() {
   583  			// too many args
   584  			args := []string{"too", "many", "args"}
   585  			cmd := NewImagesByCVEIDCommand(mockService{})
   586  			buff := bytes.NewBufferString("")
   587  			cmd.SetOut(buff)
   588  			cmd.SetErr(buff)
   589  			cmd.SetArgs(args)
   590  			err := cmd.Execute()
   591  			So(err, ShouldNotBeNil)
   592  
   593  			// bad args
   594  			args = []string{"not-a-cve-id"}
   595  			cmd = NewImagesByCVEIDCommand(mockService{})
   596  			buff = bytes.NewBufferString("")
   597  			cmd.SetOut(buff)
   598  			cmd.SetErr(buff)
   599  			cmd.SetArgs(args)
   600  			err = cmd.Execute()
   601  			So(err, ShouldNotBeNil)
   602  
   603  			// no URL
   604  			args = []string{"CVE-1942"}
   605  			cmd = NewImagesByCVEIDCommand(mockService{})
   606  			buff = bytes.NewBufferString("")
   607  			cmd.SetOut(buff)
   608  			cmd.SetErr(buff)
   609  			cmd.SetArgs(args)
   610  			err = cmd.Execute()
   611  			So(err, ShouldNotBeNil)
   612  		})
   613  
   614  		Convey("fixed command", func() {
   615  			args := []string{"fixed", "image-name", "CVE-123"}
   616  			cmd := NewCVECommand(mockService{})
   617  			buff := bytes.NewBufferString("")
   618  			cmd.SetOut(buff)
   619  			cmd.SetErr(buff)
   620  			cmd.SetArgs(args)
   621  			err := cmd.Execute()
   622  			So(err, ShouldNotBeNil)
   623  		})
   624  
   625  		Convey("fixed command error", func() {
   626  			// too many args
   627  			args := []string{"too", "many", "args", "args"}
   628  			cmd := NewFixedTagsCommand(mockService{})
   629  			buff := bytes.NewBufferString("")
   630  			cmd.SetOut(buff)
   631  			cmd.SetErr(buff)
   632  			cmd.SetArgs(args)
   633  			err := cmd.Execute()
   634  			So(err, ShouldNotBeNil)
   635  
   636  			// bad args
   637  			args = []string{"repo-tag-instead-of-just-repo:fail-here", "CVE-123"}
   638  			cmd = NewFixedTagsCommand(mockService{})
   639  			buff = bytes.NewBufferString("")
   640  			cmd.SetOut(buff)
   641  			cmd.SetErr(buff)
   642  			cmd.SetArgs(args)
   643  			err = cmd.Execute()
   644  			So(err, ShouldNotBeNil)
   645  
   646  			// no URL
   647  			args = []string{"CVE-1942"}
   648  			cmd = NewFixedTagsCommand(mockService{})
   649  			buff = bytes.NewBufferString("")
   650  			cmd.SetOut(buff)
   651  			cmd.SetErr(buff)
   652  			cmd.SetArgs(args)
   653  			err = cmd.Execute()
   654  			So(err, ShouldNotBeNil)
   655  		})
   656  
   657  		Convey("image", func() {
   658  			args := []string{"list", "repo:tag"}
   659  			cmd := NewCVECommand(mockService{})
   660  			buff := bytes.NewBufferString("")
   661  			cmd.SetOut(buff)
   662  			cmd.SetErr(buff)
   663  			cmd.SetArgs(args)
   664  			err := cmd.Execute()
   665  			So(err, ShouldNotBeNil)
   666  		})
   667  
   668  		Convey("image command error", func() {
   669  			// too many args
   670  			args := []string{"too", "many", "args", "args"}
   671  			cmd := NewCveForImageCommand(mockService{})
   672  			buff := bytes.NewBufferString("")
   673  			cmd.SetOut(buff)
   674  			cmd.SetErr(buff)
   675  			cmd.SetArgs(args)
   676  			err := cmd.Execute()
   677  			So(err, ShouldNotBeNil)
   678  
   679  			// bad args
   680  			args = []string{"repo-tag-instead-of-just-repo:fail-here", "CVE-123"}
   681  			cmd = NewCveForImageCommand(mockService{})
   682  			buff = bytes.NewBufferString("")
   683  			cmd.SetOut(buff)
   684  			cmd.SetErr(buff)
   685  			cmd.SetArgs(args)
   686  			err = cmd.Execute()
   687  			So(err, ShouldNotBeNil)
   688  
   689  			// no URL
   690  			args = []string{"CVE-1942"}
   691  			cmd = NewCveForImageCommand(mockService{})
   692  			buff = bytes.NewBufferString("")
   693  			cmd.SetOut(buff)
   694  			cmd.SetErr(buff)
   695  			cmd.SetArgs(args)
   696  			err = cmd.Execute()
   697  			So(err, ShouldNotBeNil)
   698  		})
   699  	})
   700  }
   701  
   702  type mockServiceForRetry struct {
   703  	mockService
   704  	retryCounter int
   705  	succeedOn    int
   706  }
   707  
   708  func (service *mockServiceForRetry) getTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password, repo,
   709  	cveID string,
   710  ) (*zcommon.ImagesForCve, error) {
   711  	service.retryCounter += 1
   712  
   713  	if service.retryCounter < service.succeedOn || service.succeedOn < 0 {
   714  		return &zcommon.ImagesForCve{}, zerr.ErrCVEDBNotFound
   715  	}
   716  
   717  	return service.mockService.getTagsForCVEGQL(ctx, config, username, password, repo, cveID)
   718  }