diff --git a/README.md b/README.md index f4b67776b3c..005e9ddc327 100644 --- a/README.md +++ b/README.md @@ -114,4 +114,4 @@ KICS is used by various companies and organizations, some are listed below. If y --- -© 2023 Checkmarx Ltd. All Rights Reserved. +© 2024 Checkmarx Ltd. All Rights Reserved. diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json new file mode 100644 index 00000000000..8182205d01c --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json @@ -0,0 +1,11 @@ +{ + "id": "0f04217d-488f-4e7a-bec8-f16159686cd6", + "queryName": "DynamoDB Table Point In Time Recovery Disabled", + "severity": "MEDIUM", + "category": "Best Practices", + "descriptionText": "It's considered a best practice to have point in time recovery enabled for DynamoDB Table", + "descriptionUrl": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-pointintimerecoveryspecification.html", + "platform": "CloudFormation", + "descriptionID": "a0a51171", + "cloudProvider": "aws" +} diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/query.rego b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/query.rego new file mode 100644 index 00000000000..5ac17c30751 --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/query.rego @@ -0,0 +1,62 @@ +package Cx + +import data.generic.common as common_lib +import data.generic.cloudformation as cf_lib + +CxPolicy[result] { + document := input.document[i] + resource := document.Resources[key] + resource.Type == "AWS::DynamoDB::Table" + properties := resource.Properties + + properties.PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled == false + + result := { + "documentId": input.document[i].id, + "resourceType": resource.Type, + "resourceName": cf_lib.get_resource_name(resource, key), + "searchKey": sprintf("Resources.%s.Properties.PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled", [key]), + "issueType": "IncorrectValue", + "keyExpectedValue": sprintf("Resources[%s].Properties.PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled should be set to 'true'", [key]), + "keyActualValue": sprintf("Resources[%s].Properties.PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled is set to 'false'", [key]), + } +} + +CxPolicy[result] { + document := input.document[i] + resource := document.Resources[key] + resource.Type == "AWS::DynamoDB::Table" + properties := resource.Properties + + not common_lib.valid_key(properties, "PointInTimeRecoverySpecification") + + result := { + "documentId": input.document[i].id, + "resourceType": resource.Type, + "resourceName": cf_lib.get_resource_name(resource, key), + "searchKey": sprintf("Resources.%s.Properties", [key]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("Resources[%s].Properties.PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled should be defined and set to 'true'", [key]), + "keyActualValue": sprintf("Resources[%s].Properties.PointInTimeRecoverySpecification is not defined", [key]), + } +} + +CxPolicy[result] { + document := input.document[i] + resource := document.Resources[key] + resource.Type == "AWS::DynamoDB::Table" + properties := resource.Properties + specification := properties.PointInTimeRecoverySpecification + + not common_lib.valid_key(specification, "PointInTimeRecoveryEnabled") + + result := { + "documentId": input.document[i].id, + "resourceType": resource.Type, + "resourceName": cf_lib.get_resource_name(resource, key), + "searchKey": sprintf("Resources.%s.Properties.PointInTimeRecoverySpecification", [key]), + "issueType": "MissingAttribute", + "keyExpectedValue": sprintf("Resources[%s].Properties.PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled should be defined and set to 'true'", [key]), + "keyActualValue": sprintf("Resources[%s].Properties.PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled is not defined", [key]), + } +} \ No newline at end of file diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/negative1.yaml b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/negative1.yaml new file mode 100644 index 00000000000..2d5c40288b2 --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/negative1.yaml @@ -0,0 +1,6 @@ +Resources: + MyDynamoDBTable: + Type: AWS::DynamoDB::Table + Properties: + PointInTimeRecoverySpecification: + PointInTimeRecoveryEnabled: true diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/negative2.json b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/negative2.json new file mode 100644 index 00000000000..4c1df5b5ef7 --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/negative2.json @@ -0,0 +1,15 @@ +{ + "Resources": { + "DynamoDBOnDemandTable1": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "BillingMode": "PAY_PER_REQUEST", + "PointInTimeRecoverySpecification" : { + "PointInTimeRecoveryEnabled" : true + } + } + }, + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Sample CloudFormation template for DynamoDB with customer managed CMK" + } +} diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive1.yaml b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive1.yaml new file mode 100644 index 00000000000..8f52a77e5e8 --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive1.yaml @@ -0,0 +1,6 @@ +Resources: + MyDynamoDBTable: + Type: AWS::DynamoDB::Table + Properties: + PointInTimeRecoverySpecification: + PointInTimeRecoveryEnabled: false diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive2.yaml b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive2.yaml new file mode 100644 index 00000000000..ba26abc5cac --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive2.yaml @@ -0,0 +1,5 @@ +Resources: + MyDynamoDBTable: + Type: AWS::DynamoDB::Table + Properties: + TableName: my-table diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive3.json b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive3.json new file mode 100644 index 00000000000..07d9427442a --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive3.json @@ -0,0 +1,15 @@ +{ + "Resources": { + "DynamoDBOnDemandTable1": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "BillingMode": "PAY_PER_REQUEST", + "PointInTimeRecoverySpecification" : { + "PointInTimeRecoveryEnabled" : false + } + } + }, + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Sample CloudFormation template for DynamoDB with customer managed CMK" + } +} diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive4.json b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive4.json new file mode 100644 index 00000000000..7eac82d4f31 --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive4.json @@ -0,0 +1,12 @@ +{ + "Resources": { + "DynamoDBOnDemandTable1": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "BillingMode": "PAY_PER_REQUEST" + } + }, + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Sample CloudFormation template for DynamoDB with customer managed CMK" + } +} diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive5.yaml b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive5.yaml new file mode 100644 index 00000000000..eb222d7f5ce --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive5.yaml @@ -0,0 +1,5 @@ +Resources: + MyDynamoDBTable: + Type: AWS::DynamoDB::Table + Properties: + PointInTimeRecoverySpecification: {} \ No newline at end of file diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive6.json b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive6.json new file mode 100644 index 00000000000..a83a1d3b63e --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive6.json @@ -0,0 +1,13 @@ +{ + "Resources": { + "DynamoDBOnDemandTable1": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "BillingMode": "PAY_PER_REQUEST", + "PointInTimeRecoverySpecification" : {} + } + }, + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "Sample CloudFormation template for DynamoDB with customer managed CMK" + } +} diff --git a/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json new file mode 100644 index 00000000000..a0a74b5bdfa --- /dev/null +++ b/assets/queries/cloudFormation/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json @@ -0,0 +1,38 @@ +[ + { + "queryName": "DynamoDB Table Point In Time Recovery Disabled", + "severity": "MEDIUM", + "line": 6, + "filename": "positive1.yaml" + }, + { + "queryName": "DynamoDB Table Point In Time Recovery Disabled", + "severity": "MEDIUM", + "line": 4, + "filename": "positive2.yaml" + }, + { + "queryName": "DynamoDB Table Point In Time Recovery Disabled", + "severity": "MEDIUM", + "line": 8, + "filename": "positive3.json" + }, + { + "queryName": "DynamoDB Table Point In Time Recovery Disabled", + "severity": "MEDIUM", + "line": 5, + "filename": "positive4.json" + }, + { + "queryName": "DynamoDB Table Point In Time Recovery Disabled", + "severity": "MEDIUM", + "line": 5, + "filename": "positive5.yaml" + }, + { + "queryName": "DynamoDB Table Point In Time Recovery Disabled", + "severity": "MEDIUM", + "line": 7, + "filename": "positive6.json" + } +] diff --git a/assets/queries/pulumi/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json b/assets/queries/pulumi/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json index d1793ba1271..12306fb4d77 100644 --- a/assets/queries/pulumi/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json +++ b/assets/queries/pulumi/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json @@ -1,7 +1,7 @@ { "id": "327b0729-4c5c-4c44-8b5c-e476cd9c7290", "queryName": "DynamoDB Table Point In Time Recovery Disabled", - "severity": "INFO", + "severity": "MEDIUM", "category": "Best Practices", "descriptionText": "It's considered a best practice to have point in time recovery enabled for DynamoDB Table", "descriptionUrl": "https://www.pulumi.com/registry/packages/aws/api-docs/dynamodb/table/#pointintimerecovery_yaml", diff --git a/assets/queries/pulumi/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json b/assets/queries/pulumi/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json index 3ed4e0a83e6..e6888622d2d 100644 --- a/assets/queries/pulumi/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json +++ b/assets/queries/pulumi/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json @@ -1,13 +1,13 @@ [ { "queryName": "DynamoDB Table Point In Time Recovery Disabled", - "severity": "INFO", + "severity": "MEDIUM", "line": 7, "fileName": "positive.yaml" }, { "queryName": "DynamoDB Table Point In Time Recovery Disabled", - "severity": "INFO", + "severity": "MEDIUM", "line": 21, "fileName": "positive.yaml" } diff --git a/assets/queries/terraform/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json b/assets/queries/terraform/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json index 4325975f8f9..223c5873c3d 100644 --- a/assets/queries/terraform/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json +++ b/assets/queries/terraform/aws/dynamodb_table_point_in_time_recovery_disabled/metadata.json @@ -1,7 +1,7 @@ { "id": "741f1291-47ac-4a85-a07b-3d32a9d6bd3e", "queryName": "DynamoDB Table Point In Time Recovery Disabled", - "severity": "INFO", + "severity": "MEDIUM", "category": "Best Practices", "descriptionText": "It's considered a best practice to have point in time recovery enabled for DynamoDB Table", "descriptionUrl": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table#point_in_time_recovery", diff --git a/assets/queries/terraform/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json b/assets/queries/terraform/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json index 5c0fb98859b..9f98d2de117 100644 --- a/assets/queries/terraform/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json +++ b/assets/queries/terraform/aws/dynamodb_table_point_in_time_recovery_disabled/test/positive_expected_result.json @@ -1,13 +1,13 @@ [ { "queryName": "DynamoDB Table Point In Time Recovery Disabled", - "severity": "INFO", + "severity": "MEDIUM", "line": 10, "filename": "positive1.tf" }, { "queryName": "DynamoDB Table Point In Time Recovery Disabled", - "severity": "INFO", + "severity": "MEDIUM", "line": 1, "filename": "positive2.tf" } diff --git a/e2e/cli_test.go b/e2e/cli_test.go index 431f2c00aba..9c0dce3079a 100644 --- a/e2e/cli_test.go +++ b/e2e/cli_test.go @@ -92,6 +92,7 @@ func Test_E2E_CLI(t *testing.T) { // Check log file logData, _ := utils.ReadFixture(tt.Args.ExpectedLog.LogFile, "output") validation := tt.Args.ExpectedLog.ValidationFunc(logData) + require.Truef(t, validation, "The output log file 'output/%s' doesn't match the regex validation", tt.Args.ExpectedLog.LogFile) } diff --git a/e2e/testcases/e2e-cli-007_scan.go b/e2e/testcases/e2e-cli-007_scan.go index 4af76aaa12b..d6abc67bba1 100644 --- a/e2e/testcases/e2e-cli-007_scan.go +++ b/e2e/testcases/e2e-cli-007_scan.go @@ -9,13 +9,13 @@ func init() { //nolint Name: "should perform a simple scan [E2E-CLI-007]", Args: args{ Args: []cmdArgs{ - []string{"scan", "-p", "/path/e2e/fixtures/samples/positive.yaml"}, + []string{"scan", "-p", "/path/e2e/fixtures/samples/positive.yaml", "-v"}, }, }, WantStatus: []int{50}, Validation: func(outputText string) bool { - match1, _ := regexp.MatchString(`Files scanned: \d+`, outputText) - match2, _ := regexp.MatchString(`Parsed files: \d+`, outputText) + match1, _ := regexp.MatchString(`Scanned Files: \d+`, outputText) + match2, _ := regexp.MatchString(`Parsed Files: \d+`, outputText) match3, _ := regexp.MatchString(`Queries loaded: \d+`, outputText) match4, _ := regexp.MatchString(`Queries failed to execute: \d+`, outputText) match5, _ := regexp.MatchString(`Results Summary:`, outputText) diff --git a/e2e/testcases/e2e-cli-027_scan_exclude-paths.go b/e2e/testcases/e2e-cli-027_scan_exclude-paths.go index 0ec16cd5aa8..4f3e05bb934 100644 --- a/e2e/testcases/e2e-cli-027_scan_exclude-paths.go +++ b/e2e/testcases/e2e-cli-027_scan_exclude-paths.go @@ -10,11 +10,11 @@ func init() { //nolint Args: args{ Args: []cmdArgs{ []string{"scan", "--exclude-paths", "/path/test/fixtures/test_swagger/swaggerFileWithoutAuthorizer.yaml", - "-p", "/path/test/fixtures/test_swagger/"}, + "-p", "/path/test/fixtures/test_swagger/", "-v"}, }, }, Validation: func(outputText string) bool { - match, _ := regexp.MatchString(`Files scanned: 1`, outputText) + match, _ := regexp.MatchString(`Scanned Files: 1`, outputText) return match }, WantStatus: []int{40}, diff --git a/pkg/engine/inspector.go b/pkg/engine/inspector.go index bf4ecb179a3..cf26def1487 100644 --- a/pkg/engine/inspector.go +++ b/pkg/engine/inspector.go @@ -309,6 +309,7 @@ func (c *Inspector) doRun(ctx *QueryContext) (vulns []model.Vulnerability, err e if r := recover(); r != nil { errMessage := fmt.Sprintf("Recovered from panic during query '%s' run. ", ctx.Query.Metadata.Query) err = fmt.Errorf("panic: %v", r) + fmt.Println() log.Err(err).Msg(errMessage) } }() diff --git a/pkg/printer/options.go b/pkg/printer/options.go index 7e36a1df397..7eb56131b63 100644 --- a/pkg/printer/options.go +++ b/pkg/printer/options.go @@ -138,3 +138,23 @@ func LogLevel(opt interface{}, changed bool) error { } return nil } + +type LogSink struct { + logs []string +} + +func NewLogger(logs *LogSink) zerolog.Logger { + if logs == nil { + return log.Logger + } + return zerolog.New(logs) +} + +func (l *LogSink) Write(p []byte) (n int, err error) { + l.logs = append(l.logs, string(p)) + return len(p), nil +} + +func (l *LogSink) Index(i int) string { + return l.logs[i] +} diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go index 5557bc91d5e..324fac59c92 100644 --- a/pkg/printer/printer.go +++ b/pkg/printer/printer.go @@ -18,7 +18,6 @@ import ( ) const ( - wordWrapCount = 5 charsLimitPerLine = 255 ) @@ -102,19 +101,9 @@ func WordWrap(s, indentation string, limit int) string { } // PrintResult prints on output the summary results -func PrintResult(summary *model.Summary, failedQueries map[string]error, printer *Printer, usingCustomQueries bool) error { +func PrintResult(summary *model.Summary, printer *Printer, usingCustomQueries bool) error { log.Debug().Msg("helpers.PrintResult()") - fmt.Printf("Files scanned: %d\n", summary.ScannedFiles) - fmt.Printf("Parsed files: %d\n", summary.ParsedFiles) - fmt.Printf("Queries loaded: %d\n", summary.TotalQueries) - - fmt.Printf("Queries failed to execute: %d\n\n", summary.FailedToExecuteQueries) - for queryName, err := range failedQueries { - fmt.Printf("\t- %s:\n", queryName) - fmt.Printf("%s", WordWrap(err.Error(), "\t\t", wordWrapCount)) - } - - fmt.Printf("------------------------------------\n\n") + fmt.Printf("\n\n") for index := range summary.Queries { idx := len(summary.Queries) - index - 1 if summary.Queries[idx].Severity == model.SeverityTrace { diff --git a/pkg/printer/printer_test.go b/pkg/printer/printer_test.go index bad40aaee3a..42a49874d78 100644 --- a/pkg/printer/printer_test.go +++ b/pkg/printer/printer_test.go @@ -108,159 +108,34 @@ var printTests = []struct { expectedResultFull string customQueries bool }{ + { - caseTest: test.ComplexSummaryMock, - expectedResult: "Files scanned: 2\n" + - "Parsed files: 2\n" + - "Queries loaded: 3\n" + - "Queries failed to execute: 0\n\n" + - "------------------------------------\n\n" + - "AMI Not Encrypted, Severity: HIGH, Results: 2\n" + - "\t[1]: positive.tf:30\n" + - "\t[2]: positive.tf:35\n" + - "AmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n" + - "\t[1]: positive.tf:1\n" + - "ALB protocol is HTTP, Severity: HIGH, Results: 2\n" + - "\t[1]: positive.tf:25\n" + - "\t[2]: positive.tf:19\n\n" + - "Results Summary:\n" + - "HIGH: 4\n" + - "MEDIUM: 1\n" + - "LOW: 0\n" + - "INFO: 0\n" + - "TOTAL: 5\n\n", - expectedResultFull: "Files scanned: 2\n" + - "Parsed files: 2\n" + - "Queries loaded: 3\n" + - "Queries failed to execute: 0\n\n" + - "------------------------------------\n\n" + - "AMI Not Encrypted, Severity: HIGH, Results: 2\n" + - "Description: AWS AMI Encryption is not enabled\n" + - "Platform: \n" + - "CWE: 22\n" + - "Learn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/97707503-a22c-4cd7-b7c0-f088fa7cf830\n\n" + - "\t[1]: positive.tf:30\n\n\n\n" + - "\t[2]: positive.tf:35\n\n\n\n" + - "AmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n" + - "Description: AmazonMQ Broker should have Encryption Options defined\nPlatform: \n" + - "Learn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/aws/3db3f534-e3a3-487f-88c7-0a9fbf64b702\n\n" + - "\t[1]: positive.tf:1\n\n\n\n" + - "ALB protocol is HTTP, Severity: HIGH, Results: 2\n" + - "Description: ALB protocol is HTTP Description\n" + - "Platform: \n" + - "Learn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/de7f5e83-da88-4046-871f-ea18504b1d43\n\n" + - "\t[1]: positive.tf:25\n\n\n\n" + - "\t[2]: positive.tf:19\n\n\n\n\n" + - "Results Summary:\n" + - "HIGH: 4\n" + - "MEDIUM: 1\n" + - "LOW: 0\n" + - "INFO: 0\n" + - "TOTAL: 5\n\n", - customQueries: false, + caseTest: test.ComplexSummaryMock, + expectedResult: "\n\nAMI Not Encrypted, Severity: HIGH, Results: 2\n\t[1]: positive.tf:30\n\t[2]: positive.tf:35\nAmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n\t[1]: positive.tf:1\nALB protocol is HTTP, Severity: HIGH, Results: 2\n\t[1]: positive.tf:25\n\t[2]: positive.tf:19\n\nResults Summary:\nHIGH: 4\nMEDIUM: 1\nLOW: 0\nINFO: 0\nTOTAL: 5\n\n", + expectedResultFull: "\n\nAMI Not Encrypted, Severity: HIGH, Results: 2\nDescription: AWS AMI Encryption is not enabled\nPlatform: \nCWE: 22\nLearn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/97707503-a22c-4cd7-b7c0-f088fa7cf830\n\n\t[1]: positive.tf:30\n\n\n\n\t[2]: positive.tf:35\n\n\n\nAmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\nDescription: AmazonMQ Broker should have Encryption Options defined\nPlatform: \nLearn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/aws/3db3f534-e3a3-487f-88c7-0a9fbf64b702\n\n\t[1]: positive.tf:1\n\n\n\nALB protocol is HTTP, Severity: HIGH, Results: 2\nDescription: ALB protocol is HTTP Description\nPlatform: \nLearn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/de7f5e83-da88-4046-871f-ea18504b1d43\n\n\t[1]: positive.tf:25\n\n\n\n\t[2]: positive.tf:19\n\n\n\n\nResults Summary:\nHIGH: 4\nMEDIUM: 1\nLOW: 0\nINFO: 0\nTOTAL: 5\n\n", + customQueries: false, }, { - caseTest: test.ComplexSummaryMock, - expectedResult: "Files scanned: 2\n" + - "Parsed files: 2\n" + - "Queries loaded: 3\n" + - "Queries failed to execute: 0\n\n" + - "------------------------------------\n\n" + - "AMI Not Encrypted, Severity: HIGH, Results: 2\n" + - "\t[1]: positive.tf:30\n" + - "\t[2]: positive.tf:35\n" + - "AmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n" + - "\t[1]: positive.tf:1\n" + - "ALB protocol is HTTP, Severity: HIGH, Results: 2\n" + - "\t[1]: positive.tf:25\n" + - "\t[2]: positive.tf:19\n\n" + - "Results Summary:\n" + - "HIGH: 4\n" + - "MEDIUM: 1\n" + - "LOW: 0\n" + - "INFO: 0\n" + - "TOTAL: 5\n\n", - expectedResultFull: "Files scanned: 2\n" + - "Parsed files: 2\n" + - "Queries loaded: 3\n" + - "Queries failed to execute: 0\n\n" + - "------------------------------------\n\n" + - "AMI Not Encrypted, Severity: HIGH, Results: 2\n" + - "Description: AWS AMI Encryption is not enabled\n" + - "Platform: \n" + - "CWE: 22\n" + - "\t[1]: positive.tf:30\n\n\n\n" + - "\t[2]: positive.tf:35\n\n\n\n" + - "AmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n" + - "Description: AmazonMQ Broker should have Encryption Options defined\nPlatform: \n" + - "\t[1]: positive.tf:1\n\n\n\n" + - "ALB protocol is HTTP, Severity: HIGH, Results: 2\n" + - "Description: ALB protocol is HTTP Description\n" + - "Platform: \n" + - "\t[1]: positive.tf:25\n\n\n\n" + - "\t[2]: positive.tf:19\n\n\n\n\n" + - "Results Summary:\n" + - "HIGH: 4\n" + - "MEDIUM: 1\n" + - "LOW: 0\n" + - "INFO: 0\n" + - "TOTAL: 5\n\n", - customQueries: true, + caseTest: test.ComplexSummaryMock, + expectedResult: "\n\nAMI Not Encrypted, Severity: HIGH, Results: 2\n\t[1]: positive.tf:30\n\t[2]: positive.tf:35\nAmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n\t[1]: positive.tf:1\nALB protocol is HTTP, Severity: HIGH, Results: 2\n\t[1]: positive.tf:25\n\t[2]: positive.tf:19\n\nResults Summary:\nHIGH: 4\nMEDIUM: 1\nLOW: 0\nINFO: 0\nTOTAL: 5\n\n", + expectedResultFull: "\n\nAMI Not Encrypted, Severity: HIGH, Results: 2\nDescription: AWS AMI Encryption is not enabled\nPlatform: \nCWE: 22\n\t[1]: positive.tf:30\n\n\n\n\t[2]: positive.tf:35\n\n\n\nAmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\nDescription: AmazonMQ Broker should have Encryption Options defined\nPlatform: \n\t[1]: positive.tf:1\n\n\n\nALB protocol is HTTP, Severity: HIGH, Results: 2\nDescription: ALB protocol is HTTP Description\nPlatform: \n\t[1]: positive.tf:25\n\n\n\n\t[2]: positive.tf:19\n\n\n\n\nResults Summary:\nHIGH: 4\nMEDIUM: 1\nLOW: 0\nINFO: 0\nTOTAL: 5\n\n", + customQueries: true, }, { - caseTest: test.ComplexSummaryMockWithExperimental, - expectedResult: "Files scanned: 2\n" + - "Parsed files: 2\n" + - "Queries loaded: 2\n" + - "Queries failed to execute: 0\n\n" + - "------------------------------------\n\n" + - "AmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n" + - "\t[1]: positive.tf:1\n" + - "ALB protocol is HTTP, Severity: HIGH, Results: 2\n" + - "Note: this is an experimental query\n" + - "\t[1]: positive.tf:25\n" + - "\t[2]: positive.tf:19\n\n" + - "Results Summary:\n" + - "HIGH: 2\n" + - "MEDIUM: 1\n" + - "LOW: 0\n" + - "INFO: 0\n" + - "TOTAL: 3\n\n", - expectedResultFull: "Files scanned: 2\n" + - "Parsed files: 2\n" + - "Queries loaded: 2\n" + - "Queries failed to execute: 0\n\n" + - "------------------------------------\n\n" + - "AmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n" + - "Description: AmazonMQ Broker should have Encryption Options defined\nPlatform: \n" + - "Learn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/aws/3db3f534-e3a3-487f-88c7-0a9fbf64b702\n\n" + - "\t[1]: positive.tf:1\n\n\n\n" + - "ALB protocol is HTTP, Severity: HIGH, Results: 2\n" + - "Note: this is an experimental query\n" + - "Description: ALB protocol is HTTP Description\n" + - "Platform: \n" + - "Learn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/de7f5e83-da88-4046-871f-ea18504b1d43\n\n" + - "\t[1]: positive.tf:25\n\n\n\n" + - "\t[2]: positive.tf:19\n\n\n\n\n" + - "Results Summary:\n" + - "HIGH: 2\n" + - "MEDIUM: 1\n" + - "LOW: 0\n" + - "INFO: 0\n" + - "TOTAL: 3\n\n", - customQueries: false, + caseTest: test.ComplexSummaryMockWithExperimental, + expectedResult: "\n\nAmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\n\t[1]: positive.tf:1\nALB protocol is HTTP, Severity: HIGH, Results: 2\nNote: this is an experimental query\n\t[1]: positive.tf:25\n\t[2]: positive.tf:19\n\nResults Summary:\nHIGH: 2\nMEDIUM: 1\nLOW: 0\nINFO: 0\nTOTAL: 3\n\n", + expectedResultFull: "\n\nAmazonMQ Broker Encryption Disabled, Severity: MEDIUM, Results: 1\nDescription: AmazonMQ Broker should have Encryption Options defined\nPlatform: \nLearn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/aws/3db3f534-e3a3-487f-88c7-0a9fbf64b702\n\n\t[1]: positive.tf:1\n\n\n\nALB protocol is HTTP, Severity: HIGH, Results: 2\nNote: this is an experimental query\nDescription: ALB protocol is HTTP Description\nPlatform: \nLearn more about this vulnerability: https://docs.kics.io/latest/queries/-queries/de7f5e83-da88-4046-871f-ea18504b1d43\n\n\t[1]: positive.tf:25\n\n\n\n\t[2]: positive.tf:19\n\n\n\n\nResults Summary:\nHIGH: 2\nMEDIUM: 1\nLOW: 0\nINFO: 0\nTOTAL: 3\n\n", + customQueries: false, }, } -var failedQueries = map[string]error{} - // TestPrintResult tests the functions [PrintResult()] and all the methods called by them func TestPrintResult(t *testing.T) { color.Disable() for idx, testCase := range printTests { t.Run(fmt.Sprintf("Print test case %d", idx), func(t *testing.T) { out, err := test.CaptureOutput(func() error { - return PrintResult(&testCase.caseTest, failedQueries, NewPrinter(true), testCase.customQueries) + return PrintResult(&testCase.caseTest, NewPrinter(true), testCase.customQueries) }) require.NoError(t, err) require.Equal(t, testCase.expectedResult, out) @@ -270,7 +145,7 @@ func TestPrintResult(t *testing.T) { for idx, testCase := range printTests { t.Run(fmt.Sprintf("Print test case %d no minimal", idx), func(t *testing.T) { out, err := test.CaptureOutput(func() error { - return PrintResult(&testCase.caseTest, failedQueries, NewPrinter(false), testCase.customQueries) + return PrintResult(&testCase.caseTest, NewPrinter(false), testCase.customQueries) }) require.NoError(t, err) require.Equal(t, testCase.expectedResultFull, out) diff --git a/pkg/printer/utils.go b/pkg/printer/utils.go index 22f3af454dc..0426410f1f0 100644 --- a/pkg/printer/utils.go +++ b/pkg/printer/utils.go @@ -1,11 +1,10 @@ package printer import ( - "fmt" "time" consoleFlags "github.com/Checkmarx/kics/internal/console/flags" - "github.com/rs/zerolog/log" + "github.com/rs/zerolog" ) func validateFlags() error { @@ -28,14 +27,12 @@ func validateFlags() error { } // PrintScanDuration prints the scan duration -func PrintScanDuration(elapsed time.Duration) { +func PrintScanDuration(logger *zerolog.Logger, elapsed time.Duration) { if consoleFlags.GetBoolFlag(consoleFlags.CIFlag) { elapsedStrFormat := "Scan duration: %vms\n" - fmt.Printf(elapsedStrFormat, elapsed.Milliseconds()) - log.Info().Msgf(elapsedStrFormat, elapsed.Milliseconds()) + (*logger).Info().Msgf(elapsedStrFormat, elapsed.Milliseconds()) } else { elapsedStrFormat := "Scan duration: %v\n" - fmt.Printf(elapsedStrFormat, elapsed) - log.Info().Msgf(elapsedStrFormat, elapsed) + (*logger).Info().Msgf(elapsedStrFormat, elapsed) } } diff --git a/pkg/printer/utils_test.go b/pkg/printer/utils_test.go index 83591f07148..b17d3cd55e2 100644 --- a/pkg/printer/utils_test.go +++ b/pkg/printer/utils_test.go @@ -1,14 +1,12 @@ package printer import ( - "io/ioutil" - "os" "testing" "time" "github.com/Checkmarx/kics/internal/console/flags" "github.com/spf13/cobra" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) func Test_PrintScanDuration(t *testing.T) { @@ -33,12 +31,12 @@ func Test_PrintScanDuration(t *testing.T) { { name: "should print scan duration", cmd: mockCmd, - flagsListContent: "", + flagsListContent: ``, persintentFlag: true, supportedPlatforms: []string{"terraform"}, supportedCloudProviders: []string{"aws"}, elapsed: time.Duration(1), - expected: "Scan duration: 1ns\n", + expected: "Scan duration: 1ns", }, { name: "should print scan duration when ci flag is true", @@ -53,7 +51,7 @@ func Test_PrintScanDuration(t *testing.T) { supportedPlatforms: []string{"terraform"}, supportedCloudProviders: []string{"aws"}, elapsed: time.Duration(1), - expected: "Scan duration: 0ms\n", + expected: "Scan duration: 0ms", }, } @@ -61,17 +59,13 @@ func Test_PrintScanDuration(t *testing.T) { t.Run(test.name, func(t *testing.T) { flags.InitJSONFlags(test.cmd, test.flagsListContent, test.persintentFlag, test.supportedPlatforms, test.supportedCloudProviders) - rescueStdout := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w + myBuffer := LogSink{} + logger := NewLogger(&myBuffer) - PrintScanDuration(test.elapsed) + PrintScanDuration(&logger, test.elapsed) + aux := myBuffer.Index(0) - w.Close() - out, _ := ioutil.ReadAll(r) - os.Stdout = rescueStdout - - require.Equal(t, test.expected, string(out)) + assert.Contains(t, aux, test.expected) }) } } diff --git a/pkg/report/commons.go b/pkg/report/commons.go index b4776222b17..3e379a18130 100644 --- a/pkg/report/commons.go +++ b/pkg/report/commons.go @@ -61,7 +61,6 @@ func getCurrentTime() string { func fileCreationReport(path, filename string) { log.Info().Str("fileName", filename).Msgf("Results saved to file %s", path) - fmt.Printf("Results saved to file %s\n", path) } func closeFile(path, filename string, file *os.File) { diff --git a/pkg/scan/post_scan.go b/pkg/scan/post_scan.go index e73a07b8f3e..be0f9ee6148 100644 --- a/pkg/scan/post_scan.go +++ b/pkg/scan/post_scan.go @@ -52,14 +52,13 @@ func (c *Client) getSummary(results []model.Vulnerability, end time.Time, pathPa func (c *Client) resolveOutputs( summary *model.Summary, documents model.Documents, - failedQueries map[string]error, printer *consolePrinter.Printer, proBarBuilder progress.PbBuilder, ) error { log.Debug().Msg("console.resolveOutputs()") usingCustomQueries := usingCustomQueries(c.ScanParams.QueriesPath) - if err := consolePrinter.PrintResult(summary, failedQueries, printer, usingCustomQueries); err != nil { + if err := consolePrinter.PrintResult(summary, printer, usingCustomQueries); err != nil { return err } if c.ScanParams.PayloadPath != "" { @@ -124,7 +123,6 @@ func (c *Client) postScan(scanResults *Results) error { if err := c.resolveOutputs( &summary, scanResults.Files.Combine(c.ScanParams.LineInfoPayload), - scanResults.FailedQueries, c.Printer, *c.ProBarBuilder); err != nil { log.Err(err) @@ -133,7 +131,8 @@ func (c *Client) postScan(scanResults *Results) error { deleteExtractionFolder(scanResults.ExtractedPaths.ExtractionMap) - consolePrinter.PrintScanDuration(time.Since(c.ScanStartTime)) + logger := consolePrinter.NewLogger(nil) + consolePrinter.PrintScanDuration(&logger, time.Since(c.ScanStartTime)) printVersionCheck(c.Printer, &summary)