diff --git a/e2e/cli_test.go b/e2e/cli_test.go index 2073a2c12f1..07af2212027 100644 --- a/e2e/cli_test.go +++ b/e2e/cli_test.go @@ -195,7 +195,7 @@ var tests = []struct { wantStatus: []int{126}, }, // E2E-CLI-011 - KICS scan with a valid case insensitive --type flag - // must perform the scan successfully and return exit code 0 + // must perform the scan successfully and return exit code 50 { name: "E2E-CLI-011", args: args{ diff --git a/e2e/fixtures/E2E_CLI_011_PAYLOAD.json b/e2e/fixtures/E2E_CLI_011_PAYLOAD.json index a05ec5f9011..ab43f9ed69c 100644 --- a/e2e/fixtures/E2E_CLI_011_PAYLOAD.json +++ b/e2e/fixtures/E2E_CLI_011_PAYLOAD.json @@ -1,29 +1,29 @@ { - "document": [ - { - "id": "0", - "file": "file", - "resource": { - "aws_redshift_cluster": { - "default": { - "master_password": "Mustbe8characters", - "node_type": "dc1.large", - "cluster_type": "single-node", - "cluster_identifier": "tf-redshift-cluster", - "database_name": "mydb", - "master_username": "foo" - }, - "default1": { - "master_username": "foo", - "master_password": "Mustbe8characters", - "node_type": "dc1.large", - "cluster_type": "single-node", - "publicly_accessible": true, - "cluster_identifier": "tf-redshift-cluster", - "database_name": "mydb" - } - } - } - } - ] + "document": [ + { + "id": "0", + "file": "file", + "resource": { + "aws_redshift_cluster": { + "default": { + "master_password": "Mustbe8characters", + "node_type": "dc1.large", + "cluster_type": "single-node", + "cluster_identifier": "tf-redshift-cluster", + "database_name": "mydb", + "master_username": "foo" + }, + "default1": { + "master_username": "foo", + "master_password": "Mustbe8characters", + "node_type": "dc1.large", + "cluster_type": "single-node", + "publicly_accessible": true, + "cluster_identifier": "tf-redshift-cluster", + "database_name": "mydb" + } + } + } + } + ] } diff --git a/internal/console/helpers/helpers_test.go b/internal/console/helpers/helpers_test.go index 11d9a40a3b7..2b0bb2b72f1 100644 --- a/internal/console/helpers/helpers_test.go +++ b/internal/console/helpers/helpers_test.go @@ -391,6 +391,28 @@ func TestHelpers_GenerateReport(t *testing.T) { wantErr: true, remove: []string{"result.html"}, }, + { + name: "test_generate_report_error", + args: args{ + path: ".", + filename: "result", + body: "", + formats: []string{"sarif"}, + }, + wantErr: false, + remove: []string{"result.sarif"}, + }, + { + name: "test_generate_report_error", + args: args{ + path: ".", + filename: "result", + body: "", + formats: []string{"glsast"}, + }, + wantErr: false, + remove: []string{"gl-sast-result.json"}, + }, } for _, tt := range tests { diff --git a/internal/console/scan.go b/internal/console/scan.go index ce68de18bda..d917991c9dc 100644 --- a/internal/console/scan.go +++ b/internal/console/scan.go @@ -27,6 +27,7 @@ import ( jsonParser "github.com/Checkmarx/kics/pkg/parser/json" terraformParser "github.com/Checkmarx/kics/pkg/parser/terraform" yamlParser "github.com/Checkmarx/kics/pkg/parser/yaml" + "github.com/Checkmarx/kics/pkg/report" "github.com/Checkmarx/kics/pkg/resolver" "github.com/Checkmarx/kics/pkg/resolver/helm" "github.com/Checkmarx/kics/pkg/scanner" @@ -651,7 +652,7 @@ func resolveOutputs( return err } if payloadPath != "" { - if err := printOutput(filepath.Dir(payloadPath), filepath.Base(payloadPath), documents, []string{"json"}); err != nil { + if err := report.ExportJSONReport(filepath.Dir(payloadPath), filepath.Base(payloadPath), documents); err != nil { return err } } diff --git a/pkg/report/commons.go b/pkg/report/commons.go index 45d0732f701..cf10058f7fd 100644 --- a/pkg/report/commons.go +++ b/pkg/report/commons.go @@ -1,9 +1,11 @@ package report import ( + "encoding/json" "fmt" "html/template" "os" + "path/filepath" "strings" "time" @@ -62,3 +64,48 @@ func getPlatforms(queries model.VulnerableQuerySlice) string { } return strings.Join(platforms, ", ") } + +func getRelativePath(basePath, filePath string) string { + var rtn string + relativePath, err := filepath.Rel(basePath, filePath) + if err != nil { + log.Error().Msgf("Cannot make %s relative to %s", filePath, basePath) + rtn = filePath + } else { + rtn = relativePath + } + return rtn +} + +// ExportJSONReport - encodes a given body to a JSON file in a given filepath +func ExportJSONReport(path, filename string, body interface{}) error { + if !strings.Contains(filename, ".") { + filename += jsonExtension + } + fullPath := filepath.Join(path, filename) + + f, err := os.OpenFile(filepath.Clean(fullPath), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) + if err != nil { + return err + } + + defer closeFile(fullPath, filename, f) + + encoder := json.NewEncoder(f) + encoder.SetIndent("", "\t") + + return encoder.Encode(body) +} + +func getSummary(body interface{}) (sum model.Summary, err error) { + var summary model.Summary + result, err := json.Marshal(body) + if err != nil { + return model.Summary{}, err + } + if err := json.Unmarshal(result, &summary); err != nil { + return model.Summary{}, err + } + + return summary, nil +} diff --git a/pkg/report/gitlab_sast.go b/pkg/report/gitlab_sast.go index 2b65ae651e8..faecbe3432f 100644 --- a/pkg/report/gitlab_sast.go +++ b/pkg/report/gitlab_sast.go @@ -1,10 +1,8 @@ package report import ( - "encoding/json" "strings" - "github.com/Checkmarx/kics/pkg/model" reportModel "github.com/Checkmarx/kics/pkg/report/model" ) @@ -17,22 +15,21 @@ func PrintGitlabSASTReport(path, filename string, body interface{}) error { if !strings.HasPrefix(filename, "gl-sast-") { filename = "gl-sast-" + filename } - var summary model.Summary - result, err := json.Marshal(body) - if err != nil { - return err - } - if err := json.Unmarshal(result, &summary); err != nil { - return err - } + if body != "" { + summary, err := getSummary(body) + if err != nil { + return err + } - gitlabSASTReport := reportModel.NewGitlabSASTReport(summary.Times.Start, summary.Times.End) + gitlabSASTReport := reportModel.NewGitlabSASTReport(summary.Times.Start, summary.Times.End) - for idxQuery := range summary.Queries { - for idxFile := range summary.Queries[idxQuery].Files { - gitlabSASTReport.BuildGitlabSASTVulnerability(&summary.Queries[idxQuery], &summary.Queries[idxQuery].Files[idxFile]) + for idxQuery := range summary.Queries { + for idxFile := range summary.Queries[idxQuery].Files { + gitlabSASTReport.BuildGitlabSASTVulnerability(&summary.Queries[idxQuery], &summary.Queries[idxQuery].Files[idxFile]) + } } + body = gitlabSASTReport } - return PrintJSONReport(path, filename, gitlabSASTReport) + return ExportJSONReport(path, filename, body) } diff --git a/pkg/report/json.go b/pkg/report/json.go index ca717434d81..bf2292f033d 100644 --- a/pkg/report/json.go +++ b/pkg/report/json.go @@ -1,30 +1,32 @@ package report import ( - "encoding/json" "os" - "path/filepath" - "strings" ) const jsonExtension = ".json" // PrintJSONReport prints on JSON file the summary results func PrintJSONReport(path, filename string, body interface{}) error { - if !strings.Contains(filename, ".") { - filename += jsonExtension - } - fullPath := filepath.Join(path, filename) - - f, err := os.OpenFile(filepath.Clean(fullPath), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) - if err != nil { - return err - } + if body != "" { + summary, err := getSummary(body) + if err != nil { + return err + } - defer closeFile(fullPath, filename, f) + basePath, err := os.Getwd() + if err != nil { + return err + } - encoder := json.NewEncoder(f) - encoder.SetIndent("", "\t") + for i := range summary.Queries { + query := summary.Queries[i] + for j := range query.Files { + query.Files[j].FileName = getRelativePath(basePath, query.Files[j].FileName) + } + } + body = summary + } - return encoder.Encode(body) + return ExportJSONReport(path, filename, body) } diff --git a/pkg/report/pdf.go b/pkg/report/pdf.go index 4dfcbe4787a..7b5607a1984 100644 --- a/pkg/report/pdf.go +++ b/pkg/report/pdf.go @@ -126,15 +126,8 @@ func createResultsTable(m pdf.Maroto, query *model.VulnerableQuery, basePath str } else { m.SetBackgroundColor(color.NewWhite()) } - var filePath string - relativePath, err := filepath.Rel(basePath, query.Files[idx].FileName) - if err != nil { - log.Error().Msgf("Cannot make %s relative to %s", query.Files[idx].FileName, basePath) - filePath = query.Files[idx].FileName - } else { - filePath = relativePath - } + filePath := getRelativePath(basePath, query.Files[idx].FileName) fileLine := fmt.Sprintf("%s:%s", filePath, fmt.Sprint(query.Files[idx].Line)) m.Row(colFive, func() { m.Col(colFullPage, func() { diff --git a/pkg/report/sarif.go b/pkg/report/sarif.go index 326ae0c3abc..4cd8fa8b962 100644 --- a/pkg/report/sarif.go +++ b/pkg/report/sarif.go @@ -1,10 +1,8 @@ package report import ( - "encoding/json" "strings" - "github.com/Checkmarx/kics/pkg/model" reportModel "github.com/Checkmarx/kics/pkg/report/model" ) @@ -13,19 +11,18 @@ func PrintSarifReport(path, filename string, body interface{}) error { if !strings.HasSuffix(filename, ".sarif") { filename += ".sarif" } - var summary model.Summary - result, err := json.Marshal(body) - if err != nil { - return err - } - if err := json.Unmarshal(result, &summary); err != nil { - return err - } + if body != "" { + summary, err := getSummary(body) + if err != nil { + return err + } - sarifReport := reportModel.NewSarifReport() - for idx := range summary.Queries { - sarifReport.BuildSarifIssue(&summary.Queries[idx]) + sarifReport := reportModel.NewSarifReport() + for idx := range summary.Queries { + sarifReport.BuildSarifIssue(&summary.Queries[idx]) + } + body = sarifReport } - return PrintJSONReport(path, filename, sarifReport) + return ExportJSONReport(path, filename, body) } diff --git a/test/helpers.go b/test/helpers.go index ec4aa817e75..9633156e371 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -171,6 +171,9 @@ var SummaryMock = model.Summary{ }, TotalCounter: 2, }, + ScannedPaths: []string{ + "./", + }, } // ComplexSummaryMock a summary with more results to be used without running kics scan