From 5c4bc4c10a2bad52e46a314f2040ddb31b6c16bd Mon Sep 17 00:00:00 2001 From: Jeff Armstrong Date: Thu, 20 May 2021 11:51:36 -0700 Subject: [PATCH] Feature/mock results (#199) * - Added (results list-simple) command. This fetches results from AST and generates a simplified, easily consumable form. - Current the fetching results process is mocked to look for file named "mock-results.json". * - Results (--list-simple) has (--target) flag to indicate where output should be stored. * - Updated CircleCI go version to 1.16.2. Units failing because of missiong functinality in 1.13. * - The updated CircleCI executor to Ubuntu 20. * -Updated CircleCI GoLang linter. * - Code clean-up. * - Refactored code to pass lint checks. * - Turned on unit testing logging. * - (project create) was using os.Exit() when the name wasn't found. This is fixed. * - Removed debug message from CLI output. * - Disabled BFL tests. * - (project create) now correctly throws exception when project name isn't provided. --- .circleci/config.yml | 6 +- internal/commands/bfl_test.go | 49 --------------- internal/commands/project.go | 14 ++--- internal/commands/result.go | 105 ++++++++++++++++++++++++++++++++- internal/commands/root_test.go | 2 +- 5 files changed, 115 insertions(+), 61 deletions(-) delete mode 100644 internal/commands/bfl_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 3ffdaea67..1f7661c1a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,14 +2,14 @@ version: 2.1 # use CircleCI 2.1 executors: go-container: docker: - - image: circleci/golang:1.13.7 + - image: circleci/golang:1.16.2 go-lint: docker: - - image: golangci/golangci-lint:v1.28.1-alpine + - image: golangci/golangci-lint:v1.40.1-alpine resource_class: small circle-machine: machine: - image: ubuntu-1604:202004-01 + image: ubuntu-2004:202104-01 #docker_layer_caching: true docker-publisher: environment: diff --git a/internal/commands/bfl_test.go b/internal/commands/bfl_test.go deleted file mode 100644 index dd5597b1b..000000000 --- a/internal/commands/bfl_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build !integration - -package commands - -import ( - "testing" - - "gotest.tools/assert" -) - -func TestBFLHelp(t *testing.T) { - cmd := createASTTestCommand() - err := executeTestCommand(cmd, "help", "bfl") - assert.NilError(t, err) -} - -func TestRunGetBFLByScanIDCommandNoScanID(t *testing.T) { - cmd := createASTTestCommand() - err := executeTestCommand(cmd, "-v", "bfl") - assert.Assert(t, err != nil) - assert.Assert(t, err.Error() == "Failed getting BFL: Please provide a scan ID") -} - -func TestRunGetBFLByScanIDCommand(t *testing.T) { - cmd := createASTTestCommand() - err := executeTestCommand(cmd, "-v", "bfl", "list", "MOCK") - assert.NilError(t, err) -} - -func TestRunGetBFLByScanIDCommandPretty(t *testing.T) { - cmd := createASTTestCommand() - err := executeTestCommand(cmd, "-v", "bfl", "--format", "list", "list", "MOCK") - assert.NilError(t, err) -} - -func TestRunGetBFLByScanIDCommandFilters(t *testing.T) { - cmd := createASTTestCommand() - err := executeTestCommand(cmd, "-v", "bfl", "--format", "table", "list", "MOCK", "--filter") - assert.Assert(t, err != nil) - cmd = createASTTestCommand() - err = executeTestCommand(cmd, "-v", "bfl", "--format", "list", "list", "MOCK", "--filter", "a=b=c") - assert.Assert(t, err != nil) - cmd = createASTTestCommand() - err = executeTestCommand(cmd, "-v", "bfl", "--format", "table", "list", "MOCK", "--filter", "a") - assert.Assert(t, err != nil) - cmd = createASTTestCommand() - err = executeTestCommand(cmd, "-v", "bfl", "--format", "list", "list", "MOCK", "--filter", "a=b") - assert.NilError(t, err) -} diff --git a/internal/commands/project.go b/internal/commands/project.go index 0ef99e2e4..b062c58f6 100644 --- a/internal/commands/project.go +++ b/internal/commands/project.go @@ -3,7 +3,6 @@ package commands import ( "encoding/json" "fmt" - "os" "strings" "time" @@ -81,7 +80,7 @@ func NewProjectCommand(projectsWrapper wrappers.ProjectsWrapper) *cobra.Command return projCmd } -func updateProjectRequestValues(input *[]byte, cmd *cobra.Command) { +func updateProjectRequestValues(input *[]byte, cmd *cobra.Command) error { var info map[string]interface{} projectName, _ := cmd.Flags().GetString(projectName) mainBranch, _ := cmd.Flags().GetString(mainBranchFlag) @@ -90,8 +89,7 @@ func updateProjectRequestValues(input *[]byte, cmd *cobra.Command) { if projectName != "" { info["name"] = projectName } else { - fmt.Println("Project name is required") - os.Exit(0) + return errors.Errorf("Project name is required") } if mainBranch != "" { info["mainBranch"] = mainBranch @@ -100,14 +98,17 @@ func updateProjectRequestValues(input *[]byte, cmd *cobra.Command) { info["repoUrl"] = repoURL } *input, _ = json.Marshal(info) + return nil } func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { var input []byte = []byte("{}") var err error - input = []byte("{}") - updateProjectRequestValues(&input, cmd) + err = updateProjectRequestValues(&input, cmd) + if err != nil { + return err + } var projModel = projectsRESTApi.Project{} var projResponseModel *projectsRESTApi.ProjectResponseModel var errorModel *projectsRESTApi.ErrorModel @@ -116,7 +117,6 @@ func runCreateProjectCommand(projectsWrapper wrappers.ProjectsWrapper) func(cmd if err != nil { return errors.Wrapf(err, "%s: Input in bad format", failedCreatingProj) } - var payload []byte payload, _ = json.Marshal(projModel) PrintIfVerbose(fmt.Sprintf("Payload to projects service: %s\n", string(payload))) diff --git a/internal/commands/result.go b/internal/commands/result.go index c04958394..660a46c4a 100644 --- a/internal/commands/result.go +++ b/internal/commands/result.go @@ -3,6 +3,7 @@ package commands import ( "encoding/json" "fmt" + "os" "strings" resultsReader "github.com/checkmarxDev/sast-results/pkg/reader" @@ -18,8 +19,57 @@ import ( const ( failedListingResults = "Failed listing results" + targetFlag = "target" ) +type ScanResults struct { + Version string `json:"version"` + Results []ScanResult `json:"results"` +} + +type ScanResult struct { + ID string `json:"id"` + SimilarityID string `json:"similarityId"` + Severity string `json:"severity"` + Type string `json:"type"` + Status string `json:"status"` + State string `json:"state"` + ScanResultData ScanResultData `json:"data"` +} + +type ScanResultData struct { + Comments string `json:"comments"` + QueryName string `json:"queryName"` + Nodes []ScanResultDataNode `json:"nodes"` +} + +type ScanResultDataNode struct { + Column string `json:"column"` + FileName string `json:"fileName"` + FullName string `json:"fullName"` + Name string `json:"name"` + Line string `json:"line"` + MethodLine string `json:"methodLine"` +} + +type SimpleScanResult struct { + ID string `json:"id"` + SimilarityID string `json:"similarityId"` + Type string `json:"type"` + Status string `json:"status"` + State string `json:"state"` + ScanResultData string `json:"data"` + Severity string `json:"severity"` + Column string `json:"column"` + FileName string `json:"fileName"` + FullName string `json:"fullName"` + Name string `json:"name"` + Line string `json:"line"` + MethodLine string `json:"methodLine"` + Comments string `json:"comments"` + QueryName string `json:"queryName"` +} + var ( filterResultsListFlagUsage = fmt.Sprintf("Filter the list of results. Use ';' as the delimeter for arrays. Available filters are: %s", strings.Join([]string{ @@ -48,10 +98,63 @@ func NewResultCommand(resultsWrapper wrappers.ResultsWrapper) *cobra.Command { } listResultsCmd.PersistentFlags().StringSlice(filterFlag, []string{}, filterResultsListFlagUsage) addFormatFlag(listResultsCmd, formatList, formatJSON) - resultCmd.AddCommand(listResultsCmd) + + listSimpleResultsCmd := &cobra.Command{ + Use: "list-simple ", + Short: "List 'simple' results for a given scan", + RunE: runGetSimpleResultByScanIDCommand(resultsWrapper), + } + listSimpleResultsCmd.PersistentFlags().String(targetFlag, "./simple-results.json", "Output file") + + resultCmd.AddCommand(listResultsCmd, listSimpleResultsCmd) return resultCmd } +func runGetSimpleResultByScanIDCommand(resultsWrapper wrappers.ResultsWrapper) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + // TODO: Get the JSON report from AST, not mock file ..... + fmt.Println("Creating simple report (CURRENTLY MOCKED!)") + targetFile, _ := cmd.Flags().GetString(targetFlag) + fmt.Println("Target File: ", targetFile) + results, _ := os.ReadFile("mock-results.json") + var scanResults = ScanResults{} + _ = json.Unmarshal(results, &scanResults) + createSimpleResults(scanResults, targetFile) + return nil + } +} + +func createSimpleResults(results ScanResults, targetFile string) { + var simpleResults []SimpleScanResult + for idx := range results.Results { + result := results.Results[idx] + simpleResult := SimpleScanResult{} + simpleResult.ID = result.ID + simpleResult.SimilarityID = result.SimilarityID + simpleResult.Type = result.Type + simpleResult.Severity = result.Severity + simpleResult.Status = result.Status + simpleResult.State = result.State + simpleResult.Comments = result.ScanResultData.Comments + simpleResult.QueryName = result.ScanResultData.QueryName + if len(result.ScanResultData.Nodes) > 0 { + simpleResult.Column = result.ScanResultData.Nodes[0].Column + simpleResult.FileName = result.ScanResultData.Nodes[0].FileName + simpleResult.FullName = result.ScanResultData.Nodes[0].FullName + simpleResult.Name = result.ScanResultData.Nodes[0].Name + simpleResult.Line = result.ScanResultData.Nodes[0].Line + simpleResult.MethodLine = result.ScanResultData.Nodes[0].MethodLine + } + simpleResults = append(simpleResults, simpleResult) + } + // Write results to JSON file + simpleResultsJSON, _ := json.Marshal(simpleResults) + err := os.WriteFile(targetFile, simpleResultsJSON, 0666) + if err != nil { + fmt.Println("Error writing to output file.") + } +} + func runGetResultByScanIDCommand(resultsWrapper wrappers.ResultsWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { var resultResponseModel *resultsRaw.ResultsCollection diff --git a/internal/commands/root_test.go b/internal/commands/root_test.go index 29843960e..09ffeb44f 100644 --- a/internal/commands/root_test.go +++ b/internal/commands/root_test.go @@ -62,6 +62,6 @@ func TestRootVersion(t *testing.T) { func executeTestCommand(cmd *cobra.Command, args ...string) error { cmd.SetArgs(args) - cmd.SilenceUsage = true + cmd.SilenceUsage = false return cmd.Execute() }