diff --git a/e2e/fixtures/E2E_CLI_081_RESULT.json b/e2e/fixtures/E2E_CLI_081_RESULT.json new file mode 100644 index 00000000000..055b9cee8ca --- /dev/null +++ b/e2e/fixtures/E2E_CLI_081_RESULT.json @@ -0,0 +1,55 @@ +{ + "kics_version": "development", + "files_scanned": 1, + "lines_scanned": 78, + "files_parsed": 1, + "lines_parsed": 78, + "lines_ignored": 0, + "files_failed_to_scan": 0, + "queries_total": 43, + "queries_failed_to_execute": 0, + "queries_failed_to_compute_similarity_id": 0, + "scan_id": "console", + "severity_counters": { + "HIGH": 1, + "INFO": 0, + "LOW": 0, + "MEDIUM": 0, + "TRACE": 0 + }, + "total_counter": 1, + "total_bom_resources": 0, + "start": "2024-02-12T12:34:07.3154393Z", + "end": "2024-02-12T12:34:25.658434Z", + "paths": [ + "/path/test/fixtures/test_output_path" + ], + "queries": [ + { + "query_name": "Azure Instance Using Basic Authentication", + "query_id": "6797f581-0433-4768-ae3e-7ceb2f8b138e", + "query_url": "https://docs.microsoft.com/en-us/azure/templates/microsoft.compute/virtualmachines?tabs=json#linuxconfiguration-object", + "severity": "HIGH", + "platform": "AzureResourceManager", + "category": "Best Practices", + "experimental": false, + "description": "Azure Instances should use SSH Key instead of basic authentication", + "description_id": "98ba05ca", + "files": [ + { + "file_name": "..\\test\\fixtures\\test_output_path\\positive1.json", + "similarity_id": "42d73d5b2fa1fbcb1145ea43b7dc4ec20f92adda85c61161b6a7714b6cd86219", + "line": 53, + "resource_type": "Microsoft.Compute/virtualMachines", + "resource_name": "[variables('vmName')]", + "issue_type": "IncorrectValue", + "search_key": "resources.name=[variables('vmName')].properties.osProfile.linuxConfiguration.disablePasswordAuthentication", + "search_line": 53, + "search_value": "", + "expected_value": "'disablePasswordAuthentication' should be set to true", + "actual_value": "'disablePasswordAuthentication' property value is set to false" + } + ] + } + ] +} diff --git a/e2e/fixtures/E2E_CLI_082_RESULT b/e2e/fixtures/E2E_CLI_082_RESULT new file mode 100644 index 00000000000..da4219a4fa0 --- /dev/null +++ b/e2e/fixtures/E2E_CLI_082_RESULT @@ -0,0 +1,2 @@ +Error: the directory name you provided for the output-path flag contains invalid characters +{{.ScanHelp}} diff --git a/e2e/testcases/e2e-cli-081_output_path_valid.go b/e2e/testcases/e2e-cli-081_output_path_valid.go new file mode 100644 index 00000000000..a362f3a16d5 --- /dev/null +++ b/e2e/testcases/e2e-cli-081_output_path_valid.go @@ -0,0 +1,26 @@ +package testcases + +// E2E-CLI-081 - KICS scan +// should check if output path is valid +func init() { //nolint + testSample := TestCase{ + Name: "should check if output path is valid [E2E-CLI-081]", + Args: args{ + Args: []cmdArgs{ + []string{"scan", "-o", "/path/e2e/output", + "--output-name", "E2E_CLI_081_RESULT", + "-p", "\"/path/test/fixtures/test_output_path\"", + }, + }, + ExpectedResult: []ResultsValidation{ + { + ResultsFile: "E2E_CLI_081_RESULT", + ResultsFormats: []string{"json"}, + }, + }, + }, + WantStatus: []int{50}, + } + + Tests = append(Tests, testSample) +} diff --git a/e2e/testcases/e2e-cli-082_output_path_invalid.go b/e2e/testcases/e2e-cli-082_output_path_invalid.go new file mode 100644 index 00000000000..b68ef31b3d9 --- /dev/null +++ b/e2e/testcases/e2e-cli-082_output_path_invalid.go @@ -0,0 +1,23 @@ +package testcases + +// E2E-CLI-082 - KICS scan +// should check if output path is invalid +func init() { //nolint + testSample := TestCase{ + Name: "should check if output path is invalid [E2E-CLI-082]", + Args: args{ + Args: []cmdArgs{ + []string{"scan", "-o", "/path/e2e/output?", + "--output-name", "E2E_CLI_082_RESULT", + "-p", "\"/path/test/fixtures/test_output_path\"", + }, + }, + ExpectedOut: []string{ + "E2E_CLI_082_RESULT", + }, + }, + WantStatus: []int{126}, + } + + Tests = append(Tests, testSample) +} diff --git a/internal/console/assets/scan-flags.json b/internal/console/assets/scan-flags.json index 6807a0ab05c..81422eccdcf 100644 --- a/internal/console/assets/scan-flags.json +++ b/internal/console/assets/scan-flags.json @@ -119,7 +119,8 @@ "flagType": "str", "shorthandFlag": "o", "defaultValue": "", - "usage": "directory path to store reports" + "usage": "directory path to store reports", + "validation": "validatePath" }, "path": { "flagType": "multiStr", diff --git a/internal/console/flags/validate.go b/internal/console/flags/validate.go index 1a356b0aa23..8b4322b92d9 100644 --- a/internal/console/flags/validate.go +++ b/internal/console/flags/validate.go @@ -9,6 +9,7 @@ var flagValidationFuncs = flagValidationFuncsMap{ "validateMultiStrEnum": validateMultiStrEnum, "validateStrEnum": validateStrEnum, "allQueriesID": allQueriesID, + "validatePath": validatePath, } func isQueryID(id string) bool { diff --git a/internal/console/flags/validate_path.go b/internal/console/flags/validate_path.go new file mode 100644 index 00000000000..9582128ff6c --- /dev/null +++ b/internal/console/flags/validate_path.go @@ -0,0 +1,22 @@ +package flags + +import ( + "errors" + "regexp" +) + +func validatePath(flagName string) error { + relPath := `^(?:\.\.\\|\.\\|\.\.\/|\.\/|\\|\/)?(?:[^<>:"\/\\|?*]+[\\\/])*[^<>:"\/\\|?*]+(\/|\\)?$` + absPath := `^[a-zA-Z]:[\\\/](?:[^<>:"\/\\|?*]+[\\\/])*[^<>:"\/\\|?*]+(?:\/|\\)?$` + regex := regexp.MustCompile(relPath + `|` + absPath) + + path := GetStrFlag(flagName) + isValid := regex.MatchString(path) || path == "" + + if !isValid { + errorMsg := "the directory name you provided for the " + flagName + " flag contains invalid characters" + return errors.New(errorMsg) + } + + return nil +} diff --git a/internal/console/flags/validate_path_test.go b/internal/console/flags/validate_path_test.go new file mode 100644 index 00000000000..d5beb232151 --- /dev/null +++ b/internal/console/flags/validate_path_test.go @@ -0,0 +1,136 @@ +package flags + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFlags_validatePathEnum(t *testing.T) { + tests := []struct { + name string + flagName string + flagValue string + wantErr bool + }{ + { + name: "should execute fine", + flagName: "output-path", + flagValue: "C:/Users/user/files", + wantErr: false, + }, + { + name: "should execute fine", + flagName: "output-path", + flagValue: "/file", + wantErr: false, + }, + { + name: "should execute fine", + flagName: "output-path", + flagValue: "file", + wantErr: false, + }, + { + name: "should execute fine", + flagName: "output-path", + flagValue: "C:\\Users\\user\\.file", + wantErr: false, + }, + { + name: "should execute fine", + flagName: "output-path", + flagValue: "/user/files", + wantErr: false, + }, + { + name: "should execute fine", + flagName: "output-path", + flagValue: "\\user\\file", + wantErr: false, + }, + { + name: "should execute fine", + flagName: "output-path", + flagValue: "user\\file", + wantErr: false, + }, + { + name: "should execute fine", + flagName: "output-path", + flagValue: "./user/files", + wantErr: false, + }, + { + name: "should execute fine", + flagName: "output-path", + flagValue: "../user/files", + wantErr: false, + }, + { + name: "should return an error regarding invalid characters (*)", + flagName: "output-path", + flagValue: "../user/fil*es", + wantErr: true, + }, + { + name: "should return an error regarding invalid characters (|)", + flagName: "output-path", + flagValue: "C:/Users/user/files/|", + wantErr: true, + }, + { + name: "should return an error regarding invalid characters (\")", + flagName: "output-path", + flagValue: "C:/Users/user/files/\"", + wantErr: true, + }, + { + name: "should return an error regarding invalid characters (?)", + flagName: "output-path", + flagValue: "C:/Users/user/files/?", + wantErr: true, + }, + { + name: "should return an error regarding invalid characters (?)", + flagName: "output-path", + flagValue: "..\file?", + wantErr: true, + }, + { + name: "should return an error regarding invalid characters (>)", + flagName: "output-path", + flagValue: "C:/Users/user/files/>", + wantErr: true, + }, + { + name: "should return an error regarding invalid characters (<)", + flagName: "output-path", + flagValue: "C:/Users/user/files/<", + wantErr: true, + }, + { + name: "should return an error regarding invalid characters (*)", + flagName: "output-path", + flagValue: "C:/Users/user/files/*", + wantErr: true, + }, + { + name: "should return an error regarding invalid characters", + flagName: "output-path", + flagValue: "c:/**/:??/folder", + wantErr: true, + }, + } + for _, test := range tests { + flagsStrReferences[test.flagName] = &test.flagValue + t.Run(test.name, func(t *testing.T) { + gotErr := validatePath(test.flagName) + if !test.wantErr { + require.NoError(t, gotErr) + } else { + require.Error(t, gotErr) + } + }) + } +} diff --git a/test/fixtures/test_output_path/positive1.json b/test/fixtures/test_output_path/positive1.json new file mode 100644 index 00000000000..4c3b06490fc --- /dev/null +++ b/test/fixtures/test_output_path/positive1.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "projectName": { + "type": "string", + "metadata": { + "description": "Specifies a name for generating resource names." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Specifies the location for all resources." + } + }, + "adminUsername": { + "type": "string", + "metadata": { + "description": "Specifies a username for the Virtual Machine." + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_D2s_v3", + "metadata": { + "description": "description" + } + } + }, + "variables": { + "vmName": "[concat(parameters('projectName'), '-vm')]", + "networkInterfaceName": "[concat(parameters('projectName'), '-nic')]" + }, + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-03-01", + "name": "[variables('vmName')]", + "location": "[parameters('location')]", + "dependsOn": [ + "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]" + ], + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "osProfile": { + "computerName": "[variables('vmName')]", + "adminUsername": "[parameters('adminUsername')]", + "linuxConfiguration": { + "disablePasswordAuthentication": false + } + }, + "storageProfile": { + "imageReference": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "18.04-LTS", + "version": "latest" + }, + "osDisk": { + "createOption": "FromImage" + } + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('networkInterfaceName'))]" + } + ] + } + } + } + ] +}