mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 23:26:39 -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-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
|
--helm-values strings specify paths to override the Helm values.yaml files
|
||||||
-h, --help help for config
|
-h, --help help for config
|
||||||
|
--ignore-policy string specify the Rego file path to evaluate each vulnerability
|
||||||
--ignorefile string specify .trivyignore file (default ".trivyignore")
|
--ignorefile string specify .trivyignore file (default ".trivyignore")
|
||||||
--include-non-failures include successes and exceptions, available with '--scanners config'
|
--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)
|
--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 {
|
func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
|
||||||
reportFlagGroup := flag.NewReportFlagGroup()
|
reportFlagGroup := flag.NewReportFlagGroup()
|
||||||
reportFlagGroup.DependencyTree = nil // disable '--dependency-tree'
|
reportFlagGroup.DependencyTree = nil // disable '--dependency-tree'
|
||||||
reportFlagGroup.IgnorePolicy = nil // disable '--ignore-policy'
|
|
||||||
reportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs'
|
reportFlagGroup.ListAllPkgs = nil // disable '--list-all-pkgs'
|
||||||
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
|
reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
|
||||||
reportFormat := flag.ReportFormatFlag
|
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.Secrets = filterSecrets(result, severities, ignoreConf.Secrets)
|
||||||
result.Licenses = filterLicenses(result.Licenses, severities, opt.IgnoreLicenses, ignoreConf.Licenses)
|
result.Licenses = filterLicenses(result.Licenses, severities, opt.IgnoreLicenses, ignoreConf.Licenses)
|
||||||
|
|
||||||
|
var ignoredMisconfs int
|
||||||
if opt.PolicyFile != "" {
|
if opt.PolicyFile != "" {
|
||||||
var err error
|
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 {
|
if err != nil {
|
||||||
return xerrors.Errorf("failed to apply the policy: %w", err)
|
return xerrors.Errorf("failed to apply the policy: %w", err)
|
||||||
}
|
}
|
||||||
|
ignoredMisconfs += ignored
|
||||||
}
|
}
|
||||||
sort.Sort(types.BySeverity(filteredVulns))
|
sort.Sort(types.BySeverity(filteredVulns))
|
||||||
|
|
||||||
result.Vulnerabilities = filteredVulns
|
result.Vulnerabilities = filteredVulns
|
||||||
result.Misconfigurations = filteredMisconfs
|
|
||||||
result.MisconfSummary = misconfSummary
|
result.MisconfSummary = misconfSummary
|
||||||
|
if result.MisconfSummary != nil {
|
||||||
|
result.MisconfSummary.Exceptions += ignoredMisconfs
|
||||||
|
}
|
||||||
|
result.Misconfigurations = filteredMisconfs
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -147,6 +153,7 @@ func filterMisconfigurations(result *types.Result, severities []string, includeN
|
|||||||
continue
|
continue
|
||||||
} else if ignoreMisconfs.Match(result.Target, misconf.ID) || ignoreMisconfs.Match(result.Target, misconf.AVDID) {
|
} else if ignoreMisconfs.Match(result.Target, misconf.ID) || ignoreMisconfs.Match(result.Target, misconf.AVDID) {
|
||||||
// Filter misconfigurations by ignore file
|
// Filter misconfigurations by ignore file
|
||||||
|
summary.Exceptions++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,10 +222,10 @@ func summarize(status types.MisconfStatus, summary *types.MisconfSummary) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misconfs []types.DetectedMisconfiguration,
|
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)
|
policy, err := os.ReadFile(policyFile)
|
||||||
if err != nil {
|
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(
|
query, err := rego.New(
|
||||||
@@ -227,7 +234,7 @@ func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misco
|
|||||||
rego.Module("trivy.rego", string(policy)),
|
rego.Module("trivy.rego", string(policy)),
|
||||||
).PrepareForEval(ctx)
|
).PrepareForEval(ctx)
|
||||||
if err != nil {
|
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
|
// Vulnerabilities
|
||||||
@@ -235,7 +242,7 @@ func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misco
|
|||||||
for _, vuln := range vulns {
|
for _, vuln := range vulns {
|
||||||
ignored, err := evaluate(ctx, query, vuln)
|
ignored, err := evaluate(ctx, query, vuln)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, 0, err
|
||||||
}
|
}
|
||||||
if ignored {
|
if ignored {
|
||||||
continue
|
continue
|
||||||
@@ -244,18 +251,20 @@ func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, misco
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Misconfigurations
|
// Misconfigurations
|
||||||
|
var ignoredMisconfs int
|
||||||
var filteredMisconfs []types.DetectedMisconfiguration
|
var filteredMisconfs []types.DetectedMisconfiguration
|
||||||
for _, misconf := range misconfs {
|
for _, misconf := range misconfs {
|
||||||
ignored, err := evaluate(ctx, query, misconf)
|
ignored, err := evaluate(ctx, query, misconf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, 0, err
|
||||||
}
|
}
|
||||||
if ignored {
|
if ignored {
|
||||||
|
ignoredMisconfs++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
filteredMisconfs = append(filteredMisconfs, misconf)
|
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) {
|
func evaluate(ctx context.Context, query rego.PreparedEvalQuery, input interface{}) (bool, error) {
|
||||||
results, err := query.Eval(ctx, rego.EvalInput(input))
|
results, err := query.Eval(ctx, rego.EvalInput(input))
|
||||||
|
|||||||
@@ -383,6 +383,11 @@ func TestFilter(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Target: "Dockerfile",
|
Target: "Dockerfile",
|
||||||
Class: types.ClassConfig,
|
Class: types.ClassConfig,
|
||||||
|
MisconfSummary: &types.MisconfSummary{
|
||||||
|
Successes: 0,
|
||||||
|
Failures: 0,
|
||||||
|
Exceptions: 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Secrets: []ftypes.SecretFinding{
|
Secrets: []ftypes.SecretFinding{
|
||||||
@@ -584,7 +589,7 @@ func TestFilter(t *testing.T) {
|
|||||||
MisconfSummary: &types.MisconfSummary{
|
MisconfSummary: &types.MisconfSummary{
|
||||||
Successes: 0,
|
Successes: 0,
|
||||||
Failures: 1,
|
Failures: 1,
|
||||||
Exceptions: 0,
|
Exceptions: 2,
|
||||||
},
|
},
|
||||||
Misconfigurations: []types.DetectedMisconfiguration{
|
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",
|
name: "happy path with duplicates, one with empty fixed version",
|
||||||
args: args{
|
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