From 01c98d1516047f6dd41e1d943f0ead3b27e53c93 Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Mon, 23 Oct 2023 01:32:08 -0600 Subject: [PATCH] feat(misconf): Support `--ignore-policy` in config scans (#5359) Signed-off-by: Simar --- .../configuration/cli/trivy_config.md | 1 + pkg/commands/app.go | 1 - pkg/result/filter.go | 25 ++++--- pkg/result/filter_test.go | 67 ++++++++++++++++++- .../testdata/test-ignore-policy-misconf.rego | 9 +++ 5 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 pkg/result/testdata/test-ignore-policy-misconf.rego diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 76a0bf91cb3b..9cb61d2990c2 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -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) diff --git a/pkg/commands/app.go b/pkg/commands/app.go index b9937b6b8756..85cf164275ff 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -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 diff --git a/pkg/result/filter.go b/pkg/result/filter.go index 8e2df0b92db9..9dc738d318aa 100644 --- a/pkg/result/filter.go +++ b/pkg/result/filter.go @@ -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)) diff --git a/pkg/result/filter_test.go b/pkg/result/filter_test.go index 96698a1874cd..afe32726aa43 100644 --- a/pkg/result/filter_test.go +++ b/pkg/result/filter_test.go @@ -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{ diff --git a/pkg/result/testdata/test-ignore-policy-misconf.rego b/pkg/result/testdata/test-ignore-policy-misconf.rego new file mode 100644 index 000000000000..39838ef27210 --- /dev/null +++ b/pkg/result/testdata/test-ignore-policy-misconf.rego @@ -0,0 +1,9 @@ +package trivy + +import data.lib.trivy + +default ignore=false + +ignore { + input.AVDID != "AVD-TEST-0001" +}