@@ -24,48 +24,51 @@ import (
24
24
)
25
25
26
26
const (
27
- failedCreatingSummary = "Failed creating summary"
28
- failedGettingScan = "Failed getting scan"
29
- failedListingResults = "Failed listing results"
30
- failedListingCodeBashing = "Failed codebashing link"
31
- mediumLabel = "medium"
32
- highLabel = "high"
33
- lowLabel = "low"
34
- infoLabel = "info"
35
- sonarTypeLabel = "_sonar"
36
- directoryPermission = 0700
37
- infoSonar = "INFO"
38
- lowSonar = "MINOR"
39
- mediumSonar = "MAJOR"
40
- highSonar = "CRITICAL"
41
- infoLowSarif = "note"
42
- mediumSarif = "warning"
43
- highSarif = "error"
44
- vulnerabilitySonar = "VULNERABILITY"
45
- infoCx = "INFO"
46
- lowCx = "LOW"
47
- mediumCx = "MEDIUM"
48
- highCx = "HIGH"
49
- codeBashingKey = "cb-url"
50
- failedGettingBfl = "Failed getting BFL"
51
- notAvailableString = "N/A"
52
- notAvailableNumber = - 1
53
- defaultPaddingSize = - 14
54
- scanPendingMessage = "Scan triggered in asynchronous mode or still running. Click more details to get the full status."
55
- scaType = "sca"
56
- directDependencyType = "Direct Dependency"
57
- indirectDependencyType = "Transitive Dependency"
58
- startedStatus = "started"
59
-
27
+ failedCreatingSummary = "Failed creating summary"
28
+ failedGettingScan = "Failed getting scan"
29
+ failedListingResults = "Failed listing results"
30
+ failedListingCodeBashing = "Failed codebashing link"
31
+ mediumLabel = "medium"
32
+ highLabel = "high"
33
+ lowLabel = "low"
34
+ infoLabel = "info"
35
+ sonarTypeLabel = "_sonar"
36
+ directoryPermission = 0700
37
+ infoSonar = "INFO"
38
+ lowSonar = "MINOR"
39
+ mediumSonar = "MAJOR"
40
+ highSonar = "CRITICAL"
41
+ infoLowSarif = "note"
42
+ mediumSarif = "warning"
43
+ highSarif = "error"
44
+ vulnerabilitySonar = "VULNERABILITY"
45
+ infoCx = "INFO"
46
+ lowCx = "LOW"
47
+ mediumCx = "MEDIUM"
48
+ highCx = "HIGH"
49
+ codeBashingKey = "cb-url"
50
+ failedGettingBfl = "Failed getting BFL"
51
+ notAvailableString = "N/A"
52
+ notAvailableNumber = - 1
53
+ defaultPaddingSize = - 14
54
+ scanPendingMessage = "Scan triggered in asynchronous mode or still running. Click more details to get the full status."
55
+ scaType = "sca"
56
+ directDependencyType = "Direct Dependency"
57
+ indirectDependencyType = "Transitive Dependency"
58
+ startedStatus = "started"
60
59
completedStatus = "completed"
60
+ exportingStatus = "Exporting"
61
+ pendingStatus = "Pending"
61
62
pdfToEmailFlagDescription = "Send the PDF report to the specified email address." +
62
63
" Use \" ,\" as the delimiter for multiple emails"
63
64
pdfOptionsFlagDescription = "Sections to generate PDF report. Available options: Iac-Security,Sast,Sca," +
64
65
defaultPdfOptionsDataSections
65
- delayValueForPdfReport = 150
66
+ sbomReportFlagDescription = "Sections to generate SBOM report. Available options: CycloneDxJson,CycloneDxXml,SpdxJson"
67
+ delayValueForReport = 150
66
68
reportNameScanReport = "scan-report"
67
69
reportTypeEmail = "email"
68
70
defaultPdfOptionsDataSections = "ScanSummary,ExecutiveSummary,ScanResults"
71
+ defaultSbomOption = "CycloneDxJson"
69
72
exploitablePathFlagDescription = "Enable or disable exploitable path in scan. Available options: true,false"
70
73
scaLastScanTimeFlagDescription = "SCA last scan time. Available options: integer above 1"
71
74
projectPrivatePackageFlagDescription = "Enable or disable project private package. Available options: true,false"
@@ -109,6 +112,7 @@ var sonarSeverities = map[string]string{
109
112
func NewResultsCommand (
110
113
resultsWrapper wrappers.ResultsWrapper ,
111
114
scanWrapper wrappers.ScansWrapper ,
115
+ resultsSbomWrapper wrappers.ResultsSbomWrapper ,
112
116
resultsPdfReportsWrapper wrappers.ResultsPdfWrapper ,
113
117
codeBashingWrapper wrappers.CodeBashingWrapper ,
114
118
bflWrapper wrappers.BflWrapper ,
@@ -125,7 +129,7 @@ func NewResultsCommand(
125
129
),
126
130
},
127
131
}
128
- showResultCmd := resultShowSubCommand (resultsWrapper , scanWrapper , resultsPdfReportsWrapper , risksOverviewWrapper )
132
+ showResultCmd := resultShowSubCommand (resultsWrapper , scanWrapper , resultsSbomWrapper , resultsPdfReportsWrapper , risksOverviewWrapper )
129
133
codeBashingCmd := resultCodeBashing (codeBashingWrapper )
130
134
bflResultCmd := resultBflSubCommand (bflWrapper )
131
135
resultCmd .AddCommand (
@@ -137,6 +141,7 @@ func NewResultsCommand(
137
141
func resultShowSubCommand (
138
142
resultsWrapper wrappers.ResultsWrapper ,
139
143
scanWrapper wrappers.ScansWrapper ,
144
+ resultsSbomWrapper wrappers.ResultsSbomWrapper ,
140
145
resultsPdfReportsWrapper wrappers.ResultsPdfWrapper ,
141
146
risksOverviewWrapper wrappers.RisksOverviewWrapper ,
142
147
) * cobra.Command {
@@ -149,7 +154,7 @@ func resultShowSubCommand(
149
154
$ cx results show --scan-id <scan Id>
150
155
` ,
151
156
),
152
- RunE : runGetResultCommand (resultsWrapper , scanWrapper , resultsPdfReportsWrapper , risksOverviewWrapper ),
157
+ RunE : runGetResultCommand (resultsWrapper , scanWrapper , resultsSbomWrapper , resultsPdfReportsWrapper , risksOverviewWrapper ),
153
158
}
154
159
addScanIDFlag (resultShowCmd , "ID to report on." )
155
160
addResultFormatFlag (
@@ -159,10 +164,12 @@ func resultShowSubCommand(
159
164
printer .FormatSummaryConsole ,
160
165
printer .FormatSarif ,
161
166
printer .FormatSummaryJSON ,
167
+ printer .FormatSbom ,
162
168
printer .FormatPDF ,
163
169
printer .FormatSummaryMarkdown ,
164
170
)
165
171
resultShowCmd .PersistentFlags ().String (commonParams .ReportFormatPdfToEmailFlag , "" , pdfToEmailFlagDescription )
172
+ resultShowCmd .PersistentFlags ().String (commonParams .ReportSbomFormatFlag , defaultSbomOption , sbomReportFlagDescription )
166
173
resultShowCmd .PersistentFlags ().String (commonParams .ReportFormatPdfOptionsFlag , defaultPdfOptionsDataSections , pdfOptionsFlagDescription )
167
174
resultShowCmd .PersistentFlags ().String (commonParams .TargetFlag , "cx_result" , "Output file" )
168
175
resultShowCmd .PersistentFlags ().String (commonParams .TargetPathFlag , "." , "Output Path" )
@@ -511,6 +518,7 @@ func generateScanSummaryURL(summary *wrappers.ResultSummary) string {
511
518
func runGetResultCommand (
512
519
resultsWrapper wrappers.ResultsWrapper ,
513
520
scanWrapper wrappers.ScansWrapper ,
521
+ resultsSbomWrapper wrappers.ResultsSbomWrapper ,
514
522
resultsPdfReportsWrapper wrappers.ResultsPdfWrapper ,
515
523
risksOverviewWrapper wrappers.RisksOverviewWrapper ,
516
524
) func (cmd * cobra.Command , args []string ) error {
@@ -520,6 +528,7 @@ func runGetResultCommand(
520
528
format , _ := cmd .Flags ().GetString (commonParams .TargetFormatFlag )
521
529
formatPdfToEmail , _ := cmd .Flags ().GetString (commonParams .ReportFormatPdfToEmailFlag )
522
530
formatPdfOptions , _ := cmd .Flags ().GetString (commonParams .ReportFormatPdfOptionsFlag )
531
+ formatSbomOptions , _ := cmd .Flags ().GetString (commonParams .ReportSbomFormatFlag )
523
532
524
533
scanID , _ := cmd .Flags ().GetString (commonParams .ScanIDFlag )
525
534
params , err := getFilters (cmd )
@@ -530,11 +539,13 @@ func runGetResultCommand(
530
539
resultsWrapper ,
531
540
risksOverviewWrapper ,
532
541
scanWrapper ,
542
+ resultsSbomWrapper ,
533
543
resultsPdfReportsWrapper ,
534
544
scanID ,
535
545
format ,
536
546
formatPdfToEmail ,
537
547
formatPdfOptions ,
548
+ formatSbomOptions ,
538
549
targetFile ,
539
550
targetPath ,
540
551
params )
@@ -585,11 +596,13 @@ func CreateScanReport(
585
596
resultsWrapper wrappers.ResultsWrapper ,
586
597
risksOverviewWrapper wrappers.RisksOverviewWrapper ,
587
598
scanWrapper wrappers.ScansWrapper ,
599
+ resultsSbomWrapper wrappers.ResultsSbomWrapper ,
588
600
resultsPdfReportsWrapper wrappers.ResultsPdfWrapper ,
589
601
scanID ,
590
602
reportTypes ,
591
603
formatPdfToEmail ,
592
604
formatPdfOptions ,
605
+ formatSbomOptions ,
593
606
targetFile ,
594
607
targetPath string ,
595
608
params map [string ]string ,
@@ -621,7 +634,7 @@ func CreateScanReport(
621
634
622
635
reportList := strings .Split (reportTypes , "," )
623
636
for _ , reportType := range reportList {
624
- err = createReport (reportType , formatPdfToEmail , formatPdfOptions , targetFile , targetPath , results , summary , resultsPdfReportsWrapper )
637
+ err = createReport (reportType , formatPdfToEmail , formatPdfOptions , formatSbomOptions , targetFile , targetPath , results , summary , resultsSbomWrapper , resultsPdfReportsWrapper )
625
638
if err != nil {
626
639
return err
627
640
}
@@ -674,10 +687,12 @@ func createReport(
674
687
format ,
675
688
formatPdfToEmail ,
676
689
formatPdfOptions ,
690
+ formatSbomOptions ,
677
691
targetFile ,
678
692
targetPath string ,
679
693
results * wrappers.ScanResultsCollection ,
680
694
summary * wrappers.ResultSummary ,
695
+ resultsSbomWrapper wrappers.ResultsSbomWrapper ,
681
696
resultsPdfReportsWrapper wrappers.ResultsPdfWrapper ,
682
697
683
698
) error {
@@ -719,8 +734,20 @@ func createReport(
719
734
convertNotAvailableNumberToZero (summary )
720
735
return writeMarkdownSummary (summaryRpt , summary )
721
736
}
722
- err := fmt .Errorf ("bad report format %s" , format )
723
- return err
737
+ if printer .IsFormat (format , printer .FormatSbom ) {
738
+ targetType := printer .FormatJSON
739
+ if strings .Contains (strings .ToLower (formatSbomOptions ), printer .FormatXML ) {
740
+ targetType = printer .FormatXML
741
+ }
742
+ summaryRpt := createTargetName (fmt .Sprintf ("%s_%s" , targetFile , printer .FormatSbom ), targetPath , targetType )
743
+ convertNotAvailableNumberToZero (summary )
744
+
745
+ if ! contains (summary .EnginesEnabled , scaType ) {
746
+ return fmt .Errorf ("to generate %s report, SCA engine must be enabled on scan summary" , printer .FormatSbom )
747
+ }
748
+ return exportSbomResults (resultsSbomWrapper , summaryRpt , summary , formatSbomOptions )
749
+ }
750
+ return fmt .Errorf ("bad report format %s" , format )
724
751
}
725
752
726
753
func createTargetName (targetFile , targetPath , targetType string ) string {
@@ -869,9 +896,47 @@ func exportJSONSummaryResults(targetFile string, results *wrappers.ResultSummary
869
896
return nil
870
897
}
871
898
899
+ func exportSbomResults (sbomWrapper wrappers.ResultsSbomWrapper , targetFile string , results * wrappers.ResultSummary , formatSbomOptions string ) error {
900
+ payload := & wrappers.SbomReportsPayload {
901
+ ScanID : results .ScanID ,
902
+ FileFormat : defaultSbomOption ,
903
+ }
904
+ if formatSbomOptions != "" && formatSbomOptions != defaultSbomOption {
905
+ format , err := validateSbomOptions (formatSbomOptions )
906
+ if err != nil {
907
+ return err
908
+ }
909
+ payload .FileFormat = format
910
+ }
911
+
912
+ pollingResp := & wrappers.SbomPollingResponse {}
913
+
914
+ sbomresp , err := sbomWrapper .GenerateSbomReport (payload )
915
+ if err != nil {
916
+ return err
917
+ }
918
+
919
+ log .Println ("Generating SBOM report with " + payload .FileFormat + " file format" )
920
+ pollingResp .ExportStatus = exportingStatus
921
+ for pollingResp .ExportStatus == exportingStatus || pollingResp .ExportStatus == pendingStatus {
922
+ pollingResp , err = sbomWrapper .GetSbomReportStatus (sbomresp .ExportID )
923
+ if err != nil {
924
+ return errors .Wrapf (err , "%s" , "failed getting SBOM report status" )
925
+ }
926
+ time .Sleep (delayValueForReport * time .Millisecond )
927
+ }
928
+ if ! strings .EqualFold (pollingResp .ExportStatus , completedStatus ) {
929
+ return errors .Errorf ("SBOM generating failed - Current status: %s" , pollingResp .ExportStatus )
930
+ }
931
+ err = sbomWrapper .DownloadSbomReport (pollingResp .ExportID , targetFile )
932
+ if err != nil {
933
+ return errors .Wrapf (err , "%s" , "Failed downloading SBOM report" )
934
+ }
935
+ return nil
936
+ }
872
937
func exportPdfResults (pdfWrapper wrappers.ResultsPdfWrapper , summary * wrappers.ResultSummary , summaryRpt , formatPdfToEmail , pdfOptions string ) error {
873
938
pdfReportsPayload := & wrappers.PdfReportsPayload {}
874
- poolingResp := & wrappers.PdfPoolingResponse {}
939
+ pollingResp := & wrappers.PdfPollingResponse {}
875
940
876
941
pdfOptionsSections , pdfOptionsEngines , err := validatePdfOptions (pdfOptions )
877
942
if err != nil {
@@ -910,16 +975,16 @@ func exportPdfResults(pdfWrapper wrappers.ResultsPdfWrapper, summary *wrappers.R
910
975
}
911
976
912
977
log .Println ("Generating PDF report" )
913
- poolingResp .Status = startedStatus
914
- for poolingResp .Status == startedStatus {
915
- poolingResp , webErr , err = pdfWrapper .CheckPdfReportStatus (pdfReportID .ReportID )
978
+ pollingResp .Status = startedStatus
979
+ for pollingResp .Status == startedStatus {
980
+ pollingResp , webErr , err = pdfWrapper .CheckPdfReportStatus (pdfReportID .ReportID )
916
981
if err != nil || webErr != nil {
917
982
return errors .Wrapf (err , "%v" , webErr )
918
983
}
919
- time .Sleep (delayValueForPdfReport * time .Millisecond )
984
+ time .Sleep (delayValueForReport * time .Millisecond )
920
985
}
921
- if poolingResp .Status != completedStatus {
922
- return errors .Errorf ("PDF generating failed - Current status: %s" , poolingResp .Status )
986
+ if pollingResp .Status != completedStatus {
987
+ return errors .Errorf ("PDF generating failed - Current status: %s" , pollingResp .Status )
923
988
}
924
989
err = pdfWrapper .DownloadPdfReport (pdfReportID .ReportID , summaryRpt )
925
990
if err != nil {
@@ -928,6 +993,19 @@ func exportPdfResults(pdfWrapper wrappers.ResultsPdfWrapper, summary *wrappers.R
928
993
return nil
929
994
}
930
995
996
+ func validateSbomOptions (sbomOption string ) (string , error ) {
997
+ var sbomOptionsStringMap = map [string ]string {
998
+ "cyclonedxjson" : "CycloneDxJson" ,
999
+ "cyclonedxxml" : "CycloneDxXml" ,
1000
+ "spdxjson" : "SpdxJson" ,
1001
+ }
1002
+ sbomOption = strings .ToLower (strings .ReplaceAll (sbomOption , " " , "" ))
1003
+ if sbomOptionsStringMap [sbomOption ] != "" {
1004
+ return sbomOptionsStringMap [sbomOption ], nil
1005
+ }
1006
+ return "" , errors .Errorf ("invalid SBOM option: %s" , sbomOption )
1007
+ }
1008
+
931
1009
func validatePdfOptions (pdfOptions string ) (pdfOptionsSections , pdfOptionsEngines []string , err error ) {
932
1010
var pdfOptionsSectionsMap = map [string ]string {
933
1011
"scansummary" : "ScanSummary" ,
0 commit comments