mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-23 07:29:00 -08:00
feat(misconf): Support --ignore-policy in config scans (#5359)
Signed-off-by: Simar <simar@linux.com>
This commit is contained in:
@@ -24,6 +24,7 @@ trivy config [flags] DIR
|
||||
--helm-set-string strings specify Helm string values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)
|
||||
--helm-values strings specify paths to override the Helm values.yaml files
|
||||
-h, --help help for config
|
||||
--ignore-policy string specify the Rego file path to evaluate each vulnerability
|
||||
--ignorefile string specify .trivyignore file (default ".trivyignore")
|
||||
--include-non-failures include successes and exceptions, available with '--scanners config'
|
||||
--k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0)
|
||||
|
||||
@@ -622,7 +622,6 @@ func NewServerCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
||||
func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
||||
reportFlagGroup := flag.NewReportFlagGroup()
|
||||
reportFlagGroup.DependencyTree = nil // disable '--dependency-tree'
|
||||
reportFlagGroup.IgnorePolicy = nil // disable '--ignore-policy'
|
||||
reportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs'
|
||||
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
|
||||
reportFormat := flag.ReportFormatFlag
|
||||
|
||||
@@ -65,18 +65,24 @@ func FilterResult(ctx context.Context, result *types.Result, ignoreConf IgnoreCo
|
||||
result.Secrets = filterSecrets(result, severities, ignoreConf.Secrets)
|
||||
result.Licenses = filterLicenses(result.Licenses, severities, opt.IgnoreLicenses, ignoreConf.Licenses)
|
||||
|
||||
var ignoredMisconfs int
|
||||
if opt.PolicyFile != "" {
|
||||
var err error
|
||||
filteredVulns, filteredMisconfs, err = applyPolicy(ctx, filteredVulns, filteredMisconfs, opt.PolicyFile)
|
||||
var ignored int
|
||||
filteredVulns, filteredMisconfs, ignored, err = applyPolicy(ctx, filteredVulns, filteredMisconfs, opt.PolicyFile)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to apply the policy: %w", err)
|
||||
}
|
||||
ignoredMisconfs += ignored
|
||||
}
|
||||
sort.Sort(types.BySeverity(filteredVulns))
|
||||
|
||||
result.Vulnerabilities = filteredVulns
|
||||
result.Misconfigurations = filteredMisconfs
|
||||
result.MisconfSummary = misconfSummary
|
||||
if result.MisconfSummary != nil {
|
||||
result.MisconfSummary.Exceptions += ignoredMisconfs
|
||||
}
|
||||
result.Misconfigurations = filteredMisconfs
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -147,6 +153,7 @@ func filterMisconfigurations(result *types.Result, severities []string, includeN
|
||||
continue
|
||||
} else if ignoreMisconfs.Match(result.Target, misconf.ID) || ignoreMisconfs.Match(result.Target, misconf.AVDID) {
|
||||
// Filter misconfigurations by ignore file
|
||||
summary.Exceptions++
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -215,10 +222,10 @@ func summarize(status types.MisconfStatus, summary *types.MisconfSummary) {
|
||||
}
|
||||
|
||||
func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misconfs []types.DetectedMisconfiguration,
|
||||
policyFile string) ([]types.DetectedVulnerability, []types.DetectedMisconfiguration, error) {
|
||||
policyFile string) ([]types.DetectedVulnerability, []types.DetectedMisconfiguration, int, error) {
|
||||
policy, err := os.ReadFile(policyFile)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("unable to read the policy file: %w", err)
|
||||
return nil, nil, 0, xerrors.Errorf("unable to read the policy file: %w", err)
|
||||
}
|
||||
|
||||
query, err := rego.New(
|
||||
@@ -227,7 +234,7 @@ func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misco
|
||||
rego.Module("trivy.rego", string(policy)),
|
||||
).PrepareForEval(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, xerrors.Errorf("unable to prepare for eval: %w", err)
|
||||
return nil, nil, 0, xerrors.Errorf("unable to prepare for eval: %w", err)
|
||||
}
|
||||
|
||||
// Vulnerabilities
|
||||
@@ -235,7 +242,7 @@ func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misco
|
||||
for _, vuln := range vulns {
|
||||
ignored, err := evaluate(ctx, query, vuln)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
if ignored {
|
||||
continue
|
||||
@@ -244,18 +251,20 @@ func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misco
|
||||
}
|
||||
|
||||
// Misconfigurations
|
||||
var ignoredMisconfs int
|
||||
var filteredMisconfs []types.DetectedMisconfiguration
|
||||
for _, misconf := range misconfs {
|
||||
ignored, err := evaluate(ctx, query, misconf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
if ignored {
|
||||
ignoredMisconfs++
|
||||
continue
|
||||
}
|
||||
filteredMisconfs = append(filteredMisconfs, misconf)
|
||||
}
|
||||
return filteredVulns, filteredMisconfs, nil
|
||||
return filteredVulns, filteredMisconfs, ignoredMisconfs, nil
|
||||
}
|
||||
func evaluate(ctx context.Context, query rego.PreparedEvalQuery, input interface{}) (bool, error) {
|
||||
results, err := query.Eval(ctx, rego.EvalInput(input))
|
||||
|
||||
@@ -383,6 +383,11 @@ func TestFilter(t *testing.T) {
|
||||
{
|
||||
Target: "Dockerfile",
|
||||
Class: types.ClassConfig,
|
||||
MisconfSummary: &types.MisconfSummary{
|
||||
Successes: 0,
|
||||
Failures: 0,
|
||||
Exceptions: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Secrets: []ftypes.SecretFinding{
|
||||
@@ -584,7 +589,7 @@ func TestFilter(t *testing.T) {
|
||||
MisconfSummary: &types.MisconfSummary{
|
||||
Successes: 0,
|
||||
Failures: 1,
|
||||
Exceptions: 0,
|
||||
Exceptions: 2,
|
||||
},
|
||||
Misconfigurations: []types.DetectedMisconfiguration{
|
||||
{
|
||||
@@ -685,6 +690,66 @@ func TestFilter(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignore file for misconf",
|
||||
args: args{
|
||||
report: types.Report{
|
||||
Results: types.Results{
|
||||
{
|
||||
Misconfigurations: []types.DetectedMisconfiguration{
|
||||
{
|
||||
ID: "AVD-TEST-0001",
|
||||
AVDID: "AVD-TEST-0001",
|
||||
Title: "test-0001",
|
||||
Description: "foo",
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
Status: types.StatusFailure,
|
||||
},
|
||||
{
|
||||
ID: "AVD-TEST-0002",
|
||||
AVDID: "AVD-TEST-0002",
|
||||
Title: "test-0002",
|
||||
Description: "bar",
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
Status: types.StatusPassed,
|
||||
},
|
||||
{ // this misconf is ignored
|
||||
ID: "AVD-TEST-0003",
|
||||
AVDID: "AVD-TEST-0003",
|
||||
Title: "test-0003",
|
||||
Description: "baz",
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
Status: types.StatusFailure,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
severities: []dbTypes.Severity{dbTypes.SeverityHigh},
|
||||
policyFile: "./testdata/test-ignore-policy-misconf.rego",
|
||||
},
|
||||
want: types.Report{
|
||||
Results: types.Results{
|
||||
{
|
||||
MisconfSummary: &types.MisconfSummary{
|
||||
Successes: 1,
|
||||
Failures: 2,
|
||||
Exceptions: 1,
|
||||
},
|
||||
Misconfigurations: []types.DetectedMisconfiguration{
|
||||
{
|
||||
ID: "AVD-TEST-0001",
|
||||
AVDID: "AVD-TEST-0001",
|
||||
Title: "test-0001",
|
||||
Description: "foo",
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
Status: types.StatusFailure,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with duplicates, one with empty fixed version",
|
||||
args: args{
|
||||
|
||||
9
pkg/result/testdata/test-ignore-policy-misconf.rego
vendored
Normal file
9
pkg/result/testdata/test-ignore-policy-misconf.rego
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
package trivy
|
||||
|
||||
import data.lib.trivy
|
||||
|
||||
default ignore=false
|
||||
|
||||
ignore {
|
||||
input.AVDID != "AVD-TEST-0001"
|
||||
}
|
||||
Reference in New Issue
Block a user