mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 15:50:15 -08:00
feat(misconf): Add support for Minimum Trivy Version (#8880)
Signed-off-by: Simar <simar@linux.com> Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io> Co-authored-by: nikpivkin <nikita.pivkin@smartforce.io>
This commit is contained in:
@@ -121,17 +121,18 @@ Trivy supports extra fields in the `custom` section as described below.
|
||||
If you are creating checks for your Trivy misconfiguration scans, some fields are optional as referenced in the table below. The `schemas` field should be used to enable policy validation using a built-in schema. It is recommended to use this to ensure your checks are
|
||||
correct and do not reference incorrect properties/values.
|
||||
|
||||
| Field name | Allowed values | Default value | In table | In JSON |
|
||||
|----------------------------|-------------------------------------------------------------------|:----------------------------:|:--------:|:-------:|
|
||||
| title | Any characters | N/A | ✅ | ✅ |
|
||||
| description | Any characters | | - | ✅ |
|
||||
| schemas.input | `schema["kubernetes"]`, `schema["dockerfile"]`, `schema["cloud"]` | (applied to all input types) | - | - |
|
||||
| custom.id | Any characters | N/A | ✅ | ✅ |
|
||||
| custom.severity | `LOW`, `MEDIUM`, `HIGH`, `CRITICAL` | UNKNOWN | ✅ | ✅ |
|
||||
| custom.recommended_actions | Any characters | | - | ✅ |
|
||||
| custom.deprecated | `true`, `false` | `false` | - | ✅ |
|
||||
| custom.input.selector.type | Any item(s) in [this list][source-types] | | - | ✅ |
|
||||
| url | Any characters | | - | ✅ |
|
||||
| Field name | Allowed values | Default value | In table | In JSON |
|
||||
|------------------------------|---------------------------------------------------------------------|:----------------------------:|:--------:|:-------:|
|
||||
| title | Any characters | N/A | ✅ | ✅ |
|
||||
| description | Any characters | | - | ✅ |
|
||||
| schemas.input | `schema["kubernetes"]`, `schema["dockerfile"]`, `schema["cloud"]` | (applied to all input types) | - | - |
|
||||
| custom.id | Any characters | N/A | ✅ | ✅ |
|
||||
| custom.severity | `LOW`, `MEDIUM`, `HIGH`, `CRITICAL` | UNKNOWN | ✅ | ✅ |
|
||||
| custom.recommended_actions | Any characters | | - | ✅ |
|
||||
| custom.deprecated | `true`, `false` | `false` | - | ✅ |
|
||||
| custom.input.selector.type | Any item(s) in [this list][source-types] | | - | ✅ |
|
||||
| custom.minimum_trivy_version | The minimum version of Trivy that's required to evaluate this check | | - | ✅ |
|
||||
| url | Any characters | | - | ✅ |
|
||||
|
||||
#### custom.avd_id and custom.id
|
||||
|
||||
|
||||
@@ -305,6 +305,7 @@ func compareReports(t *testing.T, wantFile, gotFile string, override func(t *tes
|
||||
if override != nil {
|
||||
override(t, &want, &got)
|
||||
}
|
||||
|
||||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,43 +27,49 @@
|
||||
"Misconfigurations": [
|
||||
{
|
||||
"Type": "Dockerfile Security Check",
|
||||
"ID": "N/A",
|
||||
"Title": "N/A",
|
||||
"Description": "Rego module: data.user.bar",
|
||||
"ID": "AVD-BAR-0001",
|
||||
"AVDID": "AVD-BAR-0001",
|
||||
"Title": "Custom policy",
|
||||
"Description": "Custom policy for testing",
|
||||
"Message": "something bad: bar",
|
||||
"Namespace": "user.bar",
|
||||
"Query": "data.user.bar.deny",
|
||||
"Severity": "UNKNOWN",
|
||||
"Resolution": "Custom policy for testing",
|
||||
"Severity": "LOW",
|
||||
"Status": "FAIL",
|
||||
"Layer": {},
|
||||
"CauseMetadata": {
|
||||
"Provider": "Generic",
|
||||
"Service": "general",
|
||||
"Provider": "Custom",
|
||||
"Service": "custom",
|
||||
"Code": {
|
||||
"Lines": null
|
||||
}
|
||||
},
|
||||
"RenderedCause": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Type": "Dockerfile Security Check",
|
||||
"ID": "N/A",
|
||||
"Title": "N/A",
|
||||
"Description": "Rego module: data.user.foo",
|
||||
"ID": "AVD-FOO-0001",
|
||||
"AVDID": "AVD-FOO-0001",
|
||||
"Title": "Custom policy",
|
||||
"Description": "Custom policy for testing",
|
||||
"Message": "something bad: foo",
|
||||
"Namespace": "user.foo",
|
||||
"Query": "data.user.foo.deny",
|
||||
"Severity": "UNKNOWN",
|
||||
"Resolution": "Custom policy for testing",
|
||||
"Severity": "LOW",
|
||||
"Status": "FAIL",
|
||||
"Layer": {},
|
||||
"CauseMetadata": {
|
||||
"Provider": "Generic",
|
||||
"Service": "general",
|
||||
"Provider": "Custom",
|
||||
"Service": "custom",
|
||||
"Code": {
|
||||
"Lines": null
|
||||
}
|
||||
},
|
||||
"RenderedCause": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,17 @@
|
||||
# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
package user.bar
|
||||
|
||||
deny[res] {
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-FOO-0001
|
||||
# avd_id: AVD-FOO-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
package user.foo
|
||||
|
||||
deny[res] {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/open-policy-agent/opa/v1/bundle"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/aquasecurity/go-version/pkg/semver"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/set"
|
||||
"github.com/aquasecurity/trivy/pkg/version/doc"
|
||||
@@ -293,9 +294,33 @@ func (s *Scanner) handleModulesMetadata(path string, module *ast.Module) {
|
||||
return
|
||||
}
|
||||
|
||||
if metadata != nil {
|
||||
s.moduleMetadata[path] = metadata
|
||||
s.moduleMetadata[path] = metadata
|
||||
}
|
||||
|
||||
func (s *Scanner) IsMinimumVersionSupported(metadata *StaticMetadata, module *ast.Module, tv semver.Version) bool {
|
||||
if metadata.MinimumTrivyVersion == "" { // to ensure compatibility with old modules without minimum trivy version
|
||||
return true
|
||||
}
|
||||
|
||||
mmsv, err := semver.Parse(metadata.MinimumTrivyVersion)
|
||||
if err != nil {
|
||||
s.logger.Warn(
|
||||
"Failed to parse minimum trivy version - skipping as cannot confirm if module will work with current version",
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.Err(err),
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
if tv.LessThan(mmsv) {
|
||||
s.logger.Warn(
|
||||
"Module will be skipped as current version of Trivy is older than minimum trivy version required - please update Trivy to use this module",
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.String("minimum_trivy_version", metadata.MinimumTrivyVersion),
|
||||
)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// moduleHasLegacyMetadataFormat checks if the module has a legacy metadata format.
|
||||
@@ -325,8 +350,22 @@ func (s *Scanner) filterModules() error {
|
||||
return fmt.Errorf("retrieve metadata for module %s: %w", name, err)
|
||||
}
|
||||
|
||||
if s.isModuleApplicable(module, metadata, name) {
|
||||
filtered[name] = module
|
||||
if metadata != nil {
|
||||
tv, err := semver.Parse(s.trivyVersion)
|
||||
if err != nil {
|
||||
s.logger.Warn(
|
||||
"Failed to parse Trivy version - cannot confirm if module will work with current version",
|
||||
log.String("trivy_version", s.trivyVersion),
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.Err(err),
|
||||
)
|
||||
} else if !s.IsMinimumVersionSupported(metadata, module, tv) {
|
||||
continue
|
||||
}
|
||||
|
||||
if s.isModuleApplicable(module, metadata, name) {
|
||||
filtered[name] = module
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
checks "github.com/aquasecurity/trivy-checks"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/rego"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
)
|
||||
|
||||
@@ -273,3 +274,93 @@ deny := some_func(input)
|
||||
err := scanner.LoadPolicies(nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestIsMinimumTrivyVersion(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
trivyVersion string
|
||||
MinimumTrivyVersion string
|
||||
expectedResults int
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "trivy version is newer than the check version",
|
||||
trivyVersion: "1.2.3",
|
||||
MinimumTrivyVersion: "1.2.0",
|
||||
expectedResults: 1,
|
||||
},
|
||||
{
|
||||
name: "trivy version is older than the check version",
|
||||
trivyVersion: "1.2.0",
|
||||
MinimumTrivyVersion: "1.2.3",
|
||||
expectedResults: 0,
|
||||
},
|
||||
{
|
||||
name: "trivy version is equal to the check version",
|
||||
trivyVersion: "1.2.3",
|
||||
MinimumTrivyVersion: "1.2.3",
|
||||
expectedResults: 1,
|
||||
},
|
||||
{
|
||||
name: "check version is not a valid semver",
|
||||
trivyVersion: "1.2.3",
|
||||
MinimumTrivyVersion: "invalid",
|
||||
expectedResults: 0,
|
||||
},
|
||||
{
|
||||
name: "trivy version is not a valid semver", // if we cannot parse the version, we fail open to allow the check to run
|
||||
trivyVersion: "invalid",
|
||||
MinimumTrivyVersion: "1.2.3",
|
||||
expectedResults: 1,
|
||||
},
|
||||
{
|
||||
name: "check version is not set",
|
||||
trivyVersion: "1.2.3",
|
||||
MinimumTrivyVersion: "",
|
||||
expectedResults: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fsys := fstest.MapFS{
|
||||
"check.rego": &fstest.MapFile{Data: []byte(fmt.Sprintf(`# METADATA
|
||||
# title: "dummy title"
|
||||
# description: "some description"
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# minimum_trivy_version: "%s"
|
||||
package builtin.foo.ABC123
|
||||
deny {
|
||||
input.evil
|
||||
}`, tc.MinimumTrivyVersion))},
|
||||
}
|
||||
scanner := rego.NewScanner(
|
||||
rego.WithPolicyDirs("."),
|
||||
rego.WithEmbeddedLibraries(false),
|
||||
rego.WithEmbeddedPolicies(false),
|
||||
rego.WithPolicyFilesystem(fsys),
|
||||
rego.WithTrivyVersion(tc.trivyVersion),
|
||||
)
|
||||
err := scanner.LoadPolicies(nil)
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
results, err := scanner.ScanInput(t.Context(), types.SourceJSON, rego.Input{
|
||||
Path: "/check.rego",
|
||||
Contents: map[string]any{
|
||||
"evil": true,
|
||||
},
|
||||
FS: fsys,
|
||||
})
|
||||
|
||||
if tc.expectedErr != "" {
|
||||
assert.ErrorContains(t, err, tc.expectedErr, tc.name)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results, tc.expectedResults, tc.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,26 +23,27 @@ import (
|
||||
const annotationScopePackage = "package"
|
||||
|
||||
type StaticMetadata struct {
|
||||
Deprecated bool
|
||||
ID string
|
||||
AVDID string
|
||||
Title string
|
||||
ShortCode string
|
||||
Aliases []string
|
||||
Description string
|
||||
Severity string
|
||||
RecommendedActions string
|
||||
PrimaryURL string
|
||||
References []string
|
||||
InputOptions InputOptions
|
||||
Package string
|
||||
Frameworks map[framework.Framework][]string
|
||||
Provider string
|
||||
Service string
|
||||
Library bool
|
||||
CloudFormation *scan.EngineMetadata
|
||||
Terraform *scan.EngineMetadata
|
||||
Examples string
|
||||
Deprecated bool
|
||||
ID string
|
||||
AVDID string
|
||||
Title string
|
||||
ShortCode string
|
||||
Aliases []string
|
||||
Description string
|
||||
Severity string
|
||||
RecommendedActions string
|
||||
PrimaryURL string
|
||||
References []string
|
||||
InputOptions InputOptions
|
||||
Package string
|
||||
Frameworks map[framework.Framework][]string
|
||||
Provider string
|
||||
Service string
|
||||
Library bool
|
||||
CloudFormation *scan.EngineMetadata
|
||||
Terraform *scan.EngineMetadata
|
||||
Examples string
|
||||
MinimumTrivyVersion string
|
||||
}
|
||||
|
||||
func NewStaticMetadata(pkgPath string, inputOpt InputOptions) *StaticMetadata {
|
||||
@@ -80,6 +81,7 @@ func (sm *StaticMetadata) populate(meta map[string]any) error {
|
||||
upd(&sm.RecommendedActions, "recommended_actions")
|
||||
upd(&sm.RecommendedActions, "recommended_action")
|
||||
upd(&sm.Examples, "examples")
|
||||
upd(&sm.MinimumTrivyVersion, "minimum_trivy_version")
|
||||
|
||||
if raw, ok := meta["deprecated"]; ok {
|
||||
if dep, ok := raw.(bool); ok {
|
||||
@@ -87,6 +89,10 @@ func (sm *StaticMetadata) populate(meta map[string]any) error {
|
||||
}
|
||||
}
|
||||
|
||||
if raw, ok := meta["minimum_trivy_version"]; ok {
|
||||
sm.MinimumTrivyVersion = raw.(string)
|
||||
}
|
||||
|
||||
if raw, ok := meta["severity"]; ok {
|
||||
sm.Severity = strings.ToUpper(fmt.Sprintf("%s", raw))
|
||||
}
|
||||
@@ -265,23 +271,24 @@ func (sm *StaticMetadata) ToRule() scan.Rule {
|
||||
}
|
||||
|
||||
return scan.Rule{
|
||||
Deprecated: sm.Deprecated,
|
||||
AVDID: sm.AVDID,
|
||||
Aliases: append(sm.Aliases, sm.ID),
|
||||
ShortCode: sm.ShortCode,
|
||||
Summary: sm.Title,
|
||||
Explanation: sm.Description,
|
||||
Impact: "",
|
||||
Resolution: sm.RecommendedActions,
|
||||
Provider: providers.Provider(provider),
|
||||
Service: cmp.Or(sm.Service, "general"),
|
||||
Links: sm.References,
|
||||
Severity: severity.Severity(sm.Severity),
|
||||
RegoPackage: sm.Package,
|
||||
Frameworks: sm.Frameworks,
|
||||
CloudFormation: sm.CloudFormation,
|
||||
Terraform: sm.Terraform,
|
||||
Examples: sm.Examples,
|
||||
Deprecated: sm.Deprecated,
|
||||
AVDID: sm.AVDID,
|
||||
Aliases: append(sm.Aliases, sm.ID),
|
||||
ShortCode: sm.ShortCode,
|
||||
Summary: sm.Title,
|
||||
Explanation: sm.Description,
|
||||
Impact: "",
|
||||
Resolution: sm.RecommendedActions,
|
||||
Provider: providers.Provider(provider),
|
||||
Service: cmp.Or(sm.Service, "general"),
|
||||
Links: sm.References,
|
||||
Severity: severity.Severity(sm.Severity),
|
||||
RegoPackage: sm.Package,
|
||||
Frameworks: sm.Frameworks,
|
||||
CloudFormation: sm.CloudFormation,
|
||||
Terraform: sm.Terraform,
|
||||
Examples: sm.Examples,
|
||||
MinimumTrivyVersion: sm.MinimumTrivyVersion,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,20 +14,21 @@ import (
|
||||
func Test_UpdateStaticMetadata(t *testing.T) {
|
||||
t.Run("happy", func(t *testing.T) {
|
||||
sm := StaticMetadata{
|
||||
ID: "i",
|
||||
AVDID: "a",
|
||||
Title: "t",
|
||||
ShortCode: "sc",
|
||||
Aliases: []string{"a", "b", "c"},
|
||||
Description: "d",
|
||||
Severity: "s",
|
||||
RecommendedActions: "ra",
|
||||
PrimaryURL: "pu",
|
||||
References: []string{"r"},
|
||||
Package: "pkg",
|
||||
Provider: "pr",
|
||||
Service: "srvc",
|
||||
Library: false,
|
||||
ID: "i",
|
||||
AVDID: "a",
|
||||
Title: "t",
|
||||
ShortCode: "sc",
|
||||
Aliases: []string{"a", "b", "c"},
|
||||
Description: "d",
|
||||
Severity: "s",
|
||||
RecommendedActions: "ra",
|
||||
PrimaryURL: "pu",
|
||||
References: []string{"r"},
|
||||
Package: "pkg",
|
||||
Provider: "pr",
|
||||
Service: "srvc",
|
||||
Library: false,
|
||||
MinimumTrivyVersion: "v1.2.3",
|
||||
}
|
||||
|
||||
require.NoError(t, sm.populate(
|
||||
@@ -47,24 +48,26 @@ func Test_UpdateStaticMetadata(t *testing.T) {
|
||||
"frameworks": map[string]any{
|
||||
"all": []any{"aa"},
|
||||
},
|
||||
"minimum_trivy_version": "v1.2.3",
|
||||
},
|
||||
))
|
||||
|
||||
expected := StaticMetadata{
|
||||
ID: "i_n",
|
||||
AVDID: "a_n",
|
||||
Title: "t_n",
|
||||
ShortCode: "sc_n",
|
||||
Aliases: []string{"a", "b", "c", "a_n", "b_n", "c_n"},
|
||||
Description: "d_n",
|
||||
Severity: "S_N",
|
||||
RecommendedActions: "ra_n",
|
||||
PrimaryURL: "pu",
|
||||
References: []string{"r", "r_n"},
|
||||
Package: "pkg",
|
||||
Provider: "pr_n",
|
||||
Service: "srvc_n",
|
||||
Library: true,
|
||||
ID: "i_n",
|
||||
AVDID: "a_n",
|
||||
Title: "t_n",
|
||||
ShortCode: "sc_n",
|
||||
Aliases: []string{"a", "b", "c", "a_n", "b_n", "c_n"},
|
||||
Description: "d_n",
|
||||
Severity: "S_N",
|
||||
RecommendedActions: "ra_n",
|
||||
PrimaryURL: "pu",
|
||||
MinimumTrivyVersion: "v1.2.3",
|
||||
References: []string{"r", "r_n"},
|
||||
Package: "pkg",
|
||||
Provider: "pr_n",
|
||||
Service: "srvc_n",
|
||||
Library: true,
|
||||
Frameworks: map[framework.Framework][]string{
|
||||
framework.ALL: {"aa"},
|
||||
},
|
||||
@@ -248,6 +251,7 @@ func TestMetadataFromAnnotations(t *testing.T) {
|
||||
# id: test-001
|
||||
# avd_id: test-001
|
||||
# severity: LOW
|
||||
# minimum_trivy_version: 1.2.3
|
||||
# input:
|
||||
# selector:
|
||||
# - type: yaml
|
||||
@@ -265,7 +269,8 @@ package user.test
|
||||
},
|
||||
},
|
||||
},
|
||||
Package: "data.user.test",
|
||||
MinimumTrivyVersion: "1.2.3",
|
||||
Package: "data.user.test",
|
||||
Frameworks: map[framework.Framework][]string{
|
||||
"default": {},
|
||||
},
|
||||
|
||||
@@ -130,3 +130,11 @@ func WithFrameworks(frameworks ...framework.Framework) options.ScannerOption {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WithTrivyVersion(version string) options.ScannerOption {
|
||||
return func(s options.ConfigurableScanner) {
|
||||
if ss, ok := s.(*Scanner); ok {
|
||||
ss.trivyVersion = version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ type Scanner struct {
|
||||
includeDeprecatedChecks bool
|
||||
includeEmbeddedPolicies bool
|
||||
includeEmbeddedLibraries bool
|
||||
trivyVersion string
|
||||
|
||||
embeddedLibs map[string]*ast.Module
|
||||
embeddedChecks map[string]*ast.Module
|
||||
|
||||
@@ -34,6 +34,21 @@ func Test_RegoScanning_Deny(t *testing.T) {
|
||||
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
deny {
|
||||
@@ -67,7 +82,21 @@ func Test_RegoScanning_AbsolutePolicyPath_Deny(t *testing.T) {
|
||||
|
||||
tmp := t.TempDir()
|
||||
require.NoError(t, os.Mkdir(filepath.Join(tmp, "policies"), 0o755))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(tmp, "policies", "test.rego"), []byte(`package defsec.test
|
||||
require.NoError(t, os.WriteFile(filepath.Join(tmp, "policies", "test.rego"), []byte(`# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
package defsec.test
|
||||
|
||||
deny {
|
||||
input.evil
|
||||
@@ -98,7 +127,21 @@ deny {
|
||||
|
||||
func Test_RegoScanning_Allow(t *testing.T) {
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
deny {
|
||||
@@ -132,7 +175,21 @@ func Test_RegoScanning_WithRuntimeValues(t *testing.T) {
|
||||
t.Setenv("DEFSEC_RUNTIME_VAL", "AOK")
|
||||
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
deny_evil {
|
||||
@@ -162,7 +219,21 @@ deny_evil {
|
||||
|
||||
func Test_RegoScanning_WithDenyMessage(t *testing.T) {
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
deny[msg] {
|
||||
@@ -196,6 +267,21 @@ deny[msg] {
|
||||
func Test_RegoScanning_WithDenyMetadata_ImpliedPath(t *testing.T) {
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
deny[res] {
|
||||
@@ -236,6 +322,21 @@ deny[res] {
|
||||
func Test_RegoScanning_WithDenyMetadata_PersistedPath(t *testing.T) {
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
deny[res] {
|
||||
@@ -337,7 +438,21 @@ deny[res] {
|
||||
|
||||
func Test_RegoScanning_WithMatchingInputSelector(t *testing.T) {
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
__rego_input__ := {
|
||||
@@ -405,7 +520,21 @@ deny {
|
||||
func Test_RegoScanning_NoTracingByDefault(t *testing.T) {
|
||||
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
deny {
|
||||
@@ -437,7 +566,21 @@ deny {
|
||||
func Test_RegoScanning_GlobalTracingEnabled(t *testing.T) {
|
||||
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
|
||||
deny {
|
||||
@@ -473,7 +616,20 @@ deny {
|
||||
func Test_RegoScanning_PerResultTracingEnabled(t *testing.T) {
|
||||
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
package defsec.test
|
||||
|
||||
deny {
|
||||
@@ -689,7 +845,21 @@ deny {
|
||||
|
||||
func Test_RegoScanning_CustomData(t *testing.T) {
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
import data.settings.DS123.foo_bar_baz
|
||||
|
||||
@@ -728,7 +898,21 @@ deny {
|
||||
|
||||
func Test_RegoScanning_InvalidFS(t *testing.T) {
|
||||
srcFS := CreateFS(t, map[string]string{
|
||||
"policies/test.rego": `
|
||||
"policies/test.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package defsec.test
|
||||
import data.settings.DS123.foo_bar_baz
|
||||
|
||||
|
||||
@@ -36,25 +36,26 @@ type TerraformCustomCheck struct {
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
Deprecated bool `json:"deprecated"`
|
||||
AVDID string `json:"avd_id"`
|
||||
Aliases []string `json:"aliases"`
|
||||
ShortCode string `json:"short_code"`
|
||||
Summary string `json:"summary"`
|
||||
Explanation string `json:"explanation"`
|
||||
Impact string `json:"impact"`
|
||||
Resolution string `json:"resolution"`
|
||||
Provider providers.Provider `json:"provider"`
|
||||
Service string `json:"service"`
|
||||
Links []string `json:"links"`
|
||||
Severity severity.Severity `json:"severity"`
|
||||
Terraform *EngineMetadata `json:"terraform,omitempty"`
|
||||
CloudFormation *EngineMetadata `json:"cloud_formation,omitempty"`
|
||||
Examples string `json:"-"`
|
||||
CustomChecks CustomChecks `json:"-"`
|
||||
RegoPackage string `json:"-"`
|
||||
Frameworks map[framework.Framework][]string `json:"frameworks"`
|
||||
Check CheckFunc `json:"-"`
|
||||
Deprecated bool `json:"deprecated"`
|
||||
AVDID string `json:"avd_id"`
|
||||
Aliases []string `json:"aliases"`
|
||||
ShortCode string `json:"short_code"`
|
||||
Summary string `json:"summary"`
|
||||
Explanation string `json:"explanation"`
|
||||
Impact string `json:"impact"`
|
||||
Resolution string `json:"resolution"`
|
||||
Provider providers.Provider `json:"provider"`
|
||||
Service string `json:"service"`
|
||||
Links []string `json:"links"`
|
||||
Severity severity.Severity `json:"severity"`
|
||||
Terraform *EngineMetadata `json:"terraform,omitempty"`
|
||||
CloudFormation *EngineMetadata `json:"cloud_formation,omitempty"`
|
||||
Examples string `json:"-"`
|
||||
CustomChecks CustomChecks `json:"-"`
|
||||
RegoPackage string `json:"-"`
|
||||
Frameworks map[framework.Framework][]string `json:"frameworks"`
|
||||
Check CheckFunc `json:"-"`
|
||||
MinimumTrivyVersion string `json:"minimum_trivy_version"`
|
||||
}
|
||||
|
||||
func (r Rule) IsDeprecated() bool {
|
||||
|
||||
@@ -133,7 +133,14 @@ deny[res] {
|
||||
func Test_YamlWithSeparator(t *testing.T) {
|
||||
|
||||
fsys := buildFS(map[string]string{
|
||||
"check.rego": `package defsec
|
||||
"check.rego": `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
|
||||
package defsec
|
||||
|
||||
deny[res] {
|
||||
input.kind == "Pod"
|
||||
|
||||
@@ -972,7 +972,22 @@ resource "aws_s3_bucket_versioning" "test" {
|
||||
`)},
|
||||
}
|
||||
|
||||
check := `package test
|
||||
check := `# METADATA
|
||||
# title: Custom policy
|
||||
# description: Custom policy for testing
|
||||
# scope: package
|
||||
# schemas:
|
||||
# - input: schema["input"]
|
||||
# custom:
|
||||
# id: AVD-BAR-0001
|
||||
# avd_id: AVD-BAR-0001
|
||||
# provider: custom
|
||||
# service: custom
|
||||
# severity: LOW
|
||||
# short_code: custom-policy
|
||||
# recommended_action: Custom policy for testing
|
||||
|
||||
package test
|
||||
import rego.v1
|
||||
|
||||
deny contains res if {
|
||||
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
tfpjsonscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/tfjson"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/mapfs"
|
||||
"github.com/aquasecurity/trivy/pkg/version/app"
|
||||
|
||||
_ "embed"
|
||||
)
|
||||
@@ -246,6 +247,7 @@ func initRegoOptions(opt ScannerOption) ([]options.ScannerOption, error) {
|
||||
rego.WithEmbeddedLibraries(!opt.DisableEmbeddedLibraries),
|
||||
rego.WithIncludeDeprecatedChecks(opt.IncludeDeprecatedChecks),
|
||||
rego.WithDisabledCheckIDs(disabledCheckIDs...),
|
||||
rego.WithTrivyVersion(app.Version()),
|
||||
}
|
||||
|
||||
policyFS, policyPaths, err := CreatePolicyFS(opt.PolicyPaths)
|
||||
|
||||
Reference in New Issue
Block a user