mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 07:40:48 -08:00
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:
@@ -24,6 +24,7 @@
|
||||
--pkg-types
|
||||
--quiet
|
||||
--redis-tls
|
||||
--rego-error-limit
|
||||
--removed-pkgs
|
||||
--report
|
||||
--scanners
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
9
pkg/fanal/artifact/local/testdata/misconfig/yaml/with-rego-error-limit/checks/test.json
vendored
Normal file
9
pkg/fanal/artifact/local/testdata/misconfig/yaml/with-rego-error-limit/checks/test.json
vendored
Normal 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"]
|
||||
}
|
||||
13
pkg/fanal/artifact/local/testdata/misconfig/yaml/with-rego-error-limit/checks/test_1_error.rego
vendored
Normal file
13
pkg/fanal/artifact/local/testdata/misconfig/yaml/with-rego-error-limit/checks/test_1_error.rego
vendored
Normal 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)
|
||||
}
|
||||
18
pkg/fanal/artifact/local/testdata/misconfig/yaml/with-rego-error-limit/checks/test_2_errors.rego
vendored
Normal file
18
pkg/fanal/artifact/local/testdata/misconfig/yaml/with-rego-error-limit/checks/test_2_errors.rego
vendored
Normal 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)
|
||||
}
|
||||
1
pkg/fanal/artifact/local/testdata/misconfig/yaml/with-rego-error-limit/src/test1.yaml
vendored
Normal file
1
pkg/fanal/artifact/local/testdata/misconfig/yaml/with-rego-error-limit/src/test1.yaml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
service: foo
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user