diff --git a/Dockerfile b/Dockerfile index b79e429be81..38dd8c7611a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,6 +67,7 @@ RUN wget https://github.com/GoogleCloudPlatform/terraformer/releases/download/0. COPY --from=build_env /app/bin/kics /app/bin/kics COPY --from=build_env /app/assets/queries /app/bin/assets/queries COPY --from=build_env /app/assets/libraries/* /app/bin/assets/libraries/ +COPY --from=build_env /app/assets/utils/* /app/bin/assets/utils/ WORKDIR /app/bin diff --git a/assets/utils/experimental-queries.json b/assets/utils/experimental-queries.json new file mode 100644 index 00000000000..32960f8ced3 --- /dev/null +++ b/assets/utils/experimental-queries.json @@ -0,0 +1,2 @@ +[ +] \ No newline at end of file diff --git a/docs/commands.md b/docs/commands.md index 96bfbd47781..ad6f93af765 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -67,6 +67,10 @@ Flags: can be provided multiple times or as a comma separated string example: 'info,low' possible values: 'high, medium, low, info, trace' + --experimental-queries strings include experimental queries (queries not yet thoroughly reviewed) by providing the path of the queries folder + can be provided multiple times or as a comma-separated string (platform/cloudProvider or platform) + example: 'terraform/databricks' + possible values found in: '/assets/utils/experimental-queries.json' --fail-on strings which kind of results should return an exit code different from 0 accepts: high, medium, low and info example: "high,low" (default [high,medium,low,info]) diff --git a/docs/dockerhub.md b/docs/dockerhub.md index 8ef805bf29e..6a09fd8865a 100644 --- a/docs/dockerhub.md +++ b/docs/dockerhub.md @@ -104,6 +104,10 @@ Flags: --exclude-severities strings exclude results by providing the severity of a result can be provided multiple times or as a comma separated string example: 'info,low' + --experimental-queries strings include experimental queries (queries not yet thoroughly reviewed) by providing the path of the queries folder + can be provided multiple times or as a comma-separated string (platform/cloudProvider or platform) + example: 'terraform/databricks' + possible values found in: '/assets/utils/experimental-queries.json' --fail-on strings which kind of results should return an exit code different from 0 accepts: high, medium, low and info example: "high,low" (default [high,medium,low,info]) diff --git a/e2e/fixtures/assets/scan_help b/e2e/fixtures/assets/scan_help index 587a6d05553..d583c415e36 100644 --- a/e2e/fixtures/assets/scan_help +++ b/e2e/fixtures/assets/scan_help @@ -2,62 +2,66 @@ Usage: kics scan [flags] Flags: - -m, --bom include bill of materials (BoM) in results output - --cloud-provider strings list of cloud providers to scan (alicloud, aws, azure, gcp) - --config string path to configuration file - --disable-full-descriptions disable request for full descriptions and use default vulnerability descriptions - --disable-secrets disable secrets scanning - --exclude-categories strings exclude categories by providing its name - cannot be provided with query inclusion flags - can be provided multiple times or as a comma separated string - example: 'Access control,Best practices' - --exclude-gitignore disables the exclusion of paths specified within .gitignore file - -e, --exclude-paths strings exclude paths from scan - supports glob and can be provided multiple times or as a quoted comma separated string - example: './shouldNotScan/*,somefile.txt' - --exclude-queries strings exclude queries by providing the query ID - cannot be provided with query inclusion flags - can be provided multiple times or as a comma separated string - example: 'e69890e6-fce5-461d-98ad-cb98318dfc96,4728cd65-a20c-49da-8b31-9c08b423e4db' - -x, --exclude-results strings exclude results by providing the similarity ID of a result - can be provided multiple times or as a comma separated string - example: 'fec62a97d569662093dbb9739360942f...,31263s5696620s93dbb973d9360942fc2a...' - --exclude-severities strings exclude results by providing the severity of a result - can be provided multiple times or as a comma separated string - example: 'info,low' - --exclude-type strings case insensitive list of platform types not to scan - (Ansible, AzureResourceManager, Buildah, CICD, CloudFormation, Crossplane, DockerCompose, Dockerfile, GRPC, GoogleDeploymentManager, Knative, Kubernetes, OpenAPI, Pulumi, ServerlessFW, Terraform) - cannot be provided with type inclusion flags - --fail-on strings which kind of results should return an exit code different from 0 - accepts: high, medium, low and info - example: "high,low" (default [high,medium,low,info]) - -h, --help help for scan - --ignore-on-exit string defines which kind of non-zero exits code should be ignored - accepts: all, results, errors, none - example: if 'results' is set, only engine errors will make KICS exit code different from 0 (default "none") - -i, --include-queries strings include queries by providing the query ID - cannot be provided with query exclusion flags - can be provided multiple times or as a comma separated string - example: 'e69890e6-fce5-461d-98ad-cb98318dfc96,4728cd65-a20c-49da-8b31-9c08b423e4db' - --input-data string path to query input data files - -b, --libraries-path string path to directory with libraries (default "./assets/libraries") - --minimal-ui simplified version of CLI output - --no-progress hides the progress bar - --output-name string name used on report creations (default "results") - -o, --output-path string directory path to store reports - -p, --path strings paths or directories to scan - example: "./somepath,somefile.txt" - --payload-lines adds line information inside the payload when printing the payload file - -d, --payload-path string path to store internal representation JSON file - --preview-lines int number of lines to be display in CLI results (min: 1, max: 30) (default 3) - -q, --queries-path strings paths to directory with queries (default [./assets/queries]) - --report-formats strings formats in which the results will be exported (all, asff, codeclimate, csv, cyclonedx, glsast, html, json, junit, pdf, sarif, sonarqube) (default [json]) - -r, --secrets-regexes-path string path to secrets regex rules configuration file - --terraform-vars-path string path where terraform variables are present - --timeout int number of seconds the query has to execute before being canceled (default 60) - -t, --type strings case insensitive list of platform types to scan - (Ansible, AzureResourceManager, Buildah, CICD, CloudFormation, Crossplane, DockerCompose, Dockerfile, GRPC, GoogleDeploymentManager, Knative, Kubernetes, OpenAPI, Pulumi, ServerlessFW, Terraform) - cannot be provided with type exclusion flags + -m, --bom include bill of materials (BoM) in results output + --cloud-provider strings list of cloud providers to scan (alicloud, aws, azure, gcp) + --config string path to configuration file + --disable-full-descriptions disable request for full descriptions and use default vulnerability descriptions + --disable-secrets disable secrets scanning + --exclude-categories strings exclude categories by providing its name + cannot be provided with query inclusion flags + can be provided multiple times or as a comma separated string + example: 'Access control,Best practices' + --exclude-gitignore disables the exclusion of paths specified within .gitignore file + -e, --exclude-paths strings exclude paths from scan + supports glob and can be provided multiple times or as a quoted comma separated string + example: './shouldNotScan/*,somefile.txt' + --exclude-queries strings exclude queries by providing the query ID + cannot be provided with query inclusion flags + can be provided multiple times or as a comma separated string + example: 'e69890e6-fce5-461d-98ad-cb98318dfc96,4728cd65-a20c-49da-8b31-9c08b423e4db' + -x, --exclude-results strings exclude results by providing the similarity ID of a result + can be provided multiple times or as a comma separated string + example: 'fec62a97d569662093dbb9739360942f...,31263s5696620s93dbb973d9360942fc2a...' + --exclude-severities strings exclude results by providing the severity of a result + can be provided multiple times or as a comma separated string + example: 'info,low' + --exclude-type strings case insensitive list of platform types not to scan + (Ansible, AzureResourceManager, Buildah, CICD, CloudFormation, Crossplane, DockerCompose, Dockerfile, GRPC, GoogleDeploymentManager, Knative, Kubernetes, OpenAPI, Pulumi, ServerlessFW, Terraform) + cannot be provided with type inclusion flags + --experimental-queries strings include experimental queries (queries not yet thoroughly reviewed) by providing the path of the queries folder + can be provided multiple times or as a comma-separated string (platform/cloudProvider or platform) + example: 'terraform/databricks' + possible values found in: '/assets/utils/experimental-queries.json' + --fail-on strings which kind of results should return an exit code different from 0 + accepts: high, medium, low and info + example: "high,low" (default [high,medium,low,info]) + -h, --help help for scan + --ignore-on-exit string defines which kind of non-zero exits code should be ignored + accepts: all, results, errors, none + example: if 'results' is set, only engine errors will make KICS exit code different from 0 (default "none") + -i, --include-queries strings include queries by providing the query ID + cannot be provided with query exclusion flags + can be provided multiple times or as a comma separated string + example: 'e69890e6-fce5-461d-98ad-cb98318dfc96,4728cd65-a20c-49da-8b31-9c08b423e4db' + --input-data string path to query input data files + -b, --libraries-path string path to directory with libraries (default "./assets/libraries") + --minimal-ui simplified version of CLI output + --no-progress hides the progress bar + --output-name string name used on report creations (default "results") + -o, --output-path string directory path to store reports + -p, --path strings paths or directories to scan + example: "./somepath,somefile.txt" + --payload-lines adds line information inside the payload when printing the payload file + -d, --payload-path string path to store internal representation JSON file + --preview-lines int number of lines to be display in CLI results (min: 1, max: 30) (default 3) + -q, --queries-path strings paths to directory with queries (default [./assets/queries]) + --report-formats strings formats in which the results will be exported (all, asff, codeclimate, csv, cyclonedx, glsast, html, json, junit, pdf, sarif, sonarqube) (default [json]) + -r, --secrets-regexes-path string path to secrets regex rules configuration file + --terraform-vars-path string path where terraform variables are present + --timeout int number of seconds the query has to execute before being canceled (default 60) + -t, --type strings case insensitive list of platform types to scan + (Ansible, AzureResourceManager, Buildah, CICD, CloudFormation, Crossplane, DockerCompose, Dockerfile, GRPC, GoogleDeploymentManager, Knative, Kubernetes, OpenAPI, Pulumi, ServerlessFW, Terraform) + cannot be provided with type exclusion flags Global Flags: --ci display only log messages to CLI output (mutually exclusive with silent) diff --git a/internal/console/assets/scan-flags.json b/internal/console/assets/scan-flags.json index 34aa8ae0ff9..aa3973fedc0 100644 --- a/internal/console/assets/scan-flags.json +++ b/internal/console/assets/scan-flags.json @@ -59,6 +59,13 @@ "defaultValue": "false", "usage": "include bill of materials (BoM) in results output" }, + "experimental-queries": { + "flagType": "multiStr", + "shorthandFlag": "", + "defaultValue": null, + "usage": "include experimental queries (queries not yet thoroughly reviewed) by providing the path of the queries folder\ncan be provided multiple times or as a comma-separated string (platform/cloudProvider or platform)\nexample: 'terraform/databricks'\npossible values found in: '/assets/utils/experimental-queries.json'", + "validation": "validateMultiStr" + }, "fail-on": { "flagType": "multiStr", "shorthandFlag": "", diff --git a/internal/console/flags/scan_flags.go b/internal/console/flags/scan_flags.go index d994d9f3bea..c2d2288aca8 100644 --- a/internal/console/flags/scan_flags.go +++ b/internal/console/flags/scan_flags.go @@ -2,35 +2,36 @@ package flags // Flags constants for scan const ( - BomFlag = "bom" - CloudProviderFlag = "cloud-provider" - ConfigFlag = "config" - DisableFullDescFlag = "disable-full-descriptions" - ExcludeCategoriesFlag = "exclude-categories" - ExcludePathsFlag = "exclude-paths" - ExcludeQueriesFlag = "exclude-queries" - ExcludeResultsFlag = "exclude-results" - ExcludeSeveritiesFlag = "exclude-severities" - IncludeQueriesFlag = "include-queries" - InputDataFlag = "input-data" - FailOnFlag = "fail-on" - IgnoreOnExitFlag = "ignore-on-exit" - MinimalUIFlag = "minimal-ui" - NoProgressFlag = "no-progress" - OutputNameFlag = "output-name" - OutputPathFlag = "output-path" - PathFlag = "path" - PayloadPathFlag = "payload-path" - PreviewLinesFlag = "preview-lines" - QueriesPath = "queries-path" - LibrariesPath = "libraries-path" - ReportFormatsFlag = "report-formats" - TypeFlag = "type" - ExcludeTypeFlag = "exclude-type" - TerraformVarsPathFlag = "terraform-vars-path" - QueryExecTimeoutFlag = "timeout" - LineInfoPayloadFlag = "payload-lines" - DisableSecretsFlag = "disable-secrets" - SecretsRegexesPathFlag = "secrets-regexes-path" //nolint:gosec - ExcludeGitIgnore = "exclude-gitignore" + BomFlag = "bom" + CloudProviderFlag = "cloud-provider" + ConfigFlag = "config" + DisableFullDescFlag = "disable-full-descriptions" + ExcludeCategoriesFlag = "exclude-categories" + ExcludePathsFlag = "exclude-paths" + ExcludeQueriesFlag = "exclude-queries" + ExcludeResultsFlag = "exclude-results" + ExcludeSeveritiesFlag = "exclude-severities" + ExperimentalQueriesFlag = "experimental-queries" + IncludeQueriesFlag = "include-queries" + InputDataFlag = "input-data" + FailOnFlag = "fail-on" + IgnoreOnExitFlag = "ignore-on-exit" + MinimalUIFlag = "minimal-ui" + NoProgressFlag = "no-progress" + OutputNameFlag = "output-name" + OutputPathFlag = "output-path" + PathFlag = "path" + PayloadPathFlag = "payload-path" + PreviewLinesFlag = "preview-lines" + QueriesPath = "queries-path" + LibrariesPath = "libraries-path" + ReportFormatsFlag = "report-formats" + TypeFlag = "type" + ExcludeTypeFlag = "exclude-type" + TerraformVarsPathFlag = "terraform-vars-path" + QueryExecTimeoutFlag = "timeout" + LineInfoPayloadFlag = "payload-lines" + DisableSecretsFlag = "disable-secrets" + SecretsRegexesPathFlag = "secrets-regexes-path" //nolint:gosec + ExcludeGitIgnore = "exclude-gitignore" ) diff --git a/internal/console/helpers/helpers.go b/internal/console/helpers/helpers.go index f44fcc9c53d..96942ff9189 100644 --- a/internal/console/helpers/helpers.go +++ b/internal/console/helpers/helpers.go @@ -132,9 +132,31 @@ func GetExecutableDirectory() string { // GetDefaultQueryPath - returns the default query path func GetDefaultQueryPath(queriesPath string) (string, error) { log.Debug().Msg("helpers.GetDefaultQueryPath()") + queriesDirectory, err := GetFullPath(queriesPath) + if err != nil { + return "", err + } + log.Debug().Msgf("Queries found in %s", queriesDirectory) + return queriesDirectory, nil +} + +// GetDefaultExperimentalPath returns the default Experimental path +func GetDefaultExperimentalPath(experimentalQueriesPath string) (string, error) { + log.Debug().Msg("helpers.GetDefaultExperimentalPath()") + experimentalQueriesFile, err := GetFullPath(experimentalQueriesPath) + if err != nil { + return "", err + } + + log.Debug().Msgf("Experimental Queries found in %s", experimentalQueriesFile) + return experimentalQueriesFile, nil +} + +// GetFulPath returns the full path of a partial path used for queries or experimental queries json path +func GetFullPath(partialPath string) (string, error) { executableDirPath := GetExecutableDirectory() - queriesDirectory := filepath.Join(executableDirPath, queriesPath) - if _, err := os.Stat(queriesDirectory); os.IsNotExist(err) { + fullPath := filepath.Join(executableDirPath, partialPath) + if _, err := os.Stat(fullPath); os.IsNotExist(err) { currentWorkDir, err := os.Getwd() if err != nil { return "", err @@ -143,14 +165,13 @@ func GetDefaultQueryPath(queriesPath string) (string, error) { if idx != -1 { currentWorkDir = currentWorkDir[:strings.LastIndex(currentWorkDir, "kics")] + "kics" } - queriesDirectory = filepath.Join(currentWorkDir, queriesPath) - if _, err := os.Stat(queriesDirectory); os.IsNotExist(err) { + fullPath = filepath.Join(currentWorkDir, partialPath) + if _, err := os.Stat(fullPath); os.IsNotExist(err) { return "", err } } - log.Debug().Msgf("Queries found in %s", queriesDirectory) - return queriesDirectory, nil + return fullPath, nil } // ListReportFormats return a slice with all supported report formats diff --git a/internal/console/scan.go b/internal/console/scan.go index df5ed692f7b..52524ae3b48 100644 --- a/internal/console/scan.go +++ b/internal/console/scan.go @@ -116,6 +116,7 @@ func getScanParameters(changedDefaultQueryPath, changedDefaultLibrariesPath bool ExcludeQueries: flags.GetMultiStrFlag(flags.ExcludeQueriesFlag), ExcludeResults: flags.GetMultiStrFlag(flags.ExcludeResultsFlag), ExcludeSeverities: flags.GetMultiStrFlag(flags.ExcludeSeveritiesFlag), + ExperimentalQueries: flags.GetMultiStrFlag(flags.ExperimentalQueriesFlag), IncludeQueries: flags.GetMultiStrFlag(flags.IncludeQueriesFlag), InputData: flags.GetStrFlag(flags.InputDataFlag), OutputName: flags.GetStrFlag(flags.OutputNameFlag), diff --git a/pkg/engine/inspector_test.go b/pkg/engine/inspector_test.go index cf52684ff0b..4e5b584d95b 100644 --- a/pkg/engine/inspector_test.go +++ b/pkg/engine/inspector_test.go @@ -678,7 +678,7 @@ func TestShouldSkipFile(t *testing.T) { } func newInspectorInstance(t *testing.T, queryPath []string) *Inspector { - querySource := source.NewFilesystemSource(queryPath, []string{""}, []string{""}, filepath.FromSlash("./assets/libraries")) + querySource := source.NewFilesystemSource(queryPath, []string{""}, []string{""}, filepath.FromSlash("./assets/libraries"), filepath.FromSlash("./assets/utils/experimental-queries.json")) var vb = func(ctx *QueryContext, tracker Tracker, v interface{}, detector *detector.DetectLine) (*model.Vulnerability, error) { return &model.Vulnerability{}, nil @@ -701,7 +701,7 @@ type mockSource struct { } func (m *mockSource) GetQueries(queryFilter *source.QueryInspectorParameters) ([]model.QueryMetadata, error) { - sources := source.NewFilesystemSource(m.Source, []string{""}, []string{""}, filepath.FromSlash("./assets/libraries")) + sources := source.NewFilesystemSource(m.Source, []string{""}, []string{""}, filepath.FromSlash("./assets/libraries"), filepath.FromSlash("./assets/utils/experimental-queries.json")) return sources.GetQueries(queryFilter) } diff --git a/pkg/engine/source/filesystem.go b/pkg/engine/source/filesystem.go index 12514ffc0cc..eab3e6b5048 100644 --- a/pkg/engine/source/filesystem.go +++ b/pkg/engine/source/filesystem.go @@ -3,6 +3,7 @@ package source import ( "encoding/json" "fmt" + "io" "os" "path" "path/filepath" @@ -21,10 +22,11 @@ import ( // Source is the path to the queries // Types are the types given by the flag --type for query selection mechanism type FilesystemSource struct { - Source []string - Types []string - CloudProviders []string - Library string + Source []string + Types []string + CloudProviders []string + Library string + ExperimentalQueries string } const ( @@ -43,7 +45,7 @@ const ( ) // NewFilesystemSource initializes a NewFilesystemSource with source to queries and types of queries to load -func NewFilesystemSource(source, types, cloudProviders []string, libraryPath string) *FilesystemSource { +func NewFilesystemSource(source, types, cloudProviders []string, libraryPath, experimentalQueriesPath string) *FilesystemSource { log.Debug().Msg("source.NewFilesystemSource()") if len(types) == 0 { @@ -59,10 +61,11 @@ func NewFilesystemSource(source, types, cloudProviders []string, libraryPath str } return &FilesystemSource{ - Source: source, - Types: types, - CloudProviders: cloudProviders, - Library: filepath.FromSlash(libraryPath), + Source: source, + Types: types, + CloudProviders: cloudProviders, + Library: filepath.FromSlash(libraryPath), + ExperimentalQueries: experimentalQueriesPath, } } @@ -246,11 +249,47 @@ func checkQueryExclude(metadata map[string]interface{}, queryParameters *QueryIn // GetQueries walks a given filesource path returns all queries found in an array of // QueryMetadata struct func (s *FilesystemSource) GetQueries(queryParameters *QueryInspectorParameters) ([]model.QueryMetadata, error) { + experimentalQueriesPaths := make([]string, 0) + + if s.ExperimentalQueries != "" { + experimentalQueriesFile, errOpeningFile := os.Open(s.ExperimentalQueries) + if errOpeningFile != nil { + return nil, errOpeningFile + } + + defer func(experimentalQueriesFile *os.File) { + errClosingFile := experimentalQueriesFile.Close() + if errClosingFile != nil { + log.Err(errClosingFile).Msg("Failed to close experimental queries file") + } + }(experimentalQueriesFile) + + byteValue, err := io.ReadAll(experimentalQueriesFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(byteValue, &experimentalQueriesPaths) + if err != nil { + return nil, err + } + } + + queryDirs, err := s.iterateSources(experimentalQueriesPaths, queryParameters) + if err != nil { + return nil, err + } + + queries := s.iterateQueryDirs(queryDirs, queryParameters) + + return queries, nil +} + +func (s *FilesystemSource) iterateSources(experimentalQueriesPaths []string, queryParameters *QueryInspectorParameters) ([]string, error) { queryDirs := make([]string, 0) - var err error for _, source := range s.Source { - err = filepath.Walk(source, + err := filepath.Walk(source, func(p string, f os.FileInfo, err error) error { if err != nil { return err @@ -260,7 +299,20 @@ func (s *FilesystemSource) GetQueries(queryParameters *QueryInspectorParameters) return nil } - queryDirs = append(queryDirs, filepath.Dir(p)) + querypathDir := filepath.Dir(p) + absQueryPathDir, err1 := filepath.Abs(querypathDir) + absQueriesPath, err2 := filepath.Abs(source) + if err1 == nil && err2 == nil { + var cleanPlatformCloudProviderDir string + cleanPlatformCloudProviderDir, err = filepath.Rel(absQueriesPath, absQueryPathDir) + if err == nil && isExperimental(querypathDir, cleanPlatformCloudProviderDir, experimentalQueriesPaths, queryParameters) { + queryDirs = append(queryDirs, querypathDir) + } else if err != nil { + return errors.Wrap(err, "Failed to get query relative path") + } + } else { + return errors.Wrap(err, "Failed to get query absolute path") + } return nil }) if err != nil { @@ -268,7 +320,39 @@ func (s *FilesystemSource) GetQueries(queryParameters *QueryInspectorParameters) } } + return queryDirs, nil +} + +func isExperimental( + querypathDir, cleanPlatformCloudProviderDir string, + experimentalQueriesPaths []string, + queryParameters *QueryInspectorParameters) bool { + cleanPlatformCloudProviderDir = filepath.FromSlash(cleanPlatformCloudProviderDir) + inExperimentalQueriesJSON := false + for _, queryPath := range experimentalQueriesPaths { + queryPath = filepath.FromSlash(queryPath) + if strings.Contains(querypathDir, queryPath) { + inExperimentalQueriesJSON = true + break + } + } + + inExperimentalQueriesFlag := false + for _, experimentalFlag := range queryParameters.ExperimentalQueries { + experimentalFlag = filepath.FromSlash(experimentalFlag) + if strings.HasPrefix(cleanPlatformCloudProviderDir, experimentalFlag) { + inExperimentalQueriesFlag = true + break + } + } + + return inExperimentalQueriesFlag || !inExperimentalQueriesJSON +} + +// iterateQueryDirs iterates all query directories and reads the respective queries +func (s *FilesystemSource) iterateQueryDirs(queryDirs []string, queryParameters *QueryInspectorParameters) []model.QueryMetadata { queries := make([]model.QueryMetadata, 0, len(queryDirs)) + for _, queryDir := range queryDirs { query, errRQ := ReadQuery(queryDir) if errRQ != nil { @@ -318,8 +402,7 @@ func (s *FilesystemSource) GetQueries(queryParameters *QueryInspectorParameters) queries = append(queries, query) } } - - return queries, err + return queries } // validateMetadata prevents panics when KICS queries metadata fields are missing diff --git a/pkg/engine/source/filesystem_test.go b/pkg/engine/source/filesystem_test.go index 271136f076f..173396bd8cd 100644 --- a/pkg/engine/source/filesystem_test.go +++ b/pkg/engine/source/filesystem_test.go @@ -14,7 +14,8 @@ import ( ) const ( - source = "./test/fixtures/all_auth_users_get_read_access" + source_get_queries = "./test/fixtures/all_auth_users_get_read_access" + source_get_queries_experimental = "./test/fixtures/test_experimental_queries/experimental_queries_queries" ) // BenchmarkFilesystemSource_GetQueries benchmarks getQueries to see improvements @@ -23,10 +24,11 @@ func BenchmarkFilesystemSource_GetQueries(b *testing.B) { b.Fatal(err) } type fields struct { - Source []string - Types []string - CloudProviders []string - Library string + Source []string + Types []string + CloudProviders []string + Library string + ExperimentalQueries string } tests := []struct { name string @@ -35,16 +37,17 @@ func BenchmarkFilesystemSource_GetQueries(b *testing.B) { { name: "testing_all_paths", fields: fields{ - Source: []string{"./assets/queries/"}, - Types: []string{""}, - CloudProviders: []string{""}, - Library: "./assets/libraries", + Source: []string{"./assets/queries/"}, + Types: []string{""}, + CloudProviders: []string{""}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, }, } for _, tt := range tests { b.Run(tt.name, func(b *testing.B) { - s := NewFilesystemSource(tt.fields.Source, tt.fields.Types, tt.fields.CloudProviders, tt.fields.Library) + s := NewFilesystemSource(tt.fields.Source, tt.fields.Types, tt.fields.CloudProviders, tt.fields.Library, tt.fields.ExperimentalQueries) for n := 0; n < b.N; n++ { filter := QueryInspectorParameters{ IncludeQueries: IncludeQueries{ByIDs: []string{}}, @@ -67,10 +70,11 @@ func TestFilesystemSource_GetQueriesWithExclude(t *testing.T) { //nolint contentByte, err := os.ReadFile(filepath.FromSlash("./test/fixtures/get_queries_test/content_get_queries.rego")) require.NoError(t, err) type fields struct { - Source []string - Types []string - CloudProviders []string - Library string + Source []string + Types []string + CloudProviders []string + Library string + ExperimentalQueries string } tests := []struct { name string @@ -84,8 +88,9 @@ func TestFilesystemSource_GetQueriesWithExclude(t *testing.T) { //nolint { name: "get_queries_with_exclude_result_1", fields: fields{ - Source: []string{source}, Types: []string{""}, + Source: []string{source_get_queries}, Types: []string{""}, CloudProviders: []string{""}, Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, excludeCategory: []string{}, excludeSeverities: []string{}, @@ -113,7 +118,7 @@ func TestFilesystemSource_GetQueriesWithExclude(t *testing.T) { //nolint { name: "get_queries_with_exclude_no_result_1", fields: fields{ - Source: []string{source}, Types: []string{""}, + Source: []string{source_get_queries}, Types: []string{""}, CloudProviders: []string{""}, Library: "./assets/libraries", }, excludeCategory: []string{}, @@ -135,7 +140,7 @@ func TestFilesystemSource_GetQueriesWithExclude(t *testing.T) { //nolint { name: "get_queries_with_exclude_category_no_result", fields: fields{ - Source: []string{source}, Types: []string{""}, + Source: []string{source_get_queries}, Types: []string{""}, CloudProviders: []string{""}, Library: "./assets/libraries", }, excludeCategory: []string{"Access Control"}, @@ -147,7 +152,7 @@ func TestFilesystemSource_GetQueriesWithExclude(t *testing.T) { //nolint { name: "get_queries_with_exclude_severity_no_result", fields: fields{ - Source: []string{source}, Types: []string{""}, + Source: []string{source_get_queries}, Types: []string{""}, CloudProviders: []string{""}, Library: "./assets/libraries", }, excludeCategory: []string{}, @@ -159,7 +164,7 @@ func TestFilesystemSource_GetQueriesWithExclude(t *testing.T) { //nolint } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewFilesystemSource(tt.fields.Source, []string{""}, []string{""}, tt.fields.Library) + s := NewFilesystemSource(tt.fields.Source, []string{""}, []string{""}, tt.fields.Library, tt.fields.ExperimentalQueries) filter := QueryInspectorParameters{ IncludeQueries: IncludeQueries{ByIDs: []string{}}, ExcludeQueries: ExcludeQueries{ByIDs: tt.excludeIDs, ByCategories: tt.excludeCategory, BySeverities: tt.excludeSeverities}, @@ -189,10 +194,11 @@ func TestFilesystemSource_GetQueriesWithInclude(t *testing.T) { require.NoError(t, err) type fields struct { - Source []string - Types []string - CloudProviders []string - Library string + Source []string + Types []string + CloudProviders []string + Library string + ExperimentalQueries string } tests := []struct { name string @@ -204,7 +210,9 @@ func TestFilesystemSource_GetQueriesWithInclude(t *testing.T) { { name: "get_queries_with_include_result_1", fields: fields{ - Source: []string{source}, Types: []string{""}, CloudProviders: []string{""}, Library: "./assets/libraries", + Source: []string{source_get_queries}, Types: []string{""}, CloudProviders: []string{""}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, includeIDs: []string{"57b9893d-33b1-4419-bcea-b828fb87e318"}, want: []model.QueryMetadata{ @@ -230,7 +238,7 @@ func TestFilesystemSource_GetQueriesWithInclude(t *testing.T) { { name: "get_queries_with_include_no_result_1", fields: fields{ - Source: []string{source}, Types: []string{""}, CloudProviders: []string{""}, Library: "./assets/libraries", + Source: []string{source_get_queries}, Types: []string{""}, CloudProviders: []string{""}, Library: "./assets/libraries", }, includeIDs: []string{"57b9893d-33b1-4419-bcea-xxxxxxx"}, want: []model.QueryMetadata{}, @@ -248,7 +256,7 @@ func TestFilesystemSource_GetQueriesWithInclude(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewFilesystemSource(tt.fields.Source, []string{""}, []string{""}, tt.fields.Library) + s := NewFilesystemSource(tt.fields.Source, []string{""}, []string{""}, tt.fields.Library, tt.fields.ExperimentalQueries) filter := QueryInspectorParameters{ IncludeQueries: IncludeQueries{ ByIDs: tt.includeIDs, @@ -281,8 +289,9 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint t.Fatal(err) } type fields struct { - Source []string - Library string + Source []string + Library string + ExperimentalQueries string } type args struct { platform string @@ -297,8 +306,9 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint { name: "get_generic_query_terraform", fields: fields{ - Source: []string{"./assets/queries/template"}, - Library: "./assets/libraries", + Source: []string{"./assets/queries/template"}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, args: args{ platform: "terraform", @@ -309,8 +319,9 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint { name: "get_generic_query_common", fields: fields{ - Source: []string{"./assets/queries/template"}, - Library: "./assets/libraries", + Source: []string{"./assets/queries/template"}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, args: args{ platform: "common", @@ -321,8 +332,9 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint { name: "get_generic_query_cloudformation", fields: fields{ - Source: []string{"./assets/queries/template"}, - Library: "./assets/libraries", + Source: []string{"./assets/queries/template"}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, args: args{ platform: "cloudFormation", @@ -333,8 +345,9 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint { name: "get_generic_query_ansible", fields: fields{ - Source: []string{"./assets/queries/template"}, - Library: "./assets/libraries", + Source: []string{"./assets/queries/template"}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, args: args{ platform: "ansible", @@ -345,8 +358,9 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint { name: "get_generic_query_dockerfile", fields: fields{ - Source: []string{"./assets/queries/template"}, - Library: "./assets/libraries", + Source: []string{"./assets/queries/template"}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, args: args{ platform: "dockerfile", @@ -357,8 +371,9 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint { name: "get_generic_query_k8s", fields: fields{ - Source: []string{"./assets/queries/template"}, - Library: "./assets/libraries", + Source: []string{"./assets/queries/template"}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, args: args{ platform: "k8s", @@ -381,8 +396,9 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint { name: "get_generic_query_unknown", fields: fields{ - Source: []string{"./assets/queries/template"}, - Library: "./assets/libraries", + Source: []string{"./assets/queries/template"}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", }, args: args{ platform: "unknown", @@ -393,7 +409,7 @@ func TestFilesystemSource_GetQueryLibrary(t *testing.T) { //nolint } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewFilesystemSource(tt.fields.Source, []string{""}, []string{""}, tt.fields.Library) + s := NewFilesystemSource(tt.fields.Source, []string{""}, []string{""}, tt.fields.Library, tt.fields.ExperimentalQueries) got, err := s.GetQueryLibrary(tt.args.platform) if (err != nil) != tt.wantErr { @@ -415,12 +431,16 @@ func TestFilesystemSource_GetQueries(t *testing.T) { contentByte, err := os.ReadFile(filepath.FromSlash("./test/fixtures/get_queries_test/content_get_queries.rego")) require.NoError(t, err) + contentByteExperimental, err := os.ReadFile(filepath.FromSlash("./test/fixtures/test_experimental_queries/experimental_queries_queries/experimental/test/query.rego")) + require.NoError(t, err) type fields struct { - Source []string - Types []string - CloudProviders []string - Library string + Source []string + Types []string + CloudProviders []string + Library string + ExperimentalQueries string + ExperimentalQueriesFlag []string } tests := []struct { name string @@ -431,7 +451,10 @@ func TestFilesystemSource_GetQueries(t *testing.T) { { name: "get_queries_1", fields: fields{ - Source: []string{source, source}, Types: []string{""}, CloudProviders: []string{""}, Library: "./assets/libraries", + Source: []string{source_get_queries, source_get_queries}, Types: []string{""}, CloudProviders: []string{""}, + Library: "./assets/libraries", + ExperimentalQueries: "./assets/utils/experimental-queries.json", + ExperimentalQueriesFlag: []string{}, }, want: []model.QueryMetadata{ { @@ -472,15 +495,95 @@ func TestFilesystemSource_GetQueries(t *testing.T) { { name: "get_queries_error", fields: fields{ - Source: []string{"../no-path"}, + Source: []string{"../no-path"}, + ExperimentalQueries: "./assets/utils/experimental-queries.json", + ExperimentalQueriesFlag: []string{}, }, want: nil, wantErr: true, }, + { + name: "get_queries_experimental_no_flag", + fields: fields{ + Source: []string{source_get_queries_experimental}, Types: []string{""}, CloudProviders: []string{""}, + Library: "./assets/libraries", + ExperimentalQueries: "./test/fixtures/test_experimental_queries/utils/experimental-queries.json", + ExperimentalQueriesFlag: []string{"tested"}, + }, + want: []model.QueryMetadata{ + { + Query: "tested_query", + Content: string(contentByteExperimental), + InputData: "{}", + Metadata: map[string]interface{}{ + "category": "Insecure Configurations", + "descriptionText": "SSL Client Certificate should be enabled", + "descriptionUrl": "https://docs.ansible.com/ansible/2.8/modules/aws_api_gateway_module.html", + "id": "b47b98ab-e481-4a82-8bb1-1ab39fd36e34", + "queryName": "API Gateway Without SSL Certificate", + "severity": model.SeverityMedium, + "platform": "Ansible", + "cloudProvider": "aws", + "descriptionID": "82608f36", + }, + Platform: "ansible", + Aggregation: 1, + }, + }, + wantErr: false, + }, + { + name: "get_queries_experimental_with_flag", + fields: fields{ + Source: []string{source_get_queries_experimental}, Types: []string{""}, CloudProviders: []string{""}, + Library: "./assets/libraries", + ExperimentalQueries: "./test/fixtures/test_experimental_queries/utils/experimental-queries.json", + ExperimentalQueriesFlag: []string{"experimental"}, + }, + want: []model.QueryMetadata{ + { + Query: "test", + Content: string(contentByteExperimental), + InputData: "{}", + Metadata: map[string]interface{}{ + "category": "Insecure Configurations", + "descriptionText": "SSL Client Certificate should be enabled", + "descriptionUrl": "https://docs.ansible.com/ansible/2.8/modules/aws_api_gateway_module.html", + "id": "b47b98ab-e481-4a82-8bb1-1ab39fd36e33", + "queryName": "API Gateway Without SSL Certificate", + "severity": model.SeverityMedium, + "platform": "Ansible", + "cloudProvider": "aws", + "descriptionID": "82608f36", + }, + Platform: "ansible", + Aggregation: 1, + }, + { + Query: "tested_query", + Content: string(contentByteExperimental), + InputData: "{}", + Metadata: map[string]interface{}{ + "category": "Insecure Configurations", + "descriptionText": "SSL Client Certificate should be enabled", + "descriptionUrl": "https://docs.ansible.com/ansible/2.8/modules/aws_api_gateway_module.html", + "id": "b47b98ab-e481-4a82-8bb1-1ab39fd36e34", + "queryName": "API Gateway Without SSL Certificate", + "severity": model.SeverityMedium, + "platform": "Ansible", + "cloudProvider": "aws", + "descriptionID": "82608f36", + }, + Platform: "ansible", + Aggregation: 1, + }, + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewFilesystemSource(tt.fields.Source, []string{""}, []string{""}, tt.fields.Library) + s := NewFilesystemSource(tt.fields.Source, []string{""}, []string{""}, tt.fields.Library, tt.fields.ExperimentalQueries) filter := QueryInspectorParameters{ IncludeQueries: IncludeQueries{ ByIDs: []string{}}, @@ -488,7 +591,8 @@ func TestFilesystemSource_GetQueries(t *testing.T) { ByIDs: []string{}, ByCategories: []string{}, }, - InputDataPath: "", + ExperimentalQueries: tt.fields.ExperimentalQueriesFlag, + InputDataPath: "", } got, err := s.GetQueries(&filter) if (err != nil) != tt.wantErr { diff --git a/pkg/engine/source/source.go b/pkg/engine/source/source.go index 3374a203aa8..2b70137ffc9 100644 --- a/pkg/engine/source/source.go +++ b/pkg/engine/source/source.go @@ -13,10 +13,11 @@ import ( // QueryInspectorParameters is a struct that represents the optionn to select queries to be executed type QueryInspectorParameters struct { - IncludeQueries IncludeQueries - ExcludeQueries ExcludeQueries - InputDataPath string - BomQueries bool + IncludeQueries IncludeQueries + ExcludeQueries ExcludeQueries + ExperimentalQueries []string + InputDataPath string + BomQueries bool } // ExcludeQueries is a struct that represents the option to exclude queries by ids or by categories diff --git a/pkg/remediation/scan.go b/pkg/remediation/scan.go index 84b91aba97d..48f53e57c92 100644 --- a/pkg/remediation/scan.go +++ b/pkg/remediation/scan.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "path/filepath" "time" "github.com/Checkmarx/kics/pkg/engine" @@ -13,6 +14,7 @@ import ( "github.com/open-policy-agent/opa/topdown" "github.com/Checkmarx/kics/internal/console/flags" + consoleHelpers "github.com/Checkmarx/kics/internal/console/helpers" "github.com/Checkmarx/kics/internal/tracker" "github.com/Checkmarx/kics/pkg/engine/source" "github.com/Checkmarx/kics/pkg/parser" @@ -198,7 +200,12 @@ func initScan(queryID string) (*engine.Inspector, error) { } _, err := c.GetQueryPath() + if err != nil { + log.Err(err) + return &engine.Inspector{}, err + } + experimentalQueries, err := consoleHelpers.GetDefaultExperimentalPath(filepath.FromSlash("./assets/utils/experimental-queries.json")) if err != nil { log.Err(err) return &engine.Inspector{}, err @@ -208,7 +215,8 @@ func initScan(queryID string) (*engine.Inspector, error) { c.ScanParams.QueriesPath, c.ScanParams.Platform, c.ScanParams.CloudProvider, - c.ScanParams.LibrariesPath) + c.ScanParams.LibrariesPath, + experimentalQueries) includeQueries := source.IncludeQueries{ ByIDs: []string{queryID}, diff --git a/pkg/scan/client.go b/pkg/scan/client.go index b2065b571be..55b07a72cb9 100644 --- a/pkg/scan/client.go +++ b/pkg/scan/client.go @@ -21,6 +21,7 @@ type Parameters struct { ExcludeQueries []string ExcludeResults []string ExcludeSeverities []string + ExperimentalQueries []string IncludeQueries []string InputData string OutputName string diff --git a/pkg/scan/scan.go b/pkg/scan/scan.go index bb376ad9aee..8c9f76f38c2 100644 --- a/pkg/scan/scan.go +++ b/pkg/scan/scan.go @@ -4,8 +4,10 @@ package scan import ( "context" "os" + "path/filepath" "github.com/Checkmarx/kics/assets" + consoleHelpers "github.com/Checkmarx/kics/internal/console/helpers" "github.com/Checkmarx/kics/pkg/engine" "github.com/Checkmarx/kics/pkg/engine/provider" "github.com/Checkmarx/kics/pkg/engine/secrets" @@ -56,11 +58,18 @@ func (c *Client) initScan(ctx context.Context) (*executeScanParameters, error) { return nil, nil } + experimentalQueries, err := consoleHelpers.GetDefaultExperimentalPath(filepath.FromSlash("./assets/utils/experimental-queries.json")) + if err != nil { + log.Err(err) + return nil, err + } + querySource := source.NewFilesystemSource( c.ScanParams.QueriesPath, c.ScanParams.Platform, c.ScanParams.CloudProvider, - c.ScanParams.LibrariesPath) + c.ScanParams.LibrariesPath, + experimentalQueries) queryFilter := c.createQueryFilter() @@ -200,10 +209,11 @@ func (c *Client) createQueryFilter() *source.QueryInspectorParameters { } queryFilter := source.QueryInspectorParameters{ - IncludeQueries: includeQueries, - ExcludeQueries: excludeQueries, - InputDataPath: c.ScanParams.InputData, - BomQueries: c.ScanParams.BillOfMaterials, + IncludeQueries: includeQueries, + ExcludeQueries: excludeQueries, + ExperimentalQueries: c.ScanParams.ExperimentalQueries, + InputDataPath: c.ScanParams.InputData, + BomQueries: c.ScanParams.BillOfMaterials, } return &queryFilter diff --git a/pkg/scanner/scanner_test.go b/pkg/scanner/scanner_test.go index 24463b1206f..d479778e2cf 100644 --- a/pkg/scanner/scanner_test.go +++ b/pkg/scanner/scanner_test.go @@ -97,7 +97,7 @@ func createServices(types, cloudProviders []string) (serviceSlice, *storage.Memo } t := &tracker.CITracker{} - querySource := source.NewFilesystemSource(sourcePath, types, cloudProviders, filepath.FromSlash("../../assets/libraries")) + querySource := source.NewFilesystemSource(sourcePath, types, cloudProviders, filepath.FromSlash("../../assets/libraries"), filepath.FromSlash("../../assets/utils/experimental-queries.json")) inspector, err := engine.NewInspector(context.Background(), querySource, engine.DefaultVulnerabilityBuilder, diff --git a/test/fixtures/test_experimental_queries/experimental_queries_feature/experimental.yaml b/test/fixtures/test_experimental_queries/experimental_queries_feature/experimental.yaml new file mode 100644 index 00000000000..7845aa3e0a9 --- /dev/null +++ b/test/fixtures/test_experimental_queries/experimental_queries_feature/experimental.yaml @@ -0,0 +1,30 @@ +- name: update API + aws_api_gateway: + api_id: 'abc123321cba' + state: present + swagger_file: my_api.yml + validate_certs: no +- name: update API v1 + aws_api_gateway: + api_id: 'abc123321cba' + state: present + swagger_file: my_api.yml +- name: Setup AWS API Gateway setup on AWS and deploy API definition + community.aws.aws_api_gateway: + swagger_file: my_api.yml + stage: production + cache_enabled: true + cache_size: '1.6' + tracing_enabled: true + endpoint_type: EDGE + state: present + validate_certs: no +- name: Setup AWS API Gateway setup on AWS and deploy API definition v1 + community.aws.aws_api_gateway: + swagger_file: my_api.yml + stage: production + cache_enabled: true + cache_size: '1.6' + tracing_enabled: true + endpoint_type: EDGE + state: present \ No newline at end of file diff --git a/test/fixtures/test_experimental_queries/experimental_queries_queries/experimental/test/metadata.json b/test/fixtures/test_experimental_queries/experimental_queries_queries/experimental/test/metadata.json new file mode 100644 index 00000000000..44f7883c443 --- /dev/null +++ b/test/fixtures/test_experimental_queries/experimental_queries_queries/experimental/test/metadata.json @@ -0,0 +1,12 @@ +{ + "id": "b47b98ab-e481-4a82-8bb1-1ab39fd36e33", + "queryName": "API Gateway Without SSL Certificate", + "severity": "MEDIUM", + "category": "Insecure Configurations", + "descriptionText": "SSL Client Certificate should be enabled", + "descriptionUrl": "https://docs.ansible.com/ansible/2.8/modules/aws_api_gateway_module.html", + "platform": "Ansible", + "descriptionID": "82608f36", + "cloudProvider": "aws" + } + \ No newline at end of file diff --git a/test/fixtures/test_experimental_queries/experimental_queries_queries/experimental/test/query.rego b/test/fixtures/test_experimental_queries/experimental_queries_queries/experimental/test/query.rego new file mode 100644 index 00000000000..6919afe4476 --- /dev/null +++ b/test/fixtures/test_experimental_queries/experimental_queries_queries/experimental/test/query.rego @@ -0,0 +1,42 @@ +package Cx + +import data.generic.ansible as ansLib +import data.generic.common as common_lib + +modules := {"community.aws.aws_api_gateway", "aws_api_gateway"} + +CxPolicy[result] { + task := ansLib.tasks[id][t] + api_gateway := task[modules[m]] + ansLib.checkState(api_gateway) + + not common_lib.valid_key(api_gateway, "validate_certs") + + result := { + "documentId": id, + "resourceType": modules[m], + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}", [task.name, modules[m]]), + "issueType": "MissingAttribute", + "keyExpectedValue": "aws_api_gateway.validate_certs should be set", + "keyActualValue": "aws_api_gateway.validate_certs is undefined", + } +} + +CxPolicy[result] { + task := ansLib.tasks[id][t] + api_gateway := task[modules[m]] + ansLib.checkState(api_gateway) + + not ansLib.isAnsibleTrue(api_gateway.validate_certs) + + result := { + "documentId": id, + "resourceType": modules[m], + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}.validate_certs", [task.name, modules[m]]), + "issueType": "IncorrectValue", + "keyExpectedValue": "aws_api_gateway.validate_certs should be set to yes", + "keyActualValue": "aws_api_gateway.validate_certs is not set to yes", + } +} diff --git a/test/fixtures/test_experimental_queries/experimental_queries_queries/tested/tested_query/metadata.json b/test/fixtures/test_experimental_queries/experimental_queries_queries/tested/tested_query/metadata.json new file mode 100644 index 00000000000..96b8f4bcc79 --- /dev/null +++ b/test/fixtures/test_experimental_queries/experimental_queries_queries/tested/tested_query/metadata.json @@ -0,0 +1,12 @@ +{ + "id": "b47b98ab-e481-4a82-8bb1-1ab39fd36e34", + "queryName": "API Gateway Without SSL Certificate", + "severity": "MEDIUM", + "category": "Insecure Configurations", + "descriptionText": "SSL Client Certificate should be enabled", + "descriptionUrl": "https://docs.ansible.com/ansible/2.8/modules/aws_api_gateway_module.html", + "platform": "Ansible", + "descriptionID": "82608f36", + "cloudProvider": "aws" + } + \ No newline at end of file diff --git a/test/fixtures/test_experimental_queries/experimental_queries_queries/tested/tested_query/query.rego b/test/fixtures/test_experimental_queries/experimental_queries_queries/tested/tested_query/query.rego new file mode 100644 index 00000000000..6919afe4476 --- /dev/null +++ b/test/fixtures/test_experimental_queries/experimental_queries_queries/tested/tested_query/query.rego @@ -0,0 +1,42 @@ +package Cx + +import data.generic.ansible as ansLib +import data.generic.common as common_lib + +modules := {"community.aws.aws_api_gateway", "aws_api_gateway"} + +CxPolicy[result] { + task := ansLib.tasks[id][t] + api_gateway := task[modules[m]] + ansLib.checkState(api_gateway) + + not common_lib.valid_key(api_gateway, "validate_certs") + + result := { + "documentId": id, + "resourceType": modules[m], + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}", [task.name, modules[m]]), + "issueType": "MissingAttribute", + "keyExpectedValue": "aws_api_gateway.validate_certs should be set", + "keyActualValue": "aws_api_gateway.validate_certs is undefined", + } +} + +CxPolicy[result] { + task := ansLib.tasks[id][t] + api_gateway := task[modules[m]] + ansLib.checkState(api_gateway) + + not ansLib.isAnsibleTrue(api_gateway.validate_certs) + + result := { + "documentId": id, + "resourceType": modules[m], + "resourceName": task.name, + "searchKey": sprintf("name={{%s}}.{{%s}}.validate_certs", [task.name, modules[m]]), + "issueType": "IncorrectValue", + "keyExpectedValue": "aws_api_gateway.validate_certs should be set to yes", + "keyActualValue": "aws_api_gateway.validate_certs is not set to yes", + } +} diff --git a/test/fixtures/test_experimental_queries/utils/experimental-queries.json b/test/fixtures/test_experimental_queries/utils/experimental-queries.json new file mode 100644 index 00000000000..234d13ae0a8 --- /dev/null +++ b/test/fixtures/test_experimental_queries/utils/experimental-queries.json @@ -0,0 +1,3 @@ +[ + "experimental/test" +] \ No newline at end of file