feat(misconf): Add support for configurable Rego error limit (#9657)

Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
Co-authored-by: nikpivkin <nikita.pivkin@smartforce.io>
This commit is contained in:
simar7
2025-10-31 15:19:52 +05:30
committed by GitHub
parent 3fb8703f8c
commit 445cd2b6b4
21 changed files with 142 additions and 5 deletions

View File

@@ -24,6 +24,7 @@
--pkg-types
--quiet
--redis-tls
--rego-error-limit
--removed-pkgs
--report
--scanners

View File

@@ -58,6 +58,7 @@ trivy config [flags] DIR
--redis-key string redis key file location, if using redis as cache backend
--redis-tls enable redis TLS with public certificates, if using redis as cache backend
--registry-token string registry token
--rego-error-limit int maximum number of compile errors allowed during Rego policy evaluation (default 10)
--render-cause strings specify configuration types for which the rendered causes will be shown in the table report (allowed values: terraform)
--report string specify a compliance report format for the output (allowed values: all,summary) (default "all")
-s, --severity strings severities of security issues to be displayed

View File

@@ -106,6 +106,7 @@ trivy filesystem [flags] PATH
--redis-key string redis key file location, if using redis as cache backend
--redis-tls enable redis TLS with public certificates, if using redis as cache backend
--registry-token string registry token
--rego-error-limit int maximum number of compile errors allowed during Rego policy evaluation (default 10)
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
--render-cause strings specify configuration types for which the rendered causes will be shown in the table report (allowed values: terraform)
--report string specify a compliance report format for the output (allowed values: all,summary) (default "all")

View File

@@ -127,6 +127,7 @@ trivy image [flags] IMAGE_NAME
--redis-key string redis key file location, if using redis as cache backend
--redis-tls enable redis TLS with public certificates, if using redis as cache backend
--registry-token string registry token
--rego-error-limit int maximum number of compile errors allowed during Rego policy evaluation (default 10)
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
--removed-pkgs detect vulnerabilities of removed packages (only for Alpine)
--render-cause strings specify configuration types for which the rendered causes will be shown in the table report (allowed values: terraform)

View File

@@ -118,6 +118,7 @@ trivy kubernetes [flags] [CONTEXT]
--redis-key string redis key file location, if using redis as cache backend
--redis-tls enable redis TLS with public certificates, if using redis as cache backend
--registry-token string registry token
--rego-error-limit int maximum number of compile errors allowed during Rego policy evaluation (default 10)
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
--render-cause strings specify configuration types for which the rendered causes will be shown in the table report (allowed values: terraform)
--report string specify a report format for the output (allowed values: all,summary) (default "all")

View File

@@ -105,6 +105,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
--redis-key string redis key file location, if using redis as cache backend
--redis-tls enable redis TLS with public certificates, if using redis as cache backend
--registry-token string registry token
--rego-error-limit int maximum number of compile errors allowed during Rego policy evaluation (default 10)
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
--render-cause strings specify configuration types for which the rendered causes will be shown in the table report (allowed values: terraform)
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (allowed values: oci,rekor)

View File

@@ -108,6 +108,7 @@ trivy rootfs [flags] ROOTDIR
--redis-key string redis key file location, if using redis as cache backend
--redis-tls enable redis TLS with public certificates, if using redis as cache backend
--registry-token string registry token
--rego-error-limit int maximum number of compile errors allowed during Rego policy evaluation (default 10)
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
--render-cause strings specify configuration types for which the rendered causes will be shown in the table report (allowed values: terraform)
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (allowed values: oci,rekor)

View File

@@ -495,6 +495,9 @@ rego:
# Same as '--config-data'
data: []
# Same as '--rego-error-limit'
error-limit: 10
# Same as '--include-deprecated-checks'
include-deprecated-checks: false

View File

@@ -46,6 +46,22 @@ This can be repeated for specifying multiple packages.
trivy config --config-check ./my-check --namespaces main --namespaces user ./configs
```
### Limiting Rego compile errors
By default, Trivy limits the number of compile errors allowed during Rego policy compilation.
You can configure this limit using the `--rego-error-limit` flag.
```bash
trivy config --rego-error-limit 20 ./configs
```
This flag controls the maximum number of compile errors Trivy will tolerate before stopping the compilation.
If the number of compile errors exceeds this limit, Trivy will terminate the scan.
You can set `--rego-error-limit 0` to enforce strict checking and disallow any compile errors.
The default value is defined internally via [CompileErrorLimit](https://github.com/aquasecurity/trivy/tree/{{ git.commit }}pkg/iac/rego/scanner.go).
### Private Terraform registries
Trivy can download Terraform code from private registries.
To pass credentials you must use the `TF_TOKEN_` environment variables.

View File

@@ -750,6 +750,7 @@ func initMisconfScannerOption(ctx context.Context, opts flag.Options) (misconf.S
DisableEmbeddedPolicies: disableEmbedded,
DisableEmbeddedLibraries: disableEmbedded,
IncludeDeprecatedChecks: opts.IncludeDeprecatedChecks,
RegoErrorLimit: opts.RegoOptions.ErrorLimit,
TfExcludeDownloaded: opts.TfExcludeDownloaded,
RawConfigScanners: opts.RawConfigScanners,
FilePatterns: opts.FilePatterns,

View File

@@ -723,6 +723,7 @@ func TestTerraformMisconfigurationScan(t *testing.T) {
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set fake UUID for consistent test results
@@ -2347,6 +2348,7 @@ func TestYAMLConfigScan(t *testing.T) {
artifactOpt artifact.Option
wantBlobs []cachetest.WantBlob
want artifact.Reference
wantErr string
}{
{
name: "happy path without custom schema",
@@ -2467,6 +2469,40 @@ func TestYAMLConfigScan(t *testing.T) {
Type: types.TypeFilesystem,
},
},
{
name: "with rego error limit",
fields: fields{
dir: "./testdata/misconfig/yaml/with-rego-error-limit/src",
},
artifactOpt: artifact.Option{
MisconfScannerOption: misconf.ScannerOption{
Namespaces: []string{"user"},
PolicyPaths: []string{
"./testdata/misconfig/yaml/with-rego-error-limit/checks/test_2_errors.rego",
"./testdata/misconfig/yaml/with-rego-error-limit/checks/test.json",
},
RegoErrorLimit: 1,
},
},
wantErr: "ego_type_error: undefined ref: input.wrong_ref",
},
{
name: "with Rego error limit 0",
fields: fields{
dir: "./testdata/misconfig/yaml/with-rego-error-limit/src",
},
artifactOpt: artifact.Option{
MisconfScannerOption: misconf.ScannerOption{
Namespaces: []string{"user"},
PolicyPaths: []string{
"./testdata/misconfig/yaml/with-rego-error-limit/checks/test_1_error.rego",
"./testdata/misconfig/yaml/with-rego-error-limit/checks/test.json",
},
RegoErrorLimit: 0,
},
},
wantErr: "ego_type_error: undefined ref: input.wrong_ref",
},
}
for _, tt := range tests {
@@ -2487,6 +2523,10 @@ func TestYAMLConfigScan(t *testing.T) {
require.NoError(t, err)
got, err := a.Inspect(t.Context())
if tt.wantErr != "" {
assert.ErrorContains(t, err, tt.wantErr)
return
}
require.NoError(t, err)
require.NotNil(t, got)

View File

@@ -0,0 +1,9 @@
{
"$id": "https://example.com/test.schema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"service": { "type": "string" }
},
"required": ["service"]
}

View File

@@ -0,0 +1,13 @@
# METADATA
# title: Test check
# schemas:
# - input: schema.test
# custom:
# id: TEST001
# severity: LOW
package user.test_yaml_check
deny[res] {
input.wrong_ref == "foo"
res := result.new(`Service "foo" should not be used`, input.service)
}

View File

@@ -0,0 +1,18 @@
# METADATA
# title: Test check
# schemas:
# - input: schema.test
# custom:
# id: TEST001
# severity: LOW
package user.test_yaml_check
deny[res] {
input.wrong_ref == "foo"
res := result.new(`Service "foo" should not be used`, input.service)
}
deny[res] {
input.wrong_ref == "bar"
res := result.new(`Provider "bar" should not be used`, input.provider)
}

View File

@@ -0,0 +1 @@
service: foo

View File

@@ -1,5 +1,9 @@
package flag
import (
"github.com/aquasecurity/trivy/pkg/iac/rego"
)
// e.g. config yaml:
//
// rego:
@@ -74,6 +78,13 @@ var (
},
},
}
RegoErrorLimitFlag = Flag[int]{
Name: "rego-error-limit",
ConfigName: "rego.error-limit",
Usage: "maximum number of compile errors allowed during Rego policy evaluation",
TelemetrySafe: true,
Default: rego.CompileErrorLimit,
}
)
// RegoFlagGroup composes common printer flag structs used for commands providing misconfinguration scanning.
@@ -84,6 +95,7 @@ type RegoFlagGroup struct {
CheckPaths *Flag[[]string]
DataPaths *Flag[[]string]
CheckNamespaces *Flag[[]string]
ErrorLimit *Flag[int]
}
type RegoOptions struct {
@@ -93,6 +105,7 @@ type RegoOptions struct {
CheckPaths []string
DataPaths []string
CheckNamespaces []string
ErrorLimit int
}
func NewRegoFlagGroup() *RegoFlagGroup {
@@ -103,6 +116,7 @@ func NewRegoFlagGroup() *RegoFlagGroup {
CheckPaths: ConfigCheckFlag.Clone(),
DataPaths: ConfigDataFlag.Clone(),
CheckNamespaces: CheckNamespaceFlag.Clone(),
ErrorLimit: RegoErrorLimitFlag.Clone(),
}
}
@@ -118,6 +132,7 @@ func (f *RegoFlagGroup) Flags() []Flagger {
f.CheckPaths,
f.DataPaths,
f.CheckNamespaces,
f.ErrorLimit,
}
}
@@ -129,6 +144,7 @@ func (f *RegoFlagGroup) ToOptions(opts *Options) error {
CheckPaths: f.CheckPaths.Value(),
DataPaths: f.DataPaths.Value(),
CheckNamespaces: f.CheckNamespaces.Value(),
ErrorLimit: f.ErrorLimit.Value(),
}
return nil
}

View File

@@ -41,6 +41,7 @@ func RegisterRegoRules(modules map[string]*ast.Module) {
WithCapabilities(nil).
WithUseTypeCheckAnnotations(true)
compiler.SetErrorLimit(CompileErrorLimit)
compiler.Compile(modules)
if compiler.Failed() {
// we should panic as the embedded rego policies are syntactically incorrect...

View File

@@ -248,6 +248,7 @@ func (s *Scanner) compilePolicies(srcFS fs.FS, paths []string) error {
WithCapabilities(ast.CapabilitiesForThisVersion()).
WithSchemas(schemaSet)
compiler.SetErrorLimit(s.regoErrorLimit)
compiler.Compile(s.policies)
if compiler.Failed() {
s.fallbackChecks(compiler)

View File

@@ -9,7 +9,6 @@ import (
"testing"
"testing/fstest"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -255,7 +254,7 @@ func Test_FallbackErrorWithoutLocation(t *testing.T) {
},
}
for i := range ast.CompileErrorLimitDefault + 1 {
for i := range rego.CompileErrorLimit + 1 {
src := `# METADATA
# schemas:
# - input: schema["fooschema"]

View File

@@ -25,9 +25,16 @@ import (
"github.com/aquasecurity/trivy/pkg/set"
)
var checkTypesWithSubtype = set.New(types.SourceCloud, types.SourceDefsec, types.SourceKubernetes)
// CompileErrorLimit defines the default compile error limit.
// We set this value explicitly instead of relying on OPA's ast.CompileErrorLimitDefault
// to avoid dependency on potential upstream changes.
const CompileErrorLimit = 10
var supportedProviders = makeSupportedProviders()
var (
checkTypesWithSubtype = set.New(types.SourceCloud, types.SourceDefsec, types.SourceKubernetes)
supportedProviders = makeSupportedProviders()
)
func makeSupportedProviders() set.Set[string] {
m := set.New[string]()
@@ -92,7 +99,7 @@ func NewScanner(opts ...options.ScannerOption) *Scanner {
LoadAndRegister()
s := &Scanner{
regoErrorLimit: ast.CompileErrorLimitDefault,
regoErrorLimit: CompileErrorLimit,
ruleNamespaces: builtinNamespaces.Clone(),
runtimeValues: addRuntimeValues(),
logger: log.WithPrefix("rego"),

View File

@@ -60,6 +60,7 @@ type ScannerOption struct {
DisableEmbeddedPolicies bool
DisableEmbeddedLibraries bool
IncludeDeprecatedChecks bool
RegoErrorLimit int
HelmValues []string
HelmValueFiles []string
@@ -236,6 +237,10 @@ func initRegoOptions(opt ScannerOption) ([]options.ScannerOption, error) {
rego.WithTrivyVersion(app.Version()),
}
if opt.RegoErrorLimit >= 0 {
opts = append(opts, rego.WithRegoErrorLimits(opt.RegoErrorLimit))
}
policyFS, policyPaths, err := CreatePolicyFS(opt.PolicyPaths)
if err != nil {
return nil, err