diff --git a/pkg/iac/rego/load.go b/pkg/iac/rego/load.go index a1c29d163fae..7959d7f69e01 100644 --- a/pkg/iac/rego/load.go +++ b/pkg/iac/rego/load.go @@ -295,6 +295,10 @@ func (s *Scanner) filterModules(retriever *MetadataRetriever) error { continue } + if _, disabled := s.disabledCheckIDs[meta.ID]; disabled { + continue + } + if len(meta.InputOptions.Selectors) == 0 { s.logger.Warn( "Module has no input selectors - it will be loaded for all inputs!", diff --git a/pkg/iac/rego/options.go b/pkg/iac/rego/options.go index d4b622a68c16..5cc60284abdb 100644 --- a/pkg/iac/rego/options.go +++ b/pkg/iac/rego/options.go @@ -106,3 +106,14 @@ func WithCustomSchemas(schemas map[string][]byte) options.ScannerOption { } } } + +// WithDisabledCheckIDs disables checks by their ID (ID field in metadata) +func WithDisabledCheckIDs(ids ...string) options.ScannerOption { + return func(s options.ConfigurableScanner) { + if ss, ok := s.(*Scanner); ok { + for _, id := range ids { + ss.disabledCheckIDs[id] = struct{}{} + } + } + } +} diff --git a/pkg/iac/rego/scanner.go b/pkg/iac/rego/scanner.go index a8e1caf525b3..9cf80fc83da0 100644 --- a/pkg/iac/rego/scanner.go +++ b/pkg/iac/rego/scanner.go @@ -69,6 +69,8 @@ type Scanner struct { embeddedLibs map[string]*ast.Module embeddedChecks map[string]*ast.Module customSchemas map[string][]byte + + disabledCheckIDs map[string]struct{} } func (s *Scanner) SetIncludeDeprecatedChecks(b bool) { @@ -109,12 +111,13 @@ func NewScanner(source types.Source, opts ...options.ScannerOption) *Scanner { } s := &Scanner{ - regoErrorLimit: ast.CompileErrorLimitDefault, - sourceType: source, - ruleNamespaces: make(map[string]struct{}), - runtimeValues: addRuntimeValues(), - logger: log.WithPrefix("rego"), - customSchemas: make(map[string][]byte), + regoErrorLimit: ast.CompileErrorLimitDefault, + sourceType: source, + ruleNamespaces: make(map[string]struct{}), + runtimeValues: addRuntimeValues(), + logger: log.WithPrefix("rego"), + customSchemas: make(map[string][]byte), + disabledCheckIDs: make(map[string]struct{}), } maps.Copy(s.ruleNamespaces, builtinNamespaces) diff --git a/pkg/iac/rego/scanner_test.go b/pkg/iac/rego/scanner_test.go index 73ef55223f14..a1b47ee14fad 100644 --- a/pkg/iac/rego/scanner_test.go +++ b/pkg/iac/rego/scanner_test.go @@ -1153,3 +1153,62 @@ deny { }) } } + +func Test_RegoScanner_WithDisabledCheckIDs(t *testing.T) { + + check := `# METADATA +# custom: +# id: TEST-001 +# avd_id: AVD-TEST-001 +# severity: LOW +# provider: aws +# service: s3 +# short_code: test +package user.test + +deny { + true +} +` + + tests := []struct { + name string + disabledChecks []string + expected bool + }{ + { + name: "no disabled checks", + expected: true, + }, + { + name: "disable check by ID", + disabledChecks: []string{"TEST-001"}, + }, + { + name: "disabling a non-existent check", + disabledChecks: []string{"FOO"}, + expected: true, + }, + { + name: "one of the identifiers does not exist", + disabledChecks: []string{"FOO", "TEST-001"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + scanner := rego.NewScanner( + types.SourceYAML, + rego.WithPolicyNamespaces("user"), + rego.WithPolicyReader(strings.NewReader(check)), + rego.WithDisabledCheckIDs(tt.disabledChecks...), + ) + + require.NoError(t, scanner.LoadPolicies(nil)) + results, err := scanner.ScanInput(context.TODO(), rego.Input{}) + require.NoError(t, err) + + require.Equal(t, tt.expected, len(results.GetFailed()) > 0) + }) + } +}