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 }