zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/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.dev/zot/errors" 20 "zotregistry.dev/zot/pkg/api" 21 "zotregistry.dev/zot/pkg/api/config" 22 zcommon "zotregistry.dev/zot/pkg/common" 23 extconf "zotregistry.dev/zot/pkg/extensions/config" 24 test "zotregistry.dev/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, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1 "+ 145 "ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE") 146 So(err, ShouldBeNil) 147 }) 148 149 Convey("Test debug flag", t, func() { 150 args := []string{"list", "dummyImageName:tag", "--debug", "--config", "cvetest"} 151 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL)) 152 defer os.Remove(configPath) 153 cmd := NewCVECommand(new(searchService)) 154 buff := bytes.NewBufferString("") 155 cmd.SetOut(buff) 156 cmd.SetErr(buff) 157 cmd.SetArgs(args) 158 err := cmd.Execute() 159 space := regexp.MustCompile(`\s+`) 160 str := space.ReplaceAllString(buff.String(), " ") 161 So(strings.TrimSpace(str), ShouldContainSubstring, "GET") 162 So(err, ShouldNotBeNil) 163 }) 164 165 Convey("Test CVE by name and CVE ID - long option", t, func() { 166 args := []string{"affected", "CVE-CVEID", "--repo", "dummyImageName", "--url", baseURL} 167 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 168 defer os.Remove(configPath) 169 cveCmd := NewCVECommand(new(mockService)) 170 buff := bytes.NewBufferString("") 171 cveCmd.SetOut(buff) 172 cveCmd.SetErr(buff) 173 cveCmd.SetArgs(args) 174 err := cveCmd.Execute() 175 So(err, ShouldBeNil) 176 space := regexp.MustCompile(`\s+`) 177 str := space.ReplaceAllString(buff.String(), " ") 178 So(strings.TrimSpace(str), ShouldEqual, 179 "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB") 180 }) 181 182 Convey("Test CVE by name and CVE ID - using shorthand", t, func() { 183 args := []string{"affected", "CVE-CVEID", "--repo", "dummyImageName", "--url", baseURL} 184 buff := bytes.NewBufferString("") 185 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 186 defer os.Remove(configPath) 187 cveCmd := NewCVECommand(new(mockService)) 188 cveCmd.SetOut(buff) 189 cveCmd.SetErr(buff) 190 cveCmd.SetArgs(args) 191 err := cveCmd.Execute() 192 So(err, ShouldBeNil) 193 space := regexp.MustCompile(`\s+`) 194 str := space.ReplaceAllString(buff.String(), " ") 195 So(strings.TrimSpace(str), ShouldEqual, 196 "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB") 197 }) 198 199 Convey("Test CVE by image name - in text format", t, func() { 200 args := []string{"list", "dummyImageName:tag", "--url", baseURL} 201 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 202 defer os.Remove(configPath) 203 cveCmd := NewCVECommand(new(mockService)) 204 buff := bytes.NewBufferString("") 205 cveCmd.SetOut(buff) 206 cveCmd.SetErr(buff) 207 cveCmd.SetArgs(args) 208 err := cveCmd.Execute() 209 space := regexp.MustCompile(`\s+`) 210 str := space.ReplaceAllString(buff.String(), " ") 211 So(strings.TrimSpace(str), ShouldEqual, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1 "+ 212 "ID SEVERITY TITLE dummyCVEID HIGH Title of that CVE") 213 So(err, ShouldBeNil) 214 }) 215 216 Convey("Test CVE by image name - in text format - in verbose mode", t, func() { 217 args := []string{"list", "dummyImageName:tag", "--url", baseURL, "--verbose"} 218 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 219 defer os.Remove(configPath) 220 cveCmd := NewCVECommand(new(mockService)) 221 buff := bytes.NewBufferString("") 222 cveCmd.SetOut(buff) 223 cveCmd.SetErr(buff) 224 cveCmd.SetArgs(args) 225 err := cveCmd.Execute() 226 227 outputLines := strings.Split(buff.String(), "\n") 228 expected := []string{ 229 "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1", 230 "", 231 "dummyCVEID", 232 "Severity: HIGH", 233 "Title: Title of that CVE", 234 "Description:", 235 "Description of the CVE", 236 "", 237 "Vulnerable Packages:", 238 " Package Name: packagename", 239 " Package Path: ", 240 " Installed Version: installedver", 241 " Fixed Version: fixedver", 242 "", 243 "", 244 } 245 246 for index, expectedLine := range expected { 247 So(outputLines[index], ShouldEqual, expectedLine) 248 } 249 250 So(err, ShouldBeNil) 251 }) 252 253 Convey("Test CVE by image name - in json format", t, func() { 254 args := []string{"list", "dummyImageName:tag", "--url", baseURL, "-f", "json"} 255 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 256 defer os.Remove(configPath) 257 cveCmd := NewCVECommand(new(mockService)) 258 buff := bytes.NewBufferString("") 259 cveCmd.SetOut(buff) 260 cveCmd.SetErr(buff) 261 cveCmd.SetArgs(args) 262 err := cveCmd.Execute() 263 // Output is supposed to be in json lines format, keep all spaces as is for verification 264 So(buff.String(), ShouldEqual, `{"Tag":"dummyImageName:tag","CVEList":`+ 265 `[{"Id":"dummyCVEID","Severity":"HIGH","Title":"Title of that CVE",`+ 266 `"Description":"Description of the CVE","PackageList":[{"Name":"packagename",`+ 267 `"PackagePath":"","InstalledVersion":"installedver","FixedVersion":"fixedver"}]}],"Summary":`+ 268 `{"maxSeverity":"HIGH","unknownCount":0,"lowCount":0,"mediumCount":0,"highCount":1,`+ 269 `"criticalCount":0,"count":1}}`+"\n") 270 So(err, ShouldBeNil) 271 }) 272 273 Convey("Test CVE by image name - in yaml format", t, func() { 274 args := []string{"list", "dummyImageName:tag", "--url", baseURL, "-f", "yaml"} 275 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 276 defer os.Remove(configPath) 277 cveCmd := NewCVECommand(new(mockService)) 278 buff := bytes.NewBufferString("") 279 cveCmd.SetOut(buff) 280 cveCmd.SetErr(buff) 281 cveCmd.SetArgs(args) 282 err := cveCmd.Execute() 283 space := regexp.MustCompile(`\s+`) 284 str := space.ReplaceAllString(buff.String(), " ") 285 So(strings.TrimSpace(str), ShouldEqual, `--- tag: dummyImageName:tag cvelist: - id: dummyCVEID`+ 286 ` severity: HIGH title: Title of that CVE description: Description of the CVE packagelist: `+ 287 `- name: packagename packagepath: "" installedversion: installedver fixedversion: fixedver `+ 288 `summary: maxseverity: HIGH unknowncount: 0 lowcount: 0 mediumcount: 0 highcount: 1 criticalcount: 0 count: 1`) 289 So(err, ShouldBeNil) 290 }) 291 Convey("Test CVE by image name - invalid format", t, func() { 292 args := []string{"list", "dummyImageName:tag", "--url", baseURL, "-f", "random"} 293 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 294 defer os.Remove(configPath) 295 cveCmd := NewCVECommand(new(mockService)) 296 buff := bytes.NewBufferString("") 297 cveCmd.SetOut(buff) 298 cveCmd.SetErr(buff) 299 cveCmd.SetArgs(args) 300 err := cveCmd.Execute() 301 space := regexp.MustCompile(`\s+`) 302 str := space.ReplaceAllString(buff.String(), " ") 303 So(err, ShouldNotBeNil) 304 So(strings.TrimSpace(str), ShouldContainSubstring, zerr.ErrInvalidOutputFormat.Error()) 305 }) 306 307 Convey("Test images by CVE ID - positive", t, func() { 308 args := []string{"affected", "CVE-CVEID", "--repo", "anImage", "--url", baseURL} 309 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 310 defer os.Remove(configPath) 311 cveCmd := NewCVECommand(new(mockService)) 312 buff := bytes.NewBufferString("") 313 cveCmd.SetOut(buff) 314 cveCmd.SetErr(buff) 315 cveCmd.SetArgs(args) 316 err := cveCmd.Execute() 317 space := regexp.MustCompile(`\s+`) 318 str := space.ReplaceAllString(buff.String(), " ") 319 So(strings.TrimSpace(str), ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB") //nolint:lll 320 So(err, ShouldBeNil) 321 }) 322 323 Convey("Test images by CVE ID - positive with retries", t, func() { 324 args := []string{"affected", "CVE-CVEID", "--repo", "anImage", "--url", baseURL} 325 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 326 defer os.Remove(configPath) 327 mockService := mockServiceForRetry{succeedOn: 2} // CVE info will be provided in 2nd attempt 328 cveCmd := NewCVECommand(&mockService) 329 buff := bytes.NewBufferString("") 330 cveCmd.SetOut(buff) 331 cveCmd.SetErr(buff) 332 cveCmd.SetArgs(args) 333 err := cveCmd.Execute() 334 space := regexp.MustCompile(`\s+`) 335 str := space.ReplaceAllString(buff.String(), " ") 336 t.Logf("Output: %s", str) 337 So(strings.TrimSpace(str), ShouldContainSubstring, 338 "[warning] CVE DB is not ready [1] - retry in "+strconv.Itoa(CveDBRetryInterval)+" seconds") 339 So(strings.TrimSpace(str), ShouldContainSubstring, 340 "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB") 341 So(err, ShouldBeNil) 342 }) 343 344 Convey("Test images by CVE ID - failed after retries", t, func() { 345 args := []string{"affected", "CVE-CVEID", "--url", baseURL} 346 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 347 defer os.Remove(configPath) 348 mockService := mockServiceForRetry{succeedOn: -1} // CVE info will be unavailable on all retries 349 cveCmd := NewCVECommand(&mockService) 350 buff := bytes.NewBufferString("") 351 cveCmd.SetOut(buff) 352 cveCmd.SetErr(buff) 353 cveCmd.SetArgs(args) 354 err := cveCmd.Execute() 355 space := regexp.MustCompile(`\s+`) 356 str := space.ReplaceAllString(buff.String(), " ") 357 t.Logf("Output: %s", str) 358 So(strings.TrimSpace(str), ShouldContainSubstring, 359 "[warning] CVE DB is not ready [1] - retry in "+strconv.Itoa(CveDBRetryInterval)+" seconds") 360 So(strings.TrimSpace(str), ShouldNotContainSubstring, 361 "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB") 362 So(err, ShouldNotBeNil) 363 }) 364 365 Convey("Test images by CVE ID - invalid CVE ID", t, func() { 366 args := []string{"affected", "CVE-invalidCVEID", "--config", "cvetest"} 367 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 368 defer os.Remove(configPath) 369 cveCmd := NewCVECommand(new(mockService)) 370 buff := bytes.NewBufferString("") 371 cveCmd.SetOut(buff) 372 cveCmd.SetErr(buff) 373 cveCmd.SetArgs(args) 374 err := cveCmd.Execute() 375 So(err, ShouldNotBeNil) 376 }) 377 378 Convey("Test images by CVE ID - invalid url", t, func() { 379 args := []string{"affected", "CVE-CVEID", "--url", "invalidURL"} 380 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 381 defer os.Remove(configPath) 382 cveCmd := NewCVECommand(NewSearchService()) 383 buff := bytes.NewBufferString("") 384 cveCmd.SetOut(buff) 385 cveCmd.SetErr(buff) 386 cveCmd.SetArgs(args) 387 err := cveCmd.Execute() 388 So(err, ShouldNotBeNil) 389 So(errors.Is(err, zerr.ErrInvalidURL), ShouldBeTrue) 390 So(buff.String(), ShouldContainSubstring, "invalid URL format") 391 }) 392 393 Convey("Test fixed tags by and image name CVE ID - positive", t, func() { 394 args := []string{"fixed", "fixedImage", "CVE-CVEID", "--url", baseURL} 395 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 396 defer os.Remove(configPath) 397 cveCmd := NewCVECommand(new(mockService)) 398 buff := bytes.NewBufferString("") 399 cveCmd.SetOut(buff) 400 cveCmd.SetErr(buff) 401 cveCmd.SetArgs(args) 402 err := cveCmd.Execute() 403 space := regexp.MustCompile(`\s+`) 404 str := space.ReplaceAllString(buff.String(), " ") 405 So(err, ShouldBeNil) 406 So(strings.TrimSpace(str), ShouldEqual, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE fixedImage tag os/arch 6e2f80bf false 123kB") //nolint:lll 407 }) 408 409 Convey("Test fixed tags by and image name CVE ID - invalid image name", t, func() { 410 args := []string{"affected", "CVE-CVEID", "--image", "invalidImageName", "--config", "cvetest"} 411 configPath := makeConfigFile(`{"configs":[{"_name":"cvetest","showspinner":false}]}`) 412 defer os.Remove(configPath) 413 cveCmd := NewCVECommand(NewSearchService()) 414 buff := bytes.NewBufferString("") 415 cveCmd.SetOut(buff) 416 cveCmd.SetErr(buff) 417 cveCmd.SetArgs(args) 418 err := cveCmd.Execute() 419 So(err, ShouldNotBeNil) 420 }) 421 } 422 423 func TestCVECommandGQL(t *testing.T) { 424 port := test.GetFreePort() 425 baseURL := test.GetBaseURL(port) 426 conf := config.New() 427 conf.HTTP.Port = port 428 429 defaultVal := true 430 conf.Extensions = &extconf.ExtensionConfig{ 431 Search: &extconf.SearchConfig{ 432 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 433 }, 434 } 435 436 ctlr := api.NewController(conf) 437 ctlr.Config.Storage.RootDirectory = t.TempDir() 438 cm := test.NewControllerManager(ctlr) 439 440 cm.StartAndWait(conf.HTTP.Port) 441 defer cm.StopServer() 442 443 Convey("commands without gql", t, func() { 444 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL)) 445 defer os.Remove(configPath) 446 447 Convey("cveid", func() { 448 args := []string{"affected", "CVE-1942", "--config", "cvetest"} 449 cmd := NewCVECommand(mockService{}) 450 buff := bytes.NewBufferString("") 451 cmd.SetOut(buff) 452 cmd.SetErr(buff) 453 cmd.SetArgs(args) 454 err := cmd.Execute() 455 So(err, ShouldBeNil) 456 space := regexp.MustCompile(`\s+`) 457 str := space.ReplaceAllString(buff.String(), " ") 458 actual := strings.TrimSpace(str) 459 So(actual, ShouldContainSubstring, "image-name tag os/arch 6e2f80bf false 123kB") 460 }) 461 462 Convey("cveid db download wait", func() { 463 count := 0 464 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, 465 baseURL)) 466 args := []string{"affected", "CVE-12345", "--config", "cvetest"} 467 defer os.Remove(configPath) 468 cmd := NewCVECommand(mockService{ 469 getTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password, 470 imageName, cveID string) (*zcommon.ImagesForCve, error, 471 ) { 472 if count == 0 { 473 count++ 474 fmt.Println("Count:", count) 475 476 return &zcommon.ImagesForCve{}, zerr.ErrCVEDBNotFound 477 } 478 479 return &zcommon.ImagesForCve{}, zerr.ErrInjected 480 }, 481 }) 482 buff := bytes.NewBufferString("") 483 cmd.SetOut(buff) 484 cmd.SetErr(buff) 485 cmd.SetArgs(args) 486 err := cmd.Execute() 487 So(err, ShouldNotBeNil) 488 space := regexp.MustCompile(`\s+`) 489 str := space.ReplaceAllString(buff.String(), " ") 490 actual := strings.TrimSpace(str) 491 So(actual, ShouldContainSubstring, "[warning] CVE DB is not ready") 492 }) 493 494 Convey("fixed", func() { 495 args := []string{"fixed", "image-name", "CVE-123", "--config", "cvetest"} 496 cmd := NewCVECommand(mockService{}) 497 buff := bytes.NewBufferString("") 498 cmd.SetOut(buff) 499 cmd.SetErr(buff) 500 cmd.SetArgs(args) 501 err := cmd.Execute() 502 So(err, ShouldBeNil) 503 space := regexp.MustCompile(`\s+`) 504 str := space.ReplaceAllString(buff.String(), " ") 505 actual := strings.TrimSpace(str) 506 So(actual, ShouldContainSubstring, "image-name tag os/arch 6e2f80bf false 123kB") 507 }) 508 509 Convey("fixed db download wait", func() { 510 count := 0 511 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, 512 baseURL)) 513 args := []string{"fixed", "repo", "CVE-2222", "--config", "cvetest"} 514 defer os.Remove(configPath) 515 cmd := NewCVECommand(mockService{ 516 getFixedTagsForCVEGQLFn: func(ctx context.Context, config SearchConfig, username, password, 517 imageName, cveID string) (*zcommon.ImageListWithCVEFixedResponse, error, 518 ) { 519 if count == 0 { 520 count++ 521 fmt.Println("Count:", count) 522 523 return &zcommon.ImageListWithCVEFixedResponse{}, zerr.ErrCVEDBNotFound 524 } 525 526 return &zcommon.ImageListWithCVEFixedResponse{}, zerr.ErrInjected 527 }, 528 }) 529 buff := bytes.NewBufferString("") 530 cmd.SetOut(buff) 531 cmd.SetErr(buff) 532 cmd.SetArgs(args) 533 err := cmd.Execute() 534 So(err, ShouldNotBeNil) 535 space := regexp.MustCompile(`\s+`) 536 str := space.ReplaceAllString(buff.String(), " ") 537 actual := strings.TrimSpace(str) 538 So(actual, ShouldContainSubstring, "[warning] CVE DB is not ready") 539 }) 540 541 Convey("image", func() { 542 args := []string{"list", "repo:tag", "--config", "cvetest"} 543 cmd := NewCVECommand(mockService{}) 544 buff := bytes.NewBufferString("") 545 cmd.SetOut(buff) 546 cmd.SetErr(buff) 547 cmd.SetArgs(args) 548 err := cmd.Execute() 549 So(err, ShouldBeNil) 550 space := regexp.MustCompile(`\s+`) 551 str := space.ReplaceAllString(buff.String(), " ") 552 actual := strings.TrimSpace(str) 553 So(actual, ShouldContainSubstring, "CRITICAL 0, HIGH 1, MEDIUM 0, LOW 0, UNKNOWN 0, TOTAL 1") 554 So(actual, ShouldContainSubstring, "dummyCVEID HIGH Title of that CVE") 555 }) 556 557 Convey("image db download wait", func() { 558 count := 0 559 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, 560 baseURL)) 561 args := []string{"list", "repo:vuln", "--config", "cvetest"} 562 defer os.Remove(configPath) 563 cmd := NewCVECommand(mockService{ 564 getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username, password, 565 imageName, searchedCVE string) (*cveResult, error, 566 ) { 567 if count == 0 { 568 count++ 569 fmt.Println("Count:", count) 570 571 return &cveResult{}, zerr.ErrCVEDBNotFound 572 } 573 574 return &cveResult{}, zerr.ErrInjected 575 }, 576 }) 577 buff := bytes.NewBufferString("") 578 cmd.SetOut(buff) 579 cmd.SetErr(buff) 580 cmd.SetArgs(args) 581 err := cmd.Execute() 582 So(err, ShouldNotBeNil) 583 space := regexp.MustCompile(`\s+`) 584 str := space.ReplaceAllString(buff.String(), " ") 585 actual := strings.TrimSpace(str) 586 So(actual, ShouldContainSubstring, "[warning] CVE DB is not ready") 587 }) 588 }) 589 } 590 591 func TestCVECommandErrors(t *testing.T) { 592 port := test.GetFreePort() 593 baseURL := test.GetBaseURL(port) 594 conf := config.New() 595 conf.HTTP.Port = port 596 597 conf.Extensions = &extconf.ExtensionConfig{ 598 Search: &extconf.SearchConfig{ 599 BaseConfig: extconf.BaseConfig{Enable: ref(true)}, 600 }, 601 } 602 603 ctlr := api.NewController(conf) 604 ctlr.Config.Storage.RootDirectory = t.TempDir() 605 cm := test.NewControllerManager(ctlr) 606 607 cm.StartAndWait(conf.HTTP.Port) 608 defer cm.StopServer() 609 610 Convey("commands without gql", t, func() { 611 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"cvetest","url":"%s","showspinner":false}]}`, baseURL)) 612 defer os.Remove(configPath) 613 614 Convey("cveid", func() { 615 args := []string{"affected", "CVE-1942"} 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("cveid error", func() { 626 // too many args 627 args := []string{"too", "many", "args"} 628 cmd := NewImagesByCVEIDCommand(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{"not-a-cve-id"} 638 cmd = NewImagesByCVEIDCommand(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 = NewImagesByCVEIDCommand(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("fixed command", func() { 658 args := []string{"fixed", "image-name", "CVE-123"} 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("fixed command error", func() { 669 // too many args 670 args := []string{"too", "many", "args", "args"} 671 cmd := NewFixedTagsCommand(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 = NewFixedTagsCommand(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 = NewFixedTagsCommand(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 Convey("image", func() { 701 args := []string{"list", "repo:tag"} 702 cmd := NewCVECommand(mockService{}) 703 buff := bytes.NewBufferString("") 704 cmd.SetOut(buff) 705 cmd.SetErr(buff) 706 cmd.SetArgs(args) 707 err := cmd.Execute() 708 So(err, ShouldNotBeNil) 709 }) 710 711 Convey("image command error", func() { 712 // too many args 713 args := []string{"too", "many", "args", "args"} 714 cmd := NewCveForImageCommand(mockService{}) 715 buff := bytes.NewBufferString("") 716 cmd.SetOut(buff) 717 cmd.SetErr(buff) 718 cmd.SetArgs(args) 719 err := cmd.Execute() 720 So(err, ShouldNotBeNil) 721 722 // bad args 723 args = []string{"repo-tag-instead-of-just-repo:fail-here", "CVE-123"} 724 cmd = NewCveForImageCommand(mockService{}) 725 buff = bytes.NewBufferString("") 726 cmd.SetOut(buff) 727 cmd.SetErr(buff) 728 cmd.SetArgs(args) 729 err = cmd.Execute() 730 So(err, ShouldNotBeNil) 731 732 // no URL 733 args = []string{"CVE-1942"} 734 cmd = NewCveForImageCommand(mockService{}) 735 buff = bytes.NewBufferString("") 736 cmd.SetOut(buff) 737 cmd.SetErr(buff) 738 cmd.SetArgs(args) 739 err = cmd.Execute() 740 So(err, ShouldNotBeNil) 741 }) 742 }) 743 } 744 745 type mockServiceForRetry struct { 746 mockService 747 retryCounter int 748 succeedOn int 749 } 750 751 func (service *mockServiceForRetry) getTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password, repo, 752 cveID string, 753 ) (*zcommon.ImagesForCve, error) { 754 service.retryCounter += 1 755 756 if service.retryCounter < service.succeedOn || service.succeedOn < 0 { 757 return &zcommon.ImagesForCve{}, zerr.ErrCVEDBNotFound 758 } 759 760 return service.mockService.getTagsForCVEGQL(ctx, config, username, password, repo, cveID) 761 }