From c50a74a416586ab1fb28a5d0a4de640d8c640827 Mon Sep 17 00:00:00 2001 From: Jeff Armstrong Date: Wed, 1 Sep 2021 03:37:03 -0700 Subject: [PATCH] Scan Results (#262) * - All results processing is handled through "results.go" now. - The (--report-format) flag determines what kind of report to create: json, sarif, summaryHTML, summaryConsole. - The (--output-name) flag determines where to write the report too, if not provided an appropriate default file name will be used, example: report.html. - If (--report-format) is provided to (scan create) them a report file will be generated. * - Now creates files based on --output-name, and --output-path arguments. The name of each report file will be determined by the type of report and extensions are added automatically. - The --report-formats options now allow the user to pass multiple report types as once as a comma-separated list. - When creating multiple reports the report data will only be downloaded and prepared once. --- internal/commands/result.go | 270 ++++++++++++++---------------- internal/commands/result_test.go | 20 ++- internal/commands/root.go | 10 +- internal/commands/scan.go | 40 +++-- internal/commands/util/printer.go | 14 +- internal/wrappers/results-json.go | 8 +- test/integration/result_test.go | 154 ++--------------- test/integration/scan_test.go | 2 +- test/integration/util_command.go | 17 +- 9 files changed, 208 insertions(+), 327 deletions(-) diff --git a/internal/commands/result.go b/internal/commands/result.go index da12b20af..88a23b47d 100644 --- a/internal/commands/result.go +++ b/internal/commands/result.go @@ -1,10 +1,8 @@ package commands import ( - "bytes" "encoding/json" "fmt" - "io" "os" "strings" "text/template" @@ -26,6 +24,9 @@ const ( highLabel = "high" lowLabel = "low" sastTypeFlag = "sast" + sastTypeLabel = "sast" + kicsTypeLabel = "infrastructure" + scaTypeLabel = "dependency" ) var ( @@ -54,28 +55,13 @@ func NewResultCommand(resultsWrapper wrappers.ResultsWrapper) *cobra.Command { resultCmd := &cobra.Command{ Use: "result", Short: "Retrieve results", + RunE: runGetResultCommand(resultsWrapper), } - - listResultsCmd := &cobra.Command{ - Use: "list ", - Short: "List results for a given scan", - RunE: runGetResultByScanIDCommand(resultsWrapper), - } - listResultsCmd.PersistentFlags().StringSlice(FilterFlag, []string{}, filterResultsListFlagUsage) - addFormatFlag(listResultsCmd, util.FormatList, util.FormatJSON) - addScanIDFlag(listResultsCmd, "ID to report on.") - - summaryCmd := &cobra.Command{ - Use: "summary", - Short: "Creates summary report for scan", - RunE: runGetSummaryByScanIDCommand(resultsWrapper), - } - summaryCmd.PersistentFlags().StringSlice(FilterFlag, []string{}, filterResultsListFlagUsage) - addFormatFlag(summaryCmd, util.FormatHTML, util.FormatText) - addScanIDFlag(summaryCmd, "ID to report on.") - summaryCmd.PersistentFlags().String(TargetFlag, "console", "Output file") - - resultCmd.AddCommand(listResultsCmd, summaryCmd) + addScanIDFlag(resultCmd, "ID to report on.") + addResultFormatFlag(resultCmd, util.FormatJSON, util.FormatSummary, util.FormatSummaryConsole, util.FormatSarif) + resultCmd.PersistentFlags().String(TargetFlag, "", "Output file") + resultCmd.PersistentFlags().String(TargetPathFlag, ".", "Output Path") + resultCmd.PersistentFlags().StringSlice(FilterFlag, []string{}, filterResultsListFlagUsage) return resultCmd } @@ -107,30 +93,6 @@ func getScanInfo(scanID string) (*ResultSummary, error) { return nil, err } -func runGetSummaryByScanIDCommand(resultsWrapper wrappers.ResultsWrapper) func( - cmd *cobra.Command, - args []string, -) error { - return func(cmd *cobra.Command, args []string) error { - targetFile, _ := cmd.Flags().GetString(TargetFlag) - scanID, _ := cmd.Flags().GetString(ScanIDFlag) - format, _ := cmd.Flags().GetString(FormatFlag) - params, err := getFilters(cmd) - if err != nil { - return errors.Wrapf(err, "%s", failedListingResults) - } - results, err := ReadResults(resultsWrapper, scanID, params) - if err == nil { - summary, sumErr := SummaryReport(results, scanID) - if sumErr == nil { - writeSummary(cmd.OutOrStdout(), targetFile, summary, format) - } - return sumErr - } - return err - } -} - func SummaryReport(results *wrappers.ScanResultsCollection, scanID string) (*ResultSummary, error) { summary, err := getScanInfo(scanID) if err != nil { @@ -138,19 +100,29 @@ func SummaryReport(results *wrappers.ScanResultsCollection, scanID string) (*Res } summary.TotalIssues = int(results.TotalCount) for _, result := range results.Results { - if result.Severity == "HIGH" { + if result.Type == sastTypeLabel { + summary.SastIssues++ + } + if result.Type == scaTypeLabel { + summary.ScaIssues++ + } + if result.Type == kicsTypeLabel { + summary.KicsIssues++ + } + severity := strings.ToLower(result.Severity) + if severity == highLabel { summary.HighIssues++ summary.RiskStyle = highLabel summary.RiskMsg = "High Risk" } - if result.Severity == "LOW" { + if severity == lowLabel { summary.LowIssues++ if summary.RiskStyle != highLabel && summary.RiskStyle != mediumLabel { summary.RiskStyle = lowLabel summary.RiskMsg = "Low Risk" } } - if result.Severity == mediumLabel { + if severity == mediumLabel { summary.MediumIssues++ if summary.RiskStyle != highLabel { summary.RiskStyle = mediumLabel @@ -161,82 +133,105 @@ func SummaryReport(results *wrappers.ScanResultsCollection, scanID string) (*Res return summary, nil } -func writeSummary(w io.Writer, targetFile string, summary *ResultSummary, format string) { +func writeHTMLSummary(targetFile string, summary *ResultSummary) error { + fmt.Println("Creating Summary Report: ", targetFile) summaryTemp, err := template.New("summaryTemplate").Parse(summaryTemplate) if err == nil { - if targetFile == "console" { - if format == util.FormatHTML { - buffer := new(bytes.Buffer) - _ = summaryTemp.ExecuteTemplate(buffer, "SummaryTemplate", summary) - _, _ = fmt.Fprintln(w, buffer) - } else { - writeTextSummary(w, "", summary) - } - } else { - if format == util.FormatHTML { - f, err := os.Create(targetFile) - if err == nil { - _ = summaryTemp.ExecuteTemplate(f, "SummaryTemplate", summary) - _ = f.Close() - } - } else { - writeTextSummary(w, targetFile, summary) - } - } - } -} - -func writeTextSummary(w io.Writer, targetFile string, summary *ResultSummary) { - if targetFile != "" { f, err := os.Create(targetFile) if err == nil { - sumMsg := "" - sumMsg += fmt.Sprintf(" Created At: %s\n", summary.CreatedAt) - - sumMsg += fmt.Sprintf(" Risk: %s\n", summary.RiskMsg) - sumMsg += fmt.Sprintf(" Project ID: %s\n", summary.ProjectID) - sumMsg += fmt.Sprintf(" Scan ID: %s\n", summary.ScanID) - sumMsg += fmt.Sprintf(" Total Issues: %d\n", summary.TotalIssues) - sumMsg += fmt.Sprintf(" High Issues: %d\n", summary.HighIssues) - sumMsg += fmt.Sprintf(" Medium Issues: %d\n", summary.MediumIssues) - sumMsg += fmt.Sprintf(" Low Issues: %d\n", summary.LowIssues) - sumMsg += fmt.Sprintf(" SAST Issues: %d\n", summary.SastIssues) - sumMsg += fmt.Sprintf(" KICS Issues: %d\n", summary.KicsIssues) - sumMsg += fmt.Sprintf(" SCA Issues: %d\n", summary.ScaIssues) - _, _ = f.WriteString(sumMsg) + _ = summaryTemp.ExecuteTemplate(f, "SummaryTemplate", summary) _ = f.Close() } - } else { - _, _ = fmt.Fprintf(w, " Created At: %s\n", summary.CreatedAt) - _, _ = fmt.Fprintf(w, " Risk: %s\n", summary.RiskMsg) - _, _ = fmt.Fprintf(w, " Project ID: %s\n", summary.ProjectID) - _, _ = fmt.Fprintf(w, " Scan ID: %s\n", summary.ScanID) - _, _ = fmt.Fprintf(w, " Total Issues: %d\n", summary.TotalIssues) - _, _ = fmt.Fprintf(w, " High Issues: %d\n", summary.HighIssues) - _, _ = fmt.Fprintf(w, " Medium Issues: %d\n", summary.MediumIssues) - _, _ = fmt.Fprintf(w, " Low Issues: %d\n", summary.LowIssues) - _, _ = fmt.Fprintf(w, " SAST Issues: %d\n", summary.SastIssues) - _, _ = fmt.Fprintf(w, " KICS Issues: %d\n", summary.KicsIssues) - _, _ = fmt.Fprintf(w, " SCA Issues: %d\n", summary.ScaIssues) + return err } + return nil } -func runGetResultByScanIDCommand(resultsWrapper wrappers.ResultsWrapper) func(cmd *cobra.Command, args []string) error { +func writeConsoleSummary(summary *ResultSummary) error { + fmt.Println("") + fmt.Printf(" Created At: %s\n", summary.CreatedAt) + fmt.Printf(" Risk: %s\n", summary.RiskMsg) + fmt.Printf(" Project ID: %s\n", summary.ProjectID) + fmt.Printf(" Scan ID: %s\n", summary.ScanID) + fmt.Printf(" Total Issues: %d\n", summary.TotalIssues) + fmt.Printf(" High Issues: %d\n", summary.HighIssues) + fmt.Printf(" Medium Issues: %d\n", summary.MediumIssues) + fmt.Printf(" Low Issues: %d\n", summary.LowIssues) + + fmt.Printf(" Kics Issues: %d\n", summary.KicsIssues) + fmt.Printf(" Sast Issues: %d\n", summary.SastIssues) + fmt.Printf(" SCA Issues: %d\n", summary.ScaIssues) + + return nil +} + +func runGetResultCommand(resultsWrapper wrappers.ResultsWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { + targetFile, _ := cmd.Flags().GetString(TargetFlag) + targetPath, _ := cmd.Flags().GetString(TargetPathFlag) + format, _ := cmd.Flags().GetString(TargetFormatFlag) scanID, _ := cmd.Flags().GetString(ScanIDFlag) - if scanID == "" { - return errors.Errorf("%s: Please provide a scan ID", failedListingResults) - } params, err := getFilters(cmd) if err != nil { return errors.Wrapf(err, "%s", failedListingResults) } - results, err := ReadResults(resultsWrapper, scanID, params) - if err == nil { - return exportResults(cmd, results) - } + return CreateScanReport(resultsWrapper, scanID, format, targetFile, targetPath, params) + } +} + +func CreateScanReport(resultsWrapper wrappers.ResultsWrapper, + scanID string, + reportTypes string, + targetFile string, + targetPath string, + params map[string]string) error { + if scanID == "" { + return errors.Errorf("%s: Please provide a scan ID", failedListingResults) + } + results, err := ReadResults(resultsWrapper, scanID, params) + if err != nil { + return err + } + summary, err := SummaryReport(results, scanID) + if err != nil { return err } + reportList := strings.Split(reportTypes, ",") + for _, reportType := range reportList { + err = createReport(reportType, targetFile, targetPath, results, summary) + if err != nil { + return err + } + } + return nil +} + +func createReport(format string, + targetFile string, + targetPath string, + results *wrappers.ScanResultsCollection, + summary *ResultSummary) error { + if util.IsFormat(format, util.FormatSarif) { + sarifRpt := createTargetName(targetFile, targetPath, "sarif") + return exportSarifResults(sarifRpt, results) + } + if util.IsFormat(format, util.FormatJSON) { + jsonRpt := createTargetName(targetFile, targetPath, "json") + return exportJSONResults(jsonRpt, results) + } + if util.IsFormat(format, util.FormatSummaryConsole) { + return writeConsoleSummary(summary) + } + if util.IsFormat(format, util.FormatSummary) { + summaryRpt := createTargetName(targetFile, targetPath, "html") + return writeHTMLSummary(summaryRpt, summary) + } + err := fmt.Errorf("bad report format %s", format) + return err +} + +func createTargetName(targetFile, targetPath, targetType string) string { + return targetPath + "/" + targetFile + "." + targetType } func ReadResults( @@ -259,46 +254,38 @@ func ReadResults( return nil, nil } -func exportResults(cmd *cobra.Command, results *wrappers.ScanResultsCollection) error { - formatFlag, _ := cmd.Flags().GetString(FormatFlag) - if util.IsFormat(formatFlag, util.FormatJSON) { - return exportJSONResults(cmd, results) - } - if util.IsFormat(formatFlag, util.FormatSarif) { - return exportSarifResults(cmd, results) - } - return outputResultsPretty(cmd.OutOrStdout(), results.Results) -} - -func exportSarifResults(cmd *cobra.Command, results *wrappers.ScanResultsCollection) error { +func exportSarifResults(targetFile string, results *wrappers.ScanResultsCollection) error { var err error var resultsJSON []byte + fmt.Println("Creating SARIF Report: ", targetFile) var sarifResults *wrappers.SarifResultsCollection = convertCxResultsToSarif(results) resultsJSON, err = json.Marshal(sarifResults) if err != nil { return errors.Wrapf(err, "%s: failed to serialize results response ", failedGettingAll) } - _, _ = fmt.Fprintln(cmd.OutOrStdout(), string(resultsJSON)) + f, err := os.Create(targetFile) + if err != nil { + return errors.Wrapf(err, "%s: failed to create target file ", failedGettingAll) + } + _, _ = fmt.Fprintln(f, string(resultsJSON)) + f.Close() return nil } -func exportJSONResults(cmd *cobra.Command, results *wrappers.ScanResultsCollection) error { +func exportJSONResults(targetFile string, results *wrappers.ScanResultsCollection) error { var err error var resultsJSON []byte + fmt.Println("Creating JSON Report: ", targetFile) resultsJSON, err = json.Marshal(results) if err != nil { return errors.Wrapf(err, "%s: failed to serialize results response ", failedGettingAll) } - _, _ = fmt.Fprintln(cmd.OutOrStdout(), string(resultsJSON)) - return nil -} - -func outputResultsPretty(w io.Writer, results []*wrappers.ScanResult) error { - _, _ = fmt.Fprintln(w, "************ Results ************") - for i := 0; i < len(results); i++ { - outputSingleResult(w, results[i]) - _, _ = fmt.Fprintln(w) + f, err := os.Create(targetFile) + if err != nil { + return errors.Wrapf(err, "%s: failed to create target file ", failedGettingAll) } + _, _ = fmt.Fprintln(f, string(resultsJSON)) + f.Close() return nil } @@ -358,7 +345,10 @@ func findSarifResults(results *wrappers.ScanResultsCollection) []wrappers.SarifS // this is placeholder code scanLocation.PhysicalLocation.ArtifactLocation.URI = "" scanLocation.PhysicalLocation.Region.StartLine = result.ScanResultData.Nodes[0].Line - column := result.ScanResultData.Nodes[0].Column + // TODO: fix this column issue and places that reference it when + // the data structures are fixed. + // column := result.ScanResultData.Nodes[0].Column + var column uint = 0 length := result.ScanResultData.Nodes[0].Length scanLocation.PhysicalLocation.Region.StartColumn = column scanLocation.PhysicalLocation.Region.EndColumn = column + length @@ -368,20 +358,6 @@ func findSarifResults(results *wrappers.ScanResultsCollection) []wrappers.SarifS return sarifResults } -func outputSingleResult(w io.Writer, model *wrappers.ScanResult) { - _, _ = fmt.Fprintln(w, "Result Unique ID:", model.ScanResultData.PackageID) - _, _ = fmt.Fprintln(w, "Query ID:", model.ScanResultData.QueryID) - _, _ = fmt.Fprintln(w, "Query Name:", model.ScanResultData.QueryName) - _, _ = fmt.Fprintln(w, "Severity:", model.Severity) - _, _ = fmt.Fprintln(w, "Similarity ID:", model.SimilarityID) - _, _ = fmt.Fprintln(w, "First Scan ID:", model.FirstScanID) - _, _ = fmt.Fprintln(w, "Found At:", model.FoundAt) - _, _ = fmt.Fprintln(w, "First Found At:", model.FirstFoundAt) - _, _ = fmt.Fprintln(w, "Status:", model.Status) - _, _ = fmt.Fprintln(w) - _, _ = fmt.Fprintln(w, "************ Nodes ************") -} - type ResultSummary struct { TotalIssues int HighIssues int diff --git a/internal/commands/result_test.go b/internal/commands/result_test.go index a2ec7e09a..ceae2d2a8 100644 --- a/internal/commands/result_test.go +++ b/internal/commands/result_test.go @@ -16,12 +16,24 @@ func TestResultHelp(t *testing.T) { func TestRunGetResultsByScanIdSarifFormat(t *testing.T) { cmd := createASTTestCommand() - err := executeTestCommand(cmd, "-v", "result", "list", "--scan-id", "MOCK", "--format", "sarif") - assert.NilError(t, err) + err := executeTestCommand(cmd, "result", "--scan-id", "MOCK", "--report-format", "sarif") + assert.Assert(t, err != nil) } func TestRunGetResultsByScanIdJsonFormat(t *testing.T) { cmd := createASTTestCommand() - err := executeTestCommand(cmd, "-v", "result", "list", "--scan-id", "MOCK", "--format", "json") - assert.NilError(t, err) + err := executeTestCommand(cmd, "result", "--scan-id", "MOCK", "--report-format", "json") + assert.Assert(t, err != nil) +} + +func TestRunGetResultsByScanIdSummaryHtmlFormat(t *testing.T) { + cmd := createASTTestCommand() + err := executeTestCommand(cmd, "result", "--scan-id", "MOCK", "--report-format", "summaryHTML") + assert.Assert(t, err != nil) +} + +func TestRunGetResultsByScanIdSummaryConsoleFormat(t *testing.T) { + cmd := createASTTestCommand() + err := executeTestCommand(cmd, "result", "--scan-id", "MOCK", "--report-format", "summaryConsole") + assert.Assert(t, err != nil) } diff --git a/internal/commands/root.go b/internal/commands/root.go index be63e0f79..97539a914 100644 --- a/internal/commands/root.go +++ b/internal/commands/root.go @@ -57,7 +57,6 @@ const ( InsecureFlagUsage = "Ignore TLS certificate validations" FormatFlag = "format" FormatFlagUsageFormat = "Format for the output. One of %s" - FilterFlag = "filter" BaseURIFlag = "base-uri" ProxyFlag = "proxy" @@ -83,7 +82,9 @@ const ( ProfileFlag = "profile" ProfileFlagUsage = "The default configuration profile" Help = "help" - TargetFlag = "target" + TargetFlag = "output-name" + TargetPathFlag = "output-path" + TargetFormatFlag = "report-format" ) // NewAstCLI Return an AST CLI root command to execute @@ -223,6 +224,11 @@ func addFormatFlag(cmd *cobra.Command, defaultFormat string, otherAvailableForma fmt.Sprintf(FormatFlagUsageFormat, append(otherAvailableFormats, defaultFormat))) } +func addResultFormatFlag(cmd *cobra.Command, defaultFormat string, otherAvailableFormats ...string) { + cmd.PersistentFlags().String(TargetFormatFlag, defaultFormat, + fmt.Sprintf(FormatFlagUsageFormat, append(otherAvailableFormats, defaultFormat))) +} + func addScanIDFlag(cmd *cobra.Command, helpMsg string) { cmd.PersistentFlags().String(ScanIDFlag, "", helpMsg) } diff --git a/internal/commands/scan.go b/internal/commands/scan.go index b3d6ff1f8..5a9f1816c 100644 --- a/internal/commands/scan.go +++ b/internal/commands/scan.go @@ -239,7 +239,7 @@ func scanCreateSubCommand( Long: "The create command enables the ability to create and run a new scan in CxAST.", Example: heredoc.Doc( ` - $ cx scan create --project-name --sources + $ cx scan create --project-name -s `, ), Annotations: map[string]string{ @@ -251,7 +251,6 @@ func scanCreateSubCommand( }, RunE: runCreateScanCommand(scansWrapper, uploadsWrapper, resultsWrapper), } - createScanCmd.PersistentFlags().BoolP(WaitFlag, "", false, "Wait for scan completion (default true)") createScanCmd.PersistentFlags().IntP(WaitDelayFlag, "", WaitDelayDefault, "Polling wait time in seconds") createScanCmd.PersistentFlags().StringP( @@ -283,6 +282,10 @@ func scanCreateSubCommand( createScanCmd.PersistentFlags().String(ScanTypes, "", "Scan types, ex: (sast,kics,sca)") createScanCmd.PersistentFlags().String(TagList, "", "List of tags, ex: (tagA,tagB:val,etc)") createScanCmd.PersistentFlags().StringP(BranchFlag, BranchFlagSh, commonParams.Branch, BranchFlagUsage) + addResultFormatFlag(createScanCmd, util.FormatSummaryConsole, util.FormatJSON, util.FormatSummary, util.FormatSarif) + createScanCmd.PersistentFlags().String(TargetFlag, "", "Output file") + createScanCmd.PersistentFlags().String(TargetPathFlag, ".", "Output Path") + createScanCmd.PersistentFlags().StringSlice(FilterFlag, []string{}, filterResultsListFlagUsage) // Link the environment variable to the CLI argument(s). err = viper.BindPFlag(commonParams.BranchKey, createScanCmd.PersistentFlags().Lookup(BranchFlag)) if err != nil { @@ -728,16 +731,23 @@ func runCreateScanCommand(scansWrapper wrappers.ScansWrapper, return err } } - // Get the scan summary data - results, err := ReadResults(resultsWrapper, scanResponseModel.ID, make(map[string]string)) + // Create the required reports + targetFile, _ := cmd.Flags().GetString(TargetFlag) + targetPath, _ := cmd.Flags().GetString(TargetPathFlag) + reportFormats, _ := cmd.Flags().GetString(TargetFormatFlag) + params, err := getFilters(cmd) if err != nil { - return errors.Wrapf(err, "%s\n", failedCreating) + return err } - summary, err := SummaryReport(results, scanResponseModel.ID) - if err == nil { - writeConsoleSummary(summary) + if !strings.Contains(reportFormats, util.FormatSummaryConsole) { + reportFormats += ("," + util.FormatSummaryConsole) } - return nil + return CreateScanReport(resultsWrapper, + scanResponseModel.ID, + reportFormats, + targetFile, + targetPath, + params) } } @@ -761,18 +771,6 @@ func waitForScanCompletion( return nil } -func writeConsoleSummary(summary *ResultSummary) { - fmt.Println("") - fmt.Printf(" Created At: %s\n", summary.CreatedAt) - fmt.Printf(" Risk: %s\n", summary.RiskMsg) - fmt.Printf(" Project ID: %s\n", summary.ProjectID) - fmt.Printf(" Scan ID: %s\n", summary.ScanID) - fmt.Printf(" Total Issues: %d\n", summary.TotalIssues) - fmt.Printf(" High Issues: %d\n", summary.HighIssues) - fmt.Printf(" Medium Issues: %d\n", summary.MediumIssues) - fmt.Printf(" Low Issues: %d\n", summary.LowIssues) -} - func isScanRunning(scansWrapper wrappers.ScansWrapper, scanID string) (bool, error) { var scanResponseModel *scansRESTApi.ScanResponseModel var errorModel *scansRESTApi.ErrorModel diff --git a/internal/commands/util/printer.go b/internal/commands/util/printer.go index b0b5c6ba9..269abe5df 100644 --- a/internal/commands/util/printer.go +++ b/internal/commands/util/printer.go @@ -13,12 +13,14 @@ import ( ) const ( - FormatJSON = "json" - FormatSarif = "sarif" - FormatList = "list" - FormatTable = "table" - FormatHTML = "html" - FormatText = "text" + FormatJSON = "json" + FormatSarif = "sarif" + FormatSummary = "summaryHTML" + FormatSummaryConsole = "summaryConsole" + FormatList = "list" + FormatTable = "table" + FormatHTML = "html" + FormatText = "text" ) func Print(w io.Writer, view interface{}, format string) error { diff --git a/internal/wrappers/results-json.go b/internal/wrappers/results-json.go index 114921690..8e7ef36bd 100644 --- a/internal/wrappers/results-json.go +++ b/internal/wrappers/results-json.go @@ -48,10 +48,10 @@ type VulnerabilityCVSS struct { } type ScanResultNode struct { - ID string `json:"id,omitempty"` - Line uint `json:"line,omitempty"` - Name string `json:"name,omitempty"` - Column uint `json:"column,omitempty"` + ID string `json:"id,omitempty"` + Line uint `json:"line,omitempty"` + Name string `json:"name,omitempty"` + // Column uint `json:"column,omitempty"` Length uint `json:"length,omitempty"` Method string `json:"method,omitempty"` NodeID int `json:"nodeID,omitempty"` diff --git a/test/integration/result_test.go b/test/integration/result_test.go index 09c92bc33..3a4595c13 100644 --- a/test/integration/result_test.go +++ b/test/integration/result_test.go @@ -4,8 +4,6 @@ package integration import ( "fmt" - "io" - "io/ioutil" "os" "strings" "testing" @@ -16,52 +14,10 @@ import ( "gotest.tools/assert" ) -// Create a scan and test getting its results -func TestResultListJson(t *testing.T) { - scanID, projectID := createScan(t, Dir, Tags) - - defer deleteProject(t, projectID) - defer deleteScan(t, scanID) - - resultCommand, outputBuffer := createRedirectedTestCommand(t) - - err := execute(resultCommand, - "result", "list", - flag(commands.FormatFlag), util.FormatJSON, - flag(commands.ScanIDFlag), scanID, - ) - assert.NilError(t, err, "Getting results should pass") - - result := wrappers.ScanResultsCollection{} - _ = unmarshall(t, outputBuffer, &result, "Reading results should pass") - - assert.Assert(t, uint(len(result.Results)) == result.TotalCount, "Should have results") -} +const fileName = "result-test" // Create a scan and test getting its results -func TestResultListSarif(t *testing.T) { - scanID, projectID := createScan(t, Dir, Tags) - - defer deleteProject(t, projectID) - defer deleteScan(t, scanID) - - resultCommand, outputBuffer := createRedirectedTestCommand(t) - - err := execute(resultCommand, - "result", "list", - flag(commands.FormatFlag), util.FormatSarif, - flag(commands.ScanIDFlag), scanID, - ) - assert.NilError(t, err, "Getting results should pass") - - result := wrappers.SarifResultsCollection{} - _ = unmarshall(t, outputBuffer, &result, "Reading results should pass") - - assert.Assert(t, len(result.Runs) > 0, "Should have results") -} - -// Create a scan and test getting its results -func TestResultListPretty(t *testing.T) { +func TestResultListJson(t *testing.T) { scanID, projectID := createScan(t, Dir, Tags) defer deleteProject(t, projectID) @@ -69,103 +25,29 @@ func TestResultListPretty(t *testing.T) { resultCommand, outputBuffer := createRedirectedTestCommand(t) - err := execute(resultCommand, - "result", "list", + err := execute( + resultCommand, + "result", + flag(commands.TargetFormatFlag), strings.Join([]string{util.FormatJSON, util.FormatSarif, util.FormatSummary}, ","), + flag(commands.TargetFlag), fileName, flag(commands.ScanIDFlag), scanID, ) assert.NilError(t, err, "Getting results should pass") - result, err := io.ReadAll(outputBuffer) - assert.NilError(t, err, "Reading pretty results should pass") - - assert.Assert(t, len(string(result)) > 0, "Should have results") - assert.Assert(t, strings.Contains(string(result), "Results"), "Should have results header") -} - -// Create a scan and test getting its summary in text -func TestResultSummaryText(t *testing.T) { - - scanID, projectID, result := executeResultSummary(t, util.FormatText) - - assert.Assert(t, strings.Contains(result, "Scan ID: "+scanID)) - assert.Assert(t, strings.Contains(result, "Project ID: "+projectID)) -} - -// Create a scan and test getting its summary in HTML -func TestResultSummaryHtml(t *testing.T) { - - scanID, projectID, result := executeResultSummary(t, util.FormatHTML) - - assert.Assert(t, strings.Contains(result, fmt.Sprintf("
Scan: %s
", scanID))) - assert.Assert(t, strings.Contains(result, projectID)) -} - -func executeResultSummary(t *testing.T, format string) (string, string, string) { - scanID, projectID := createScan(t, Dir, Tags) - - defer deleteProject(t, projectID) - defer deleteScan(t, scanID) - - summaryCommand, outputBuffer := createRedirectedTestCommand(t) - - err := execute(summaryCommand, - "result", "summary", - flag(commands.FormatFlag), format, - flag(commands.ScanIDFlag), scanID, - ) - assert.NilError(t, err, "Getting result summary should pass") - - resultBytes, err := io.ReadAll(outputBuffer) - assert.NilError(t, err, "Reading result summary should pass") - - result := string(resultBytes) - return scanID, projectID, result -} - -// Create a scan and test getting its summary into an html file -func TestResultSummaryOutputFileHtml(t *testing.T) { - scanID, projectID, html := executeResultSummaryToFile(t, "output.html", util.FormatHTML) - - assert.Assert(t, strings.Contains(html, fmt.Sprintf("
Scan: %s
", scanID)), "HTML should contain scan id") - assert.Assert(t, strings.Contains(html, projectID), "HTML should contain project id") -} - -// Create a scan and test getting its summary into a text file -func TestResultSummaryOutputFileText(t *testing.T) { - scanID, projectID, text := executeResultSummaryToFile(t, "output.txt", util.FormatText) - - assert.Assert(t, strings.Contains(text, fmt.Sprintf("Scan ID: %s", scanID)), "Text file should contain scan id") - assert.Assert(t, strings.Contains(text, fmt.Sprintf("Project ID: %s", projectID)), "Text file should contain project id") -} - -func executeResultSummaryToFile(t *testing.T, file string, format string) (string, string, string) { - scanID, projectID := createScan(t, Dir, Tags) - - defer deleteProject(t, projectID) - defer deleteScan(t, scanID) - - summaryCommand := createASTIntegrationTestCommand(t) - - err := execute(summaryCommand, - "result", "summary", - flag(commands.TargetFlag), file, - flag(commands.ScanIDFlag), scanID, - flag(commands.FormatFlag), format, - ) - assert.NilError(t, err, "Getting result summary should pass") - - _, err = os.Stat(file) - assert.NilError(t, err, "Output file should exist") - + extensions := []string{util.FormatJSON, util.FormatSarif, util.FormatHTML} defer func() { - err := os.Remove(file) - if err != nil { - fmt.Println(err) + for _, e := range extensions { + _ = os.Remove(fmt.Sprintf("%s.%s", fileName, e)) } }() - data, err := ioutil.ReadFile(file) - content := string(data) + result := wrappers.ScanResultsCollection{} + _ = unmarshall(t, outputBuffer, &result, "Reading results should pass") + + assert.Assert(t, uint(len(result.Results)) == result.TotalCount, "Should have results") - return scanID, projectID, content + for _, e := range extensions { + _, err = os.Stat(fmt.Sprintf("%s.%s", fileName, e)) + assert.NilError(t, err, "Report file should exist for extension "+e) + } } diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index 0d85bb8e3..b71c4b89c 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -205,7 +205,7 @@ func executeCreateScan(t *testing.T, args []string) (string, string) { createdScan := scansRESTApi.ScanResponseModel{} _ = unmarshall(t, buffer, &createdScan, "Reading scan response JSON should pass") - assert.Assert(t, createdScan.Status == scansApi.ScanQueued) + assert.Assert(t, createdScan.Status != scansApi.ScanFailed && createdScan.Status != scansApi.ScanCanceled) log.Printf("Scan ID %s created in test", createdScan.ID) diff --git a/test/integration/util_command.go b/test/integration/util_command.go index b13d0030f..872597770 100644 --- a/test/integration/util_command.go +++ b/test/integration/util_command.go @@ -78,14 +78,9 @@ func execute(cmd *cobra.Command, args ...string) error { } func executeWithTimeout(cmd *cobra.Command, timeout time.Duration, args ...string) error { - proxyUser := viper.GetString(ProxyUserEnv) - proxyPw := viper.GetString(ProxyPwEnv) - proxyPort := viper.GetInt(ProxyPortEnv) - proxyHost := viper.GetString(ProxyHostEnv) args = append(args, flag(commands.VerboseFlag)) - args = append(args, flag(commands.ProxyFlag)) - args = append(args, fmt.Sprintf(ProxyURLTmpl, proxyUser, proxyPw, proxyHost, proxyPort)) + args = appendProxyArgs(args) cmd.SetArgs(args) ctx, cancel := context.WithTimeout(context.Background(), timeout) @@ -103,3 +98,13 @@ func executeWithTimeout(cmd *cobra.Command, timeout time.Duration, args ...strin return result } } + +func appendProxyArgs(args []string) []string { + proxyUser := viper.GetString(ProxyUserEnv) + proxyPw := viper.GetString(ProxyPwEnv) + proxyPort := viper.GetInt(ProxyPortEnv) + proxyHost := viper.GetString(ProxyHostEnv) + argsWithProxy := append(args, flag(commands.ProxyFlag)) + argsWithProxy = append(argsWithProxy, fmt.Sprintf(ProxyURLTmpl, proxyUser, proxyPw, proxyHost, proxyPort)) + return argsWithProxy +}