github.com/seilagamo/poc-lava-release@v0.3.3-rc3/internal/report/report_test.go (about) 1 // Copyright 2023 Adevinta 2 3 package report 4 5 import ( 6 "fmt" 7 "os" 8 "path" 9 "testing" 10 11 vreport "github.com/adevinta/vulcan-report" 12 "github.com/google/go-cmp/cmp" 13 "github.com/google/go-cmp/cmp/cmpopts" 14 15 "github.com/seilagamo/poc-lava-release/internal/config" 16 "github.com/seilagamo/poc-lava-release/internal/engine" 17 ) 18 19 func TestWriter_calculateExitCode(t *testing.T) { 20 tests := []struct { 21 name string 22 sum summary 23 rConfig config.ReportConfig 24 want ExitCode 25 }{ 26 { 27 name: "critical", 28 sum: summary{ 29 count: map[config.Severity]int{ 30 config.SeverityCritical: 1, 31 config.SeverityHigh: 1, 32 config.SeverityMedium: 1, 33 config.SeverityLow: 1, 34 config.SeverityInfo: 1, 35 }, 36 }, 37 rConfig: config.ReportConfig{ 38 Severity: config.SeverityInfo, 39 }, 40 want: ExitCodeCritical, 41 }, 42 { 43 name: "high", 44 sum: summary{ 45 count: map[config.Severity]int{ 46 config.SeverityCritical: 0, 47 config.SeverityHigh: 1, 48 config.SeverityMedium: 1, 49 config.SeverityLow: 1, 50 config.SeverityInfo: 1, 51 }, 52 }, 53 rConfig: config.ReportConfig{ 54 Severity: config.SeverityInfo, 55 }, 56 want: ExitCodeHigh, 57 }, 58 { 59 name: "medium", 60 sum: summary{ 61 count: map[config.Severity]int{ 62 config.SeverityCritical: 0, 63 config.SeverityHigh: 0, 64 config.SeverityMedium: 1, 65 config.SeverityLow: 1, 66 config.SeverityInfo: 1, 67 }, 68 }, 69 rConfig: config.ReportConfig{ 70 Severity: config.SeverityInfo, 71 }, 72 want: ExitCodeMedium, 73 }, 74 { 75 name: "low", 76 sum: summary{ 77 count: map[config.Severity]int{ 78 config.SeverityCritical: 0, 79 config.SeverityHigh: 0, 80 config.SeverityMedium: 0, 81 config.SeverityLow: 1, 82 config.SeverityInfo: 1, 83 }, 84 }, 85 rConfig: config.ReportConfig{ 86 Severity: config.SeverityInfo, 87 }, 88 want: ExitCodeLow, 89 }, 90 { 91 name: "info", 92 sum: summary{ 93 count: map[config.Severity]int{ 94 config.SeverityCritical: 0, 95 config.SeverityHigh: 0, 96 config.SeverityMedium: 0, 97 config.SeverityLow: 0, 98 config.SeverityInfo: 1, 99 }, 100 }, 101 rConfig: config.ReportConfig{ 102 Severity: config.SeverityInfo, 103 }, 104 want: ExitCodeInfo, 105 }, 106 { 107 name: "no exit code", 108 sum: summary{ 109 count: map[config.Severity]int{ 110 config.SeverityCritical: 0, 111 config.SeverityHigh: 0, 112 config.SeverityMedium: 1, 113 config.SeverityLow: 1, 114 config.SeverityInfo: 1, 115 }, 116 }, 117 rConfig: config.ReportConfig{ 118 Severity: config.SeverityHigh, 119 }, 120 want: 0, 121 }, 122 } 123 for _, tt := range tests { 124 t.Run(tt.name, func(t *testing.T) { 125 w, err := NewWriter(tt.rConfig) 126 if err != nil { 127 t.Fatalf("unable to create a report writer: %v", err) 128 } 129 got := w.calculateExitCode(tt.sum) 130 if got != tt.want { 131 t.Errorf("unexpected exit code: got: %v, want: %v", got, tt.want) 132 } 133 }) 134 } 135 } 136 137 func TestScoreToSeverity(t *testing.T) { 138 tests := []struct { 139 name string 140 score float32 141 want config.Severity 142 }{ 143 { 144 name: "critical", 145 score: 9, 146 want: config.SeverityCritical, 147 }, 148 { 149 name: "high", 150 score: 7, 151 want: config.SeverityHigh, 152 }, 153 { 154 name: "medium", 155 score: 4, 156 want: config.SeverityMedium, 157 }, 158 { 159 name: "low", 160 score: 0.1, 161 want: config.SeverityLow, 162 }, 163 { 164 name: "info", 165 score: 0, 166 want: config.SeverityInfo, 167 }, 168 } 169 for _, tt := range tests { 170 t.Run(tt.name, func(t *testing.T) { 171 got := scoreToSeverity(tt.score) 172 if got != tt.want { 173 t.Errorf("unexpected severity: got: %v, want: %v", got, tt.want) 174 } 175 }) 176 } 177 } 178 179 func TestWriter_parseReport(t *testing.T) { 180 tests := []struct { 181 name string 182 report engine.Report 183 rConfig config.ReportConfig 184 want []vulnerability 185 wantNilErr bool 186 }{ 187 { 188 name: "all vulnerabilities included", 189 report: map[string]vreport.Report{ 190 "CheckID1": { 191 CheckData: vreport.CheckData{ 192 CheckID: "CheckID1", 193 }, 194 ResultData: vreport.ResultData{ 195 Vulnerabilities: []vreport.Vulnerability{ 196 { 197 Summary: "Vulnerability Summary 1", 198 }, 199 }, 200 }, 201 }, 202 "CheckID2": { 203 CheckData: vreport.CheckData{ 204 CheckID: "CheckID2", 205 }, 206 ResultData: vreport.ResultData{ 207 Vulnerabilities: []vreport.Vulnerability{ 208 { 209 Summary: "Vulnerability Summary 2", 210 Score: 6.7, 211 }, 212 }, 213 }, 214 }, 215 }, 216 rConfig: config.ReportConfig{ 217 Exclusions: []config.Exclusion{}, 218 }, 219 want: []vulnerability{ 220 { 221 CheckData: vreport.CheckData{ 222 CheckID: "CheckID1", 223 }, 224 Vulnerability: vreport.Vulnerability{ 225 Summary: "Vulnerability Summary 1", 226 }, 227 Severity: config.SeverityInfo, 228 excluded: false, 229 }, 230 { 231 CheckData: vreport.CheckData{ 232 CheckID: "CheckID2", 233 }, 234 Vulnerability: vreport.Vulnerability{ 235 Summary: "Vulnerability Summary 2", 236 Score: 6.7, 237 }, 238 Severity: config.SeverityMedium, 239 excluded: false, 240 }, 241 }, 242 wantNilErr: true, 243 }, 244 { 245 name: "some vulnerabilities excluded", 246 report: map[string]vreport.Report{ 247 "CheckID1": { 248 CheckData: vreport.CheckData{ 249 CheckID: "CheckID1", 250 }, 251 ResultData: vreport.ResultData{ 252 Vulnerabilities: []vreport.Vulnerability{ 253 { 254 Summary: "Vulnerability Summary 1", 255 }, 256 }, 257 }, 258 }, 259 "CheckID2": { 260 CheckData: vreport.CheckData{ 261 CheckID: "CheckID2", 262 }, 263 ResultData: vreport.ResultData{ 264 Vulnerabilities: []vreport.Vulnerability{ 265 { 266 Summary: "Vulnerability Summary 2", 267 Score: 6.7, 268 }, 269 }, 270 }, 271 }, 272 }, 273 rConfig: config.ReportConfig{ 274 Exclusions: []config.Exclusion{ 275 {Summary: "Summary 2"}, 276 }, 277 }, 278 want: []vulnerability{ 279 { 280 CheckData: vreport.CheckData{ 281 CheckID: "CheckID1", 282 }, 283 Vulnerability: vreport.Vulnerability{ 284 Summary: "Vulnerability Summary 1", 285 }, 286 Severity: config.SeverityInfo, 287 excluded: false, 288 }, 289 { 290 CheckData: vreport.CheckData{ 291 CheckID: "CheckID2", 292 }, 293 Vulnerability: vreport.Vulnerability{ 294 Summary: "Vulnerability Summary 2", 295 Score: 6.7, 296 }, 297 Severity: config.SeverityMedium, 298 excluded: true, 299 }, 300 }, 301 wantNilErr: true, 302 }, 303 } 304 for _, tt := range tests { 305 t.Run(tt.name, func(t *testing.T) { 306 w, err := NewWriter(tt.rConfig) 307 if err != nil { 308 t.Fatalf("unable to create a report writer: %v", err) 309 } 310 got, err := w.parseReport(tt.report) 311 if (err == nil) != tt.wantNilErr { 312 t.Errorf("unexpected error value: %v", err) 313 } 314 diffOpts := []cmp.Option{ 315 cmp.AllowUnexported(vulnerability{}), 316 cmpopts.SortSlices(vulnLess), 317 } 318 if diff := cmp.Diff(tt.want, got, diffOpts...); diff != "" { 319 t.Errorf("vulnerabilities mismatch (-want +got):\n%v", diff) 320 } 321 }) 322 } 323 } 324 325 func TestWriter_isExcluded(t *testing.T) { 326 tests := []struct { 327 name string 328 vulnerability vreport.Vulnerability 329 target string 330 rConfig config.ReportConfig 331 want bool 332 wantNilErr bool 333 }{ 334 { 335 name: "empty exclusions", 336 vulnerability: vreport.Vulnerability{ 337 Summary: "Vulnerability Summary 1", 338 Score: 6.7, 339 }, 340 target: ".", 341 rConfig: config.ReportConfig{ 342 Exclusions: []config.Exclusion{}, 343 }, 344 want: false, 345 wantNilErr: true, 346 }, 347 { 348 name: "exclude by summary", 349 vulnerability: vreport.Vulnerability{ 350 Summary: "Vulnerability Summary 1", 351 Score: 6.7, 352 }, 353 target: ".", 354 rConfig: config.ReportConfig{ 355 Exclusions: []config.Exclusion{ 356 { 357 Summary: "Summary 1", 358 Description: "Excluded vulnerabilities Summary 1", 359 }, 360 }, 361 }, 362 want: true, 363 wantNilErr: true, 364 }, 365 { 366 name: "not exclude by summary", 367 vulnerability: vreport.Vulnerability{ 368 Summary: "Vulnerability Summary 1", 369 Score: 6.7, 370 }, 371 target: ".", 372 rConfig: config.ReportConfig{ 373 Exclusions: []config.Exclusion{ 374 { 375 Summary: "Summary 2", 376 Description: "Excluded vulnerabilities Summary 2", 377 }, 378 }, 379 }, 380 want: false, 381 wantNilErr: true, 382 }, 383 { 384 name: "exclude by fingerprint", 385 vulnerability: vreport.Vulnerability{ 386 Summary: "Vulnerability Summary 1", 387 Score: 6.7, 388 Fingerprint: "12345", 389 }, 390 target: ".", 391 rConfig: config.ReportConfig{ 392 Exclusions: []config.Exclusion{ 393 { 394 Fingerprint: "12345", 395 }, 396 }, 397 }, 398 want: true, 399 wantNilErr: true, 400 }, 401 { 402 name: "exclude by affected resource", 403 vulnerability: vreport.Vulnerability{ 404 Summary: "Vulnerability Summary 1", 405 Score: 6.7, 406 AffectedResource: "Resource 1", 407 }, 408 target: ".", 409 rConfig: config.ReportConfig{ 410 Exclusions: []config.Exclusion{ 411 { 412 Resource: "Resource 1", 413 }, 414 }, 415 }, 416 want: true, 417 wantNilErr: true, 418 }, 419 { 420 name: "exclude by affected resource string", 421 vulnerability: vreport.Vulnerability{ 422 Summary: "Vulnerability Summary 1", 423 Score: 6.7, 424 AffectedResourceString: "Resource String 1", 425 }, 426 target: ".", 427 rConfig: config.ReportConfig{ 428 Exclusions: []config.Exclusion{ 429 { 430 Resource: "Resource String 1", 431 }, 432 }, 433 }, 434 want: true, 435 wantNilErr: true, 436 }, 437 { 438 name: "exclude by target", 439 vulnerability: vreport.Vulnerability{ 440 Summary: "Vulnerability Summary 1", 441 Score: 6.7, 442 }, 443 target: ".", 444 rConfig: config.ReportConfig{ 445 Exclusions: []config.Exclusion{ 446 { 447 Target: ".", 448 }, 449 }, 450 }, 451 want: true, 452 wantNilErr: true, 453 }, 454 { 455 name: "match all exclusion criteria (resource)", 456 vulnerability: vreport.Vulnerability{ 457 Summary: "Vulnerability Summary 1", 458 Score: 6.7, 459 AffectedResource: "Resource 1", 460 Fingerprint: "12345", 461 }, 462 target: ".", 463 rConfig: config.ReportConfig{ 464 Exclusions: []config.Exclusion{ 465 { 466 Summary: "Summary 1", 467 Resource: "Resource 1", 468 Fingerprint: "12345", 469 Target: ".", 470 }, 471 }, 472 }, 473 want: true, 474 wantNilErr: true, 475 }, 476 { 477 name: "match all exclusion criteria (resource string)", 478 vulnerability: vreport.Vulnerability{ 479 Summary: "Vulnerability Summary 1", 480 Score: 6.7, 481 AffectedResourceString: "Resource String 1", 482 Fingerprint: "12345", 483 }, 484 target: ".", 485 rConfig: config.ReportConfig{ 486 Exclusions: []config.Exclusion{ 487 { 488 Summary: "Summary 1", 489 Resource: "Resource String 1", 490 Fingerprint: "12345", 491 Target: ".", 492 }, 493 }, 494 }, 495 want: true, 496 wantNilErr: true, 497 }, 498 { 499 name: "match all exclusion criteria (resource and resource string)", 500 vulnerability: vreport.Vulnerability{ 501 Summary: "Vulnerability Summary 1", 502 Score: 6.7, 503 AffectedResource: "Resource 1", 504 AffectedResourceString: "Resource String 1", 505 Fingerprint: "12345", 506 }, 507 target: ".", 508 rConfig: config.ReportConfig{ 509 Exclusions: []config.Exclusion{ 510 { 511 Summary: "Summary 1", 512 Resource: "Resource", 513 Fingerprint: "12345", 514 Target: ".", 515 }, 516 }, 517 }, 518 want: true, 519 wantNilErr: true, 520 }, 521 { 522 name: "fail an exclusion criteria", 523 vulnerability: vreport.Vulnerability{ 524 Summary: "Vulnerability Summary 1", 525 Score: 6.7, 526 AffectedResource: "Resource 1", 527 AffectedResourceString: "Resource String 1", 528 Fingerprint: "12345", 529 }, 530 target: ".", 531 rConfig: config.ReportConfig{ 532 Exclusions: []config.Exclusion{ 533 { 534 Summary: "Summary 1", 535 Resource: "not found", 536 Fingerprint: "12345", 537 Target: ".", 538 }, 539 }, 540 }, 541 want: false, 542 wantNilErr: true, 543 }, 544 } 545 for _, tt := range tests { 546 t.Run(tt.name, func(t *testing.T) { 547 w, err := NewWriter(tt.rConfig) 548 if err != nil { 549 t.Fatalf("unable to create a report writer: %v", err) 550 } 551 got, err := w.isExcluded(tt.vulnerability, tt.target) 552 if (err == nil) != tt.wantNilErr { 553 t.Errorf("unexpected error value: %v", err) 554 } 555 if got != tt.want { 556 t.Errorf("unexpected excluded value: got: %v, want: %v", got, tt.want) 557 } 558 }) 559 } 560 } 561 562 func TestMkSummary(t *testing.T) { 563 tests := []struct { 564 name string 565 vulnerabilities []vulnerability 566 want summary 567 wantNilErr bool 568 }{ 569 { 570 name: "happy path", 571 vulnerabilities: []vulnerability{ 572 { 573 Vulnerability: vreport.Vulnerability{ 574 Summary: "Vulnerability Summary 1", 575 }, 576 Severity: config.SeverityCritical, 577 excluded: false, 578 }, 579 { 580 Vulnerability: vreport.Vulnerability{ 581 Summary: "Vulnerability Summary 2", 582 }, 583 Severity: config.SeverityCritical, 584 excluded: true, 585 }, 586 { 587 Vulnerability: vreport.Vulnerability{ 588 Summary: "Vulnerability Summary 3", 589 }, 590 Severity: config.SeverityHigh, 591 excluded: false, 592 }, 593 { 594 Vulnerability: vreport.Vulnerability{ 595 Summary: "Vulnerability Summary 4", 596 }, 597 Severity: config.SeverityHigh, 598 excluded: true, 599 }, 600 { 601 Vulnerability: vreport.Vulnerability{ 602 Summary: "Vulnerability Summary 5", 603 }, 604 Severity: config.SeverityMedium, 605 excluded: false, 606 }, 607 { 608 Vulnerability: vreport.Vulnerability{ 609 Summary: "Vulnerability Summary 6", 610 }, 611 Severity: config.SeverityMedium, 612 excluded: true, 613 }, 614 { 615 Vulnerability: vreport.Vulnerability{ 616 Summary: "Vulnerability Summary 7", 617 }, 618 Severity: config.SeverityLow, 619 excluded: false, 620 }, 621 { 622 Vulnerability: vreport.Vulnerability{ 623 Summary: "Vulnerability Summary 8", 624 }, 625 Severity: config.SeverityLow, 626 excluded: true, 627 }, 628 { 629 Vulnerability: vreport.Vulnerability{ 630 Summary: "Vulnerability Summary 9", 631 }, 632 Severity: config.SeverityInfo, 633 excluded: false, 634 }, 635 { 636 Vulnerability: vreport.Vulnerability{ 637 Summary: "Vulnerability Summary 10", 638 }, 639 Severity: config.SeverityInfo, 640 excluded: true, 641 }, 642 }, 643 want: summary{ 644 count: map[config.Severity]int{ 645 config.SeverityCritical: 1, 646 config.SeverityHigh: 1, 647 config.SeverityMedium: 1, 648 config.SeverityLow: 1, 649 config.SeverityInfo: 1, 650 }, 651 excluded: 5, 652 }, 653 wantNilErr: true, 654 }, 655 { 656 name: "unknown severity", 657 vulnerabilities: []vulnerability{ 658 { 659 Vulnerability: vreport.Vulnerability{ 660 Summary: "Vulnerability Summary 1", 661 }, 662 Severity: 7, 663 excluded: false, 664 }, 665 }, 666 want: summary{ 667 count: nil, 668 excluded: 0, 669 }, 670 wantNilErr: false, 671 }, 672 { 673 name: "empty summary", 674 vulnerabilities: []vulnerability{}, 675 want: summary{ 676 count: nil, 677 excluded: 0, 678 }, 679 wantNilErr: true, 680 }, 681 } 682 for _, tt := range tests { 683 t.Run(tt.name, func(t *testing.T) { 684 got, err := mkSummary(tt.vulnerabilities) 685 if (err == nil) != tt.wantNilErr { 686 t.Errorf("unexpected error value: %v", err) 687 } 688 if diff := cmp.Diff(tt.want, got, cmp.AllowUnexported(summary{})); diff != "" { 689 t.Errorf("summary mismatch (-want +got):\n%v", diff) 690 } 691 }) 692 } 693 } 694 695 func TestWriter_filterVulns(t *testing.T) { 696 tests := []struct { 697 name string 698 vulnerabilities []vulnerability 699 rConfig config.ReportConfig 700 want []vulnerability 701 }{ 702 { 703 name: "filter excluded", 704 vulnerabilities: []vulnerability{ 705 { 706 Vulnerability: vreport.Vulnerability{ 707 Summary: "Vulnerability Summary 1", 708 }, 709 Severity: config.SeverityCritical, 710 excluded: false, 711 }, 712 { 713 Vulnerability: vreport.Vulnerability{ 714 Summary: "Vulnerability Summary 2", 715 }, 716 Severity: config.SeverityCritical, 717 excluded: true, 718 }, 719 { 720 Vulnerability: vreport.Vulnerability{ 721 Summary: "Vulnerability Summary 3", 722 }, 723 Severity: config.SeverityHigh, 724 excluded: false, 725 }, 726 { 727 Vulnerability: vreport.Vulnerability{ 728 Summary: "Vulnerability Summary 4", 729 }, 730 Severity: config.SeverityHigh, 731 excluded: true, 732 }, 733 { 734 Vulnerability: vreport.Vulnerability{ 735 Summary: "Vulnerability Summary 5", 736 }, 737 Severity: config.SeverityMedium, 738 excluded: false, 739 }, 740 { 741 Vulnerability: vreport.Vulnerability{ 742 Summary: "Vulnerability Summary 6", 743 }, 744 Severity: config.SeverityMedium, 745 excluded: true, 746 }, 747 { 748 Vulnerability: vreport.Vulnerability{ 749 Summary: "Vulnerability Summary 7", 750 }, 751 Severity: config.SeverityLow, 752 excluded: false, 753 }, 754 { 755 Vulnerability: vreport.Vulnerability{ 756 Summary: "Vulnerability Summary 8", 757 }, 758 Severity: config.SeverityLow, 759 excluded: true, 760 }, 761 { 762 Vulnerability: vreport.Vulnerability{ 763 Summary: "Vulnerability Summary 9", 764 }, 765 Severity: config.SeverityInfo, 766 excluded: false, 767 }, 768 { 769 Vulnerability: vreport.Vulnerability{ 770 Summary: "Vulnerability Summary 10", 771 }, 772 Severity: config.SeverityInfo, 773 excluded: true, 774 }, 775 }, 776 rConfig: config.ReportConfig{ 777 Severity: config.SeverityInfo, 778 }, 779 want: []vulnerability{ 780 { 781 Vulnerability: vreport.Vulnerability{ 782 Summary: "Vulnerability Summary 1", 783 }, 784 Severity: config.SeverityCritical, 785 excluded: false, 786 }, 787 { 788 Vulnerability: vreport.Vulnerability{ 789 Summary: "Vulnerability Summary 3", 790 }, 791 Severity: config.SeverityHigh, 792 excluded: false, 793 }, 794 { 795 Vulnerability: vreport.Vulnerability{ 796 Summary: "Vulnerability Summary 5", 797 }, 798 Severity: config.SeverityMedium, 799 excluded: false, 800 }, 801 { 802 Vulnerability: vreport.Vulnerability{ 803 Summary: "Vulnerability Summary 7", 804 }, 805 Severity: config.SeverityLow, 806 excluded: false, 807 }, 808 { 809 Vulnerability: vreport.Vulnerability{ 810 Summary: "Vulnerability Summary 9", 811 }, 812 Severity: config.SeverityInfo, 813 excluded: false, 814 }, 815 }, 816 }, 817 { 818 name: "filter excluded and lower than high", 819 vulnerabilities: []vulnerability{ 820 { 821 Vulnerability: vreport.Vulnerability{ 822 Summary: "Vulnerability Summary 1", 823 }, 824 Severity: config.SeverityCritical, 825 excluded: false, 826 }, 827 { 828 Vulnerability: vreport.Vulnerability{ 829 Summary: "Vulnerability Summary 2", 830 }, 831 Severity: config.SeverityCritical, 832 excluded: true, 833 }, 834 { 835 Vulnerability: vreport.Vulnerability{ 836 Summary: "Vulnerability Summary 3", 837 }, 838 Severity: config.SeverityHigh, 839 excluded: false, 840 }, 841 { 842 Vulnerability: vreport.Vulnerability{ 843 Summary: "Vulnerability Summary 4", 844 }, 845 Severity: config.SeverityHigh, 846 excluded: true, 847 }, 848 { 849 Vulnerability: vreport.Vulnerability{ 850 Summary: "Vulnerability Summary 5", 851 }, 852 Severity: config.SeverityMedium, 853 excluded: false, 854 }, 855 { 856 Vulnerability: vreport.Vulnerability{ 857 Summary: "Vulnerability Summary 6", 858 }, 859 Severity: config.SeverityMedium, 860 excluded: true, 861 }, 862 { 863 Vulnerability: vreport.Vulnerability{ 864 Summary: "Vulnerability Summary 7", 865 }, 866 Severity: config.SeverityLow, 867 excluded: false, 868 }, 869 { 870 Vulnerability: vreport.Vulnerability{ 871 Summary: "Vulnerability Summary 8", 872 }, 873 Severity: config.SeverityLow, 874 excluded: true, 875 }, 876 { 877 Vulnerability: vreport.Vulnerability{ 878 Summary: "Vulnerability Summary 9", 879 }, 880 Severity: config.SeverityInfo, 881 excluded: false, 882 }, 883 { 884 Vulnerability: vreport.Vulnerability{ 885 Summary: "Vulnerability Summary 10", 886 }, 887 Severity: config.SeverityInfo, 888 excluded: true, 889 }, 890 }, 891 rConfig: config.ReportConfig{ 892 Severity: config.SeverityHigh, 893 }, 894 want: []vulnerability{ 895 { 896 Vulnerability: vreport.Vulnerability{ 897 Summary: "Vulnerability Summary 1", 898 }, 899 Severity: config.SeverityCritical, 900 excluded: false, 901 }, 902 { 903 Vulnerability: vreport.Vulnerability{ 904 Summary: "Vulnerability Summary 3", 905 }, 906 Severity: config.SeverityHigh, 907 excluded: false, 908 }, 909 }, 910 }, 911 } 912 for _, tt := range tests { 913 t.Run(tt.name, func(t *testing.T) { 914 w, err := NewWriter(tt.rConfig) 915 if err != nil { 916 t.Fatalf("unable to create a report writer: %v", err) 917 } 918 got := w.filterVulns(tt.vulnerabilities) 919 if diff := cmp.Diff(tt.want, got, cmp.AllowUnexported(vulnerability{})); diff != "" { 920 t.Errorf("summary mismatch (-want +got):\n%v", diff) 921 } 922 }) 923 } 924 } 925 926 func TestNewWriter_OutputFile(t *testing.T) { 927 tests := []struct { 928 name string 929 report engine.Report 930 rConfig config.ReportConfig 931 wantExitCode ExitCode 932 wantNilErr bool 933 }{ 934 { 935 name: "Standard Output JSON Report", 936 report: map[string]vreport.Report{ 937 "CheckID1": { 938 CheckData: vreport.CheckData{ 939 CheckID: "CheckID1", 940 }, 941 ResultData: vreport.ResultData{ 942 Vulnerabilities: []vreport.Vulnerability{ 943 { 944 Summary: "Vulnerability Summary 1", 945 Description: "Lorem ipsum dolor sit amet, " + 946 "consectetur adipiscing elit. Nam malesuada " + 947 "pretium ligula, ac egestas leo egestas nec. " + 948 "Morbi id placerat ipsum. Donec semper enim urna, " + 949 "et bibendum ex dictum in. Quisque venenatis " + 950 "in sem in lacinia. Fusce lacus odio, molestie " + 951 "vitae mi nec, elementum pellentesque augue. " + 952 "Aenean imperdiet odio eu sodales molestie. " + 953 "Fusce ut elementum leo. Nam sodales molestie " + 954 "lorem in rutrum. Pellentesque nec sapien elit. " + 955 "Sed tincidunt ut augue sit amet cursus. " + 956 "In convallis magna sit amet tempus pellentesque. " + 957 "Nam commodo porttitor ante sed volutpat. " + 958 "Ut vulputate leo quis ultricies sodales.", 959 AffectedResource: "Affected Resource 1", 960 ImpactDetails: "Impact detail 1", 961 Recommendations: []string{ 962 "Recommendation 1", 963 "Recommendation 2", 964 "Recommendation 3", 965 }, 966 Details: "Vulnerability Detail 1", 967 References: []string{ 968 "Reference 1", 969 "Reference 2", 970 "Reference 3", 971 }, 972 Resources: []vreport.ResourcesGroup{ 973 { 974 Name: "Resource 1", 975 Header: []string{ 976 "Header 1", 977 "Header 2", 978 "Header 3", 979 "Header 4", 980 }, 981 Rows: []map[string]string{ 982 { 983 "Header 1": "row 11", 984 "Header 2": "row 12", 985 "Header 3": "row 13", 986 "Header 4": "row 14", 987 }, 988 { 989 "Header 1": "row 21", 990 "Header 2": "row 22", 991 "Header 3": "row 23", 992 "Header 4": "row 24", 993 }, 994 { 995 "Header 1": "row 31", 996 "Header 2": "row 32", 997 "Header 3": "row 33", 998 "Header 4": "row 34", 999 }, 1000 { 1001 "Header 1": "row 41", 1002 "Header 2": "row 42", 1003 "Header 3": "row 43", 1004 "Header 4": "row 44", 1005 }, 1006 }, 1007 }, 1008 { 1009 Name: "Resource 2", 1010 Header: []string{ 1011 "Header 1", 1012 "Header 2", 1013 }, 1014 Rows: []map[string]string{ 1015 { 1016 "Header 1": "row 11", 1017 "Header 2": "row 12", 1018 }, 1019 { 1020 "Header 1": "row 21", 1021 "Header 2": "row 22", 1022 }, 1023 }, 1024 }, 1025 { 1026 Name: "Resource 3", 1027 Header: []string{ 1028 "Header 1", 1029 "Header 2", 1030 }, 1031 Rows: []map[string]string{ 1032 { 1033 "Header 1": "row 11", 1034 "Header 2": "row 12", 1035 }, 1036 { 1037 "Header 1": "row 21", 1038 "Header 2": "row 22", 1039 }, 1040 }, 1041 }, 1042 { 1043 Name: "Resource 4", 1044 Header: []string{ 1045 "Header 1", 1046 "Header 2", 1047 }, 1048 Rows: []map[string]string{ 1049 { 1050 "Header 1": "row 11", 1051 "Header 2": "row 12", 1052 }, 1053 { 1054 "Header 1": "row 21", 1055 "Header 2": "row 22", 1056 }, 1057 }, 1058 }, 1059 }, 1060 }, 1061 }, 1062 }, 1063 }, 1064 }, 1065 rConfig: config.ReportConfig{ 1066 Severity: config.SeverityInfo, 1067 OutputFile: "test.json", 1068 Format: config.OutputFormatJSON, 1069 }, 1070 wantExitCode: ExitCodeInfo, 1071 wantNilErr: true, 1072 }, 1073 } 1074 for _, tt := range tests { 1075 t.Run(tt.name, func(t *testing.T) { 1076 tmpPath, err := os.MkdirTemp("", "") 1077 if err != nil { 1078 t.Fatalf("unable to create a temporary dir") 1079 } 1080 defer os.RemoveAll(tmpPath) 1081 1082 tt.rConfig.OutputFile = path.Join(tmpPath, tt.rConfig.OutputFile) 1083 writer, err := NewWriter(tt.rConfig) 1084 if err != nil { 1085 t.Fatalf("unable to create a report writer: %v", err) 1086 } 1087 defer writer.Close() 1088 gotExitCode, err := writer.Write(tt.report) 1089 if (err == nil) != tt.wantNilErr { 1090 t.Errorf("unexpected error value: %v", err) 1091 } 1092 if gotExitCode != tt.wantExitCode { 1093 t.Errorf("unexpected error value: got: %d, want: %d", gotExitCode, tt.wantExitCode) 1094 } 1095 1096 if _, err = os.Stat(tt.rConfig.OutputFile); err != nil { 1097 t.Fatalf("unexpected error value: %v", err) 1098 } 1099 }) 1100 } 1101 } 1102 1103 func vulnLess(a, b vulnerability) bool { 1104 h := func(v vulnerability) string { 1105 return fmt.Sprintf("%#v", v) 1106 } 1107 return h(a) < h(b) 1108 }