From 7a180ec2ec7a48327834a485ad03d9d2b2154b08 Mon Sep 17 00:00:00 2001 From: Eyal Delarea Date: Tue, 26 Sep 2023 18:02:38 +0300 Subject: [PATCH] Refactor Resultwriter (#929) --- xray/commands/audit/audit.go | 18 ++-- xray/commands/scan/buildscan.go | 18 +++- xray/commands/scan/scan.go | 20 ++-- xray/utils/resultstable.go | 12 +-- xray/utils/resultstable_test.go | 2 +- xray/utils/resultwriter.go | 161 +++++++++++++++++++++----------- 6 files changed, 149 insertions(+), 82 deletions(-) diff --git a/xray/commands/audit/audit.go b/xray/commands/audit/audit.go index 2303ef0a2..1347e5357 100644 --- a/xray/commands/audit/audit.go +++ b/xray/commands/audit/audit.go @@ -109,15 +109,15 @@ func (auditCmd *AuditCommand) Run() (err error) { // Print Scan results on all cases except if errors accrued on SCA scan and no security/license issues found. printScanResults := !(auditResults.ScaError != nil && xrayutils.IsEmptyScanResponse(auditResults.ExtendedScanResults.XrayResults)) if printScanResults { - err = xrayutils.PrintScanResults(auditResults.ExtendedScanResults, - nil, - auditCmd.OutputFormat(), - auditCmd.IncludeVulnerabilities, - auditCmd.IncludeLicenses, - auditResults.IsMultipleRootProject, - auditCmd.PrintExtendedTable, false, messages, - ) - if err != nil { + if err = xrayutils.NewResultsWriter(auditResults.ExtendedScanResults). + SetIsMultipleRootProject(auditResults.IsMultipleRootProject). + SetIncludeVulnerabilities(auditCmd.IncludeVulnerabilities). + SetIncludeLicenses(auditCmd.IncludeLicenses). + SetOutputFormat(auditCmd.OutputFormat()). + SetPrintExtendedTable(auditCmd.PrintExtendedTable). + SetExtraMessages(messages). + SetScanType(services.Dependency). + PrintScanResults(); err != nil { return } } diff --git a/xray/commands/scan/buildscan.go b/xray/commands/scan/buildscan.go index 7dd6bdcd0..ec9f9a37e 100644 --- a/xray/commands/scan/buildscan.go +++ b/xray/commands/scan/buildscan.go @@ -128,22 +128,30 @@ func (bsc *BuildScanCommand) runBuildScanAndPrintResults(xrayManager *xray.XrayS extendedScanResults := &xrutils.ExtendedScanResults{XrayResults: scanResponse} + resultsPrinter := xrutils.NewResultsWriter(extendedScanResults). + SetOutputFormat(bsc.outputFormat). + SetIncludeVulnerabilities(bsc.includeVulnerabilities). + SetIncludeLicenses(false). + SetIsMultipleRootProject(true). + SetPrintExtendedTable(bsc.printExtendedTable). + SetScanType(services.Binary). + SetExtraMessages(nil) + if bsc.outputFormat != xrutils.Table { // Print the violations and/or vulnerabilities as part of one JSON. - err = xrutils.PrintScanResults(extendedScanResults, nil, bsc.outputFormat, false, false, false, bsc.printExtendedTable, true, nil) + err = resultsPrinter.PrintScanResults() } else { // Print two different tables for violations and vulnerabilities (if needed) // If "No Xray Fail build policy...." error received, no need to print violations if !noFailBuildPolicy { - err = xrutils.PrintScanResults(extendedScanResults, nil, bsc.outputFormat, false, false, false, bsc.printExtendedTable, true, nil) - if err != nil { + if err = resultsPrinter.PrintScanResults(); err != nil { return false, err } } if bsc.includeVulnerabilities { - err = xrutils.PrintScanResults(extendedScanResults, nil, bsc.outputFormat, true, false, false, bsc.printExtendedTable, true, nil) - if err != nil { + resultsPrinter.SetIncludeVulnerabilities(true) + if err = resultsPrinter.PrintScanResults(); err != nil { return false, err } } diff --git a/xray/commands/scan/scan.go b/xray/commands/scan/scan.go index b77a3c069..cd910ea0a 100644 --- a/xray/commands/scan/scan.go +++ b/xray/commands/scan/scan.go @@ -242,14 +242,18 @@ func (scanCmd *ScanCommand) Run() (err error) { scanErrors = appendErrorSlice(scanErrors, fileProducerErrors) scanErrors = appendErrorSlice(scanErrors, indexedFileProducerErrors) extendedScanResults := &xrutils.ExtendedScanResults{XrayResults: flatResults} - err = xrutils.PrintScanResults(extendedScanResults, - scanErrors, - scanCmd.outputFormat, - scanCmd.includeVulnerabilities, - scanCmd.includeLicenses, - true, - scanCmd.printExtendedTable, true, nil, - ) + + if err = xrutils.NewResultsWriter(extendedScanResults). + SetOutputFormat(scanCmd.outputFormat). + SetIncludeVulnerabilities(scanCmd.includeVulnerabilities). + SetIncludeLicenses(scanCmd.includeLicenses). + SetPrintExtendedTable(scanCmd.printExtendedTable). + SetIsMultipleRootProject(true). + SetScanType(services.Binary). + PrintScanResults(); err != nil { + return + } + if err != nil { return err } diff --git a/xray/utils/resultstable.go b/xray/utils/resultstable.go index 27fc58ccd..0b308b9ff 100644 --- a/xray/utils/resultstable.go +++ b/xray/utils/resultstable.go @@ -37,13 +37,13 @@ const ( // In case one (or more) of the violations contains the field FailBuild set to true, CliError with exit code 3 will be returned. // Set printExtended to true to print fields with 'extended' tag. // If the scan argument is set to true, print the scan tables. -func PrintViolationsTable(violations []services.Violation, extendedResults *ExtendedScanResults, multipleRoots, printExtended, isBinaryScan bool) error { +func PrintViolationsTable(violations []services.Violation, extendedResults *ExtendedScanResults, multipleRoots, printExtended bool, scanType services.ScanType) error { securityViolationsRows, licenseViolationsRows, operationalRiskViolationsRows, err := prepareViolations(violations, extendedResults, multipleRoots, true, true) if err != nil { return err } // Print tables, if scan is true; print the scan tables. - if isBinaryScan { + if scanType == services.Binary { err = coreutils.PrintTable(formats.ConvertToVulnerabilityScanTableRow(securityViolationsRows), "Security Violations", "No security violations were found", printExtended) if err != nil { return err @@ -182,13 +182,13 @@ func prepareViolations(violations []services.Violation, extendedResults *Extende // In case multipleRoots is true, the field Component will show the root of each impact path, otherwise it will show the root's child. // Set printExtended to true to print fields with 'extended' tag. // If the scan argument is set to true, print the scan tables. -func PrintVulnerabilitiesTable(vulnerabilities []services.Vulnerability, extendedResults *ExtendedScanResults, multipleRoots, printExtended, isBinaryScan bool) error { +func PrintVulnerabilitiesTable(vulnerabilities []services.Vulnerability, extendedResults *ExtendedScanResults, multipleRoots, printExtended bool, scanType services.ScanType) error { vulnerabilitiesRows, err := prepareVulnerabilities(vulnerabilities, extendedResults, multipleRoots, true, true) if err != nil { return err } - if isBinaryScan { + if scanType == services.Binary { return coreutils.PrintTable(formats.ConvertToVulnerabilityScanTableRow(vulnerabilitiesRows), "Vulnerable Components", "✨ No vulnerable components were found ✨", printExtended) } var emptyTableMessage string @@ -266,12 +266,12 @@ func sortVulnerabilityOrViolationRows(rows []formats.VulnerabilityOrViolationRow // In case multipleRoots is true, the field Component will show the root of each impact path, otherwise it will show the root's child. // Set printExtended to true to print fields with 'extended' tag. // If the scan argument is set to true, print the scan tables. -func PrintLicensesTable(licenses []services.License, printExtended, isBinaryScan bool) error { +func PrintLicensesTable(licenses []services.License, printExtended bool, scanType services.ScanType) error { licensesRows, err := PrepareLicenses(licenses) if err != nil { return err } - if isBinaryScan { + if scanType == services.Binary { return coreutils.PrintTable(formats.ConvertToLicenseScanTableRow(licensesRows), "Licenses", "No licenses were found", printExtended) } return coreutils.PrintTable(formats.ConvertToLicenseTableRow(licensesRows), "Licenses", "No licenses were found", printExtended) diff --git a/xray/utils/resultstable_test.go b/xray/utils/resultstable_test.go index 3606c1b8a..d0589fe24 100644 --- a/xray/utils/resultstable_test.go +++ b/xray/utils/resultstable_test.go @@ -25,7 +25,7 @@ func TestPrintViolationsTable(t *testing.T) { } for _, test := range tests { - err := PrintViolationsTable(test.violations, &ExtendedScanResults{}, false, true, true) + err := PrintViolationsTable(test.violations, &ExtendedScanResults{}, false, true, services.Binary) assert.NoError(t, err) if CheckIfFailBuild([]services.ScanResponse{{Violations: test.violations}}) { err = NewFailBuildError() diff --git a/xray/utils/resultwriter.go b/xray/utils/resultwriter.go index 1e8abcca4..c4fd62fdc 100644 --- a/xray/utils/resultwriter.go +++ b/xray/utils/resultwriter.go @@ -34,32 +34,88 @@ var OutputFormats = []string{string(Table), string(Json), string(SimpleJson), st var CurationOutputFormats = []string{string(Table), string(Json)} +type ResultsWriter struct { + // The scan results. + results *ExtendedScanResults + // SimpleJsonError Errors to be added to output of the SimpleJson format. + simpleJsonError []formats.SimpleJsonError + // Format The output format. + format OutputFormat + // IncludeVulnerabilities If true, include all vulnerabilities as part of the output. Else, include violations only. + includeVulnerabilities bool + // IncludeLicenses If true, also include license violations as part of the output. + includeLicenses bool + // IsMultipleRoots multipleRoots is set to true, in case the given results array contains (or may contain) results of several projects (like in binary scan). + isMultipleRoots bool + // PrintExtended, If true, show extended results. + printExtended bool + // The scanType (binary,dependency) + scanType services.ScanType + // Messages - Option array of messages, to be displayed if the format is Table + messages []string +} + +func NewResultsWriter(extendedScanResults *ExtendedScanResults) *ResultsWriter { + return &ResultsWriter{results: extendedScanResults} +} + +func (rw *ResultsWriter) SetOutputFormat(format OutputFormat) *ResultsWriter { + rw.format = format + return rw +} + +func (rw *ResultsWriter) SetScanType(scanType services.ScanType) *ResultsWriter { + rw.scanType = scanType + return rw +} + +func (rw *ResultsWriter) SetSimpleJsonError(jsonErrors []formats.SimpleJsonError) *ResultsWriter { + rw.simpleJsonError = jsonErrors + return rw +} + +func (rw *ResultsWriter) SetIncludeVulnerabilities(includeVulnerabilities bool) *ResultsWriter { + rw.includeVulnerabilities = includeVulnerabilities + return rw +} + +func (rw *ResultsWriter) SetIncludeLicenses(licenses bool) *ResultsWriter { + rw.includeLicenses = licenses + return rw +} + +func (rw *ResultsWriter) SetIsMultipleRootProject(isMultipleRootProject bool) *ResultsWriter { + rw.isMultipleRoots = isMultipleRootProject + return rw +} + +func (rw *ResultsWriter) SetPrintExtendedTable(extendedTable bool) *ResultsWriter { + rw.printExtended = extendedTable + return rw +} + +func (rw *ResultsWriter) SetExtraMessages(messages []string) *ResultsWriter { + rw.messages = messages + return rw + +} + // PrintScanResults prints the scan results in the specified format. // Note that errors are printed only with SimpleJson format. -// -// results - The scan results. -// simpleJsonError - Errors to be added to output of the SimpleJson format. -// format - The output format. -// includeVulnerabilities - If trie, include all vulnerabilities as part of the output. Else, include violations only. -// includeLicenses - If true, also include license violations as part of the output. -// isMultipleRoots - multipleRoots is set to true, in case the given results array contains (or may contain) results of several projects (like in binary scan). -// printExtended -If true, show extended results. -// scan - If true, use an output layout suitable for `jf scan` or `jf docker scan` results. Otherwise, use a layout compatible for `jf audit` . -// messages - Option array of messages, to be displayed if the format is Table -func PrintScanResults(results *ExtendedScanResults, simpleJsonError []formats.SimpleJsonError, format OutputFormat, includeVulnerabilities, includeLicenses, isMultipleRoots, printExtended, isBinaryScan bool, messages []string) error { - switch format { +func (rw *ResultsWriter) PrintScanResults() error { + switch rw.format { case Table: - return printScanResultsTables(results, isBinaryScan, includeVulnerabilities, includeLicenses, isMultipleRoots, printExtended, messages) + return rw.printScanResultsTables() case SimpleJson: - jsonTable, err := convertScanToSimpleJson(results, simpleJsonError, isMultipleRoots, includeLicenses, false) + jsonTable, err := rw.convertScanToSimpleJson() if err != nil { return err } return PrintJson(jsonTable) case Json: - return PrintJson(results.getXrayScanResults()) + return PrintJson(rw.results.getXrayScanResults()) case Sarif: - sarifFile, err := GenerateSarifContentFromResults(results, isMultipleRoots, includeLicenses, false) + sarifFile, err := rw.generateSarifContentFromResults(false) if err != nil { return err } @@ -67,41 +123,40 @@ func PrintScanResults(results *ExtendedScanResults, simpleJsonError []formats.Si } return nil } - -func printScanResultsTables(results *ExtendedScanResults, isBinaryScan, includeVulnerabilities, includeLicenses, isMultipleRoots, printExtended bool, messages []string) (err error) { - printMessages(messages) - violations, vulnerabilities, licenses := SplitScanResults(results.getXrayScanResults()) - if len(results.getXrayScanResults()) > 0 { +func (rw *ResultsWriter) printScanResultsTables() (err error) { + printMessages(rw.messages) + violations, vulnerabilities, licenses := SplitScanResults(rw.results.getXrayScanResults()) + if len(rw.results.getXrayScanResults()) > 0 { var resultsPath string - if resultsPath, err = writeJsonResults(results); err != nil { + if resultsPath, err = writeJsonResults(rw.results); err != nil { return } printMessage(coreutils.PrintTitle("The full scan results are available here: ") + coreutils.PrintLink(resultsPath)) } log.Output() - if includeVulnerabilities { - err = PrintVulnerabilitiesTable(vulnerabilities, results, isMultipleRoots, printExtended, isBinaryScan) + if rw.includeVulnerabilities { + err = PrintVulnerabilitiesTable(vulnerabilities, rw.results, rw.isMultipleRoots, rw.printExtended, rw.scanType) } else { - err = PrintViolationsTable(violations, results, isMultipleRoots, printExtended, isBinaryScan) + err = PrintViolationsTable(violations, rw.results, rw.isMultipleRoots, rw.printExtended, rw.scanType) } if err != nil { return } - if includeLicenses { - if err = PrintLicensesTable(licenses, printExtended, isBinaryScan); err != nil { + if rw.includeLicenses { + if err = PrintLicensesTable(licenses, rw.printExtended, rw.scanType); err != nil { return } } - if err = PrintSecretsTable(results.SecretsScanResults, results.EntitledForJas); err != nil { + if err = PrintSecretsTable(rw.results.SecretsScanResults, rw.results.EntitledForJas); err != nil { return } - if err = PrintIacTable(results.IacScanResults, results.EntitledForJas); err != nil { + if err = PrintIacTable(rw.results.IacScanResults, rw.results.EntitledForJas); err != nil { return } if !IsSastSupported() { return } - return PrintSastTable(results.SastScanResults, results.EntitledForJas) + return PrintSastTable(rw.results.SastScanResults, rw.results.EntitledForJas) } func printMessages(messages []string) { @@ -117,21 +172,21 @@ func printMessage(message string) { log.Output("💬" + message) } -func GenerateSarifContentFromResults(extendedResults *ExtendedScanResults, isMultipleRoots, includeLicenses, markdownOutput bool) (sarifStr string, err error) { +func (rw *ResultsWriter) generateSarifContentFromResults(markdownOutput bool) (sarifStr string, err error) { report, err := NewReport() if err != nil { return } - xrayRun, err := convertXrayResponsesToSarifRun(extendedResults, isMultipleRoots, includeLicenses, markdownOutput) + xrayRun, err := rw.convertXrayResponsesToSarifRun(markdownOutput) if err != nil { return } report.Runs = append(report.Runs, xrayRun) - report.Runs = append(report.Runs, extendedResults.ApplicabilityScanResults...) - report.Runs = append(report.Runs, extendedResults.IacScanResults...) - report.Runs = append(report.Runs, extendedResults.SecretsScanResults...) - report.Runs = append(report.Runs, extendedResults.SastScanResults...) + report.Runs = append(report.Runs, rw.results.ApplicabilityScanResults...) + report.Runs = append(report.Runs, rw.results.IacScanResults...) + report.Runs = append(report.Runs, rw.results.SecretsScanResults...) + report.Runs = append(report.Runs, rw.results.SastScanResults...) out, err := json.Marshal(report) if err != nil { @@ -141,13 +196,13 @@ func GenerateSarifContentFromResults(extendedResults *ExtendedScanResults, isMul return clientUtils.IndentJson(out), nil } -func convertXrayResponsesToSarifRun(extendedResults *ExtendedScanResults, isMultipleRoots, includeLicenses, markdownOutput bool) (run *sarif.Run, err error) { - xrayJson, err := convertXrayScanToSimpleJson(extendedResults, isMultipleRoots, includeLicenses, true) +func (rw *ResultsWriter) convertXrayResponsesToSarifRun(markdownOutput bool) (run *sarif.Run, err error) { + xrayJson, err := rw.convertXrayScanToSimpleJson(true) if err != nil { return } xrayRun := sarif.NewRunWithInformationURI("JFrog Xray Sca", "https://jfrog.com/xray/") - xrayRun.Tool.Driver.Version = &extendedResults.XrayVersion + xrayRun.Tool.Driver.Version = &rw.results.XrayVersion if len(xrayJson.Vulnerabilities) > 0 || len(xrayJson.SecurityViolations) > 0 { if err = extractXrayIssuesToSarifRun(xrayRun, xrayJson, markdownOutput); err != nil { return @@ -243,18 +298,18 @@ func addResultToSarifRun(issueId, msg, severity string, location *sarif.Location return } -func convertXrayScanToSimpleJson(extendedResults *ExtendedScanResults, isMultipleRoots, includeLicenses, simplifiedOutput bool) (formats.SimpleJsonResults, error) { - violations, vulnerabilities, licenses := SplitScanResults(extendedResults.XrayResults) +func (rw *ResultsWriter) convertXrayScanToSimpleJson(simplifiedOutput bool) (formats.SimpleJsonResults, error) { + violations, vulnerabilities, licenses := SplitScanResults(rw.results.XrayResults) jsonTable := formats.SimpleJsonResults{} if len(vulnerabilities) > 0 { - vulJsonTable, err := PrepareVulnerabilities(vulnerabilities, extendedResults, isMultipleRoots, simplifiedOutput) + vulJsonTable, err := PrepareVulnerabilities(vulnerabilities, rw.results, rw.isMultipleRoots, simplifiedOutput) if err != nil { return formats.SimpleJsonResults{}, err } jsonTable.Vulnerabilities = vulJsonTable } if len(violations) > 0 { - secViolationsJsonTable, licViolationsJsonTable, opRiskViolationsJsonTable, err := PrepareViolations(violations, extendedResults, isMultipleRoots, simplifiedOutput) + secViolationsJsonTable, licViolationsJsonTable, opRiskViolationsJsonTable, err := PrepareViolations(violations, rw.results, rw.isMultipleRoots, simplifiedOutput) if err != nil { return formats.SimpleJsonResults{}, err } @@ -262,7 +317,7 @@ func convertXrayScanToSimpleJson(extendedResults *ExtendedScanResults, isMultipl jsonTable.LicensesViolations = licViolationsJsonTable jsonTable.OperationalRiskViolations = opRiskViolationsJsonTable } - if includeLicenses { + if rw.includeLicenses { licJsonTable, err := PrepareLicenses(licenses) if err != nil { return formats.SimpleJsonResults{}, err @@ -273,21 +328,21 @@ func convertXrayScanToSimpleJson(extendedResults *ExtendedScanResults, isMultipl return jsonTable, nil } -func convertScanToSimpleJson(extendedResults *ExtendedScanResults, errors []formats.SimpleJsonError, isMultipleRoots, includeLicenses, simplifiedOutput bool) (formats.SimpleJsonResults, error) { - jsonTable, err := convertXrayScanToSimpleJson(extendedResults, isMultipleRoots, includeLicenses, simplifiedOutput) +func (rw *ResultsWriter) convertScanToSimpleJson() (formats.SimpleJsonResults, error) { + jsonTable, err := rw.convertXrayScanToSimpleJson(false) if err != nil { return formats.SimpleJsonResults{}, err } - if len(extendedResults.SecretsScanResults) > 0 { - jsonTable.Secrets = PrepareSecrets(extendedResults.SecretsScanResults) + if len(rw.results.SecretsScanResults) > 0 { + jsonTable.Secrets = PrepareSecrets(rw.results.SecretsScanResults) } - if len(extendedResults.IacScanResults) > 0 { - jsonTable.Iacs = PrepareIacs(extendedResults.IacScanResults) + if len(rw.results.IacScanResults) > 0 { + jsonTable.Iacs = PrepareIacs(rw.results.IacScanResults) } - if len(extendedResults.SastScanResults) > 0 { - jsonTable.Sast = PrepareSast(extendedResults.SastScanResults) + if len(rw.results.SastScanResults) > 0 { + jsonTable.Sast = PrepareSast(rw.results.SastScanResults) } - jsonTable.Errors = errors + jsonTable.Errors = rw.simpleJsonError return jsonTable, nil }