diff --git a/cmd/security-agent/subcommands/runtime/activity_dump.go b/cmd/security-agent/subcommands/runtime/activity_dump.go index 1a1a3908433d80..3d1287ade75fe7 100644 --- a/cmd/security-agent/subcommands/runtime/activity_dump.go +++ b/cmd/security-agent/subcommands/runtime/activity_dump.go @@ -779,7 +779,7 @@ func activityDumpToWorkloadPolicy(_ log.Component, _ config.Component, _ secrets } else { policyName = "workload_policy" } - policy, err := rules.LoadPolicyFromDefinition(policyName, "workload", &policyDef, nil, nil) + policy, err := rules.LoadPolicyFromDefinition(policyName, "workload", rules.InternalPolicyType, &policyDef, nil, nil) if err != nil { return fmt.Errorf("error in generated ruleset's syntax: '%s'", err) diff --git a/pkg/security/probe/selftests/tester.go b/pkg/security/probe/selftests/tester.go index fa30e7367d54c5..3e2e4d01d75d40 100644 --- a/pkg/security/probe/selftests/tester.go +++ b/pkg/security/probe/selftests/tester.go @@ -182,7 +182,7 @@ func (t *SelfTester) LoadPolicies(_ []rules.MacroFilter, _ []rules.RuleFilter) ( policyDef.Rules[i] = selfTest.GetRuleDefinition() } - policy, err := rules.LoadPolicyFromDefinition(policyName, policySource, policyDef, nil, nil) + policy, err := rules.LoadPolicyFromDefinition(policyName, policySource, rules.SelftestPolicy, policyDef, nil, nil) if err != nil { return nil, multierror.Append(nil, err) } diff --git a/pkg/security/rconfig/policies.go b/pkg/security/rconfig/policies.go index 64625f9a7f1fb4..115d2308e67a17 100644 --- a/pkg/security/rconfig/policies.go +++ b/pkg/security/rconfig/policies.go @@ -159,7 +159,7 @@ func (r *RCPolicyProvider) LoadPolicies(macroFilters []rules.MacroFilter, ruleFi r.RLock() defer r.RUnlock() - load := func(id string, cfg []byte) error { + load := func(policyType rules.PolicyType, id string, cfg []byte) error { if r.dumpPolicies { name, err := writePolicy(id, cfg) if err != nil { @@ -170,7 +170,7 @@ func (r *RCPolicyProvider) LoadPolicies(macroFilters []rules.MacroFilter, ruleFi } reader := bytes.NewReader(cfg) - policy, err := rules.LoadPolicy(id, rules.PolicyProviderTypeRC, reader, macroFilters, ruleFilters) + policy, err := rules.LoadPolicy(id, rules.PolicyProviderTypeRC, policyType, reader, macroFilters, ruleFilters) normalize(policy) policies = append(policies, policy) return err @@ -179,7 +179,7 @@ func (r *RCPolicyProvider) LoadPolicies(macroFilters []rules.MacroFilter, ruleFi for _, cfgPath := range slices.Sorted(maps.Keys(r.lastDefaults)) { rawConfig := r.lastDefaults[cfgPath] - if err := load(rawConfig.Metadata.ID, rawConfig.Config); err != nil { + if err := load(rules.DefaultPolicyType, rawConfig.Metadata.ID, rawConfig.Config); err != nil { r.client.UpdateApplyStatus(cfgPath, state.ApplyStatus{State: state.ApplyStateError, Error: err.Error()}) errs = multierror.Append(errs, err) } else { @@ -190,7 +190,7 @@ func (r *RCPolicyProvider) LoadPolicies(macroFilters []rules.MacroFilter, ruleFi for _, cfgPath := range slices.Sorted(maps.Keys(r.lastCustoms)) { rawConfig := r.lastCustoms[cfgPath] - if err := load(rawConfig.Metadata.ID, rawConfig.Config); err != nil { + if err := load(rules.CustomPolicyType, rawConfig.Metadata.ID, rawConfig.Config); err != nil { r.client.UpdateApplyStatus(cfgPath, state.ApplyStatus{State: state.ApplyStateError, Error: err.Error()}) errs = multierror.Append(errs, err) } else { diff --git a/pkg/security/rules/bundled/bundled_policy_provider.go b/pkg/security/rules/bundled/bundled_policy_provider.go index 2901269e52d13d..848f44604db90c 100644 --- a/pkg/security/rules/bundled/bundled_policy_provider.go +++ b/pkg/security/rules/bundled/bundled_policy_provider.go @@ -33,7 +33,7 @@ func (p *PolicyProvider) LoadPolicies([]rules.MacroFilter, []rules.RuleFilter) ( Rules: newBundledPolicyRules(p.cfg), } - policy, err := rules.LoadPolicyFromDefinition("bundled_policy", "bundled", policyDef, nil, nil) + policy, err := rules.LoadPolicyFromDefinition("bundled_policy", "bundled", rules.InternalPolicyType, policyDef, nil, nil) if err != nil { return nil, multierror.Append(nil, err) } diff --git a/pkg/security/secl/rules/model.go b/pkg/security/secl/rules/model.go index 6742021b76bb9c..29a2815f0d33b5 100644 --- a/pkg/security/secl/rules/model.go +++ b/pkg/security/secl/rules/model.go @@ -32,8 +32,6 @@ type OverrideField = string const ( // OverrideAllFields used to override all the fields OverrideAllFields OverrideField = "all" - // OverrideExpressionField used to override the expression - OverrideExpressionField OverrideField = "expression" // OverrideActionFields used to override the actions OverrideActionFields OverrideField = "actions" // OverrideEveryField used to override the every field diff --git a/pkg/security/secl/rules/policy.go b/pkg/security/secl/rules/policy.go index 6a15b2ef64b7a6..0730693a23471a 100644 --- a/pkg/security/secl/rules/policy.go +++ b/pkg/security/secl/rules/policy.go @@ -9,6 +9,7 @@ package rules import ( "fmt" "io" + "reflect" "slices" "github.com/hashicorp/go-multierror" @@ -63,50 +64,110 @@ func applyOverride(rd1, rd2 *PolicyRule) { // keep track of the combine rd1.Def.Combine = rd2.Def.Combine + wasOverridden := false // for backward compatibility, by default only the expression is copied if no options - if len(rd2.Def.OverrideOptions.Fields) == 0 { - rd1.Def.Expression = rd2.Def.Expression - } else if slices.Contains(rd2.Def.OverrideOptions.Fields, OverrideAllFields) { + if slices.Contains(rd2.Def.OverrideOptions.Fields, OverrideAllFields) && rd1.Policy.Type == DefaultPolicyType { + tmpExpression := rd1.Def.Expression *rd1.Def = *rd2.Def + rd1.Def.Expression = tmpExpression + wasOverridden = true } else { - if slices.Contains(rd2.Def.OverrideOptions.Fields, OverrideExpressionField) { - rd1.Def.Expression = rd2.Def.Expression - } if slices.Contains(rd2.Def.OverrideOptions.Fields, OverrideActionFields) { - rd1.Def.Actions = rd2.Def.Actions + var toAdd []*ActionDefinition + for _, action := range rd2.Def.Actions { + duplicated := false + for _, a := range rd1.Def.Actions { + if reflect.DeepEqual(action, a) { + duplicated = true + break + } + } + if !duplicated { + toAdd = append(toAdd, action) + } + } + + if len(toAdd) > 0 { + wasOverridden = true + rd1.Def.Actions = append(rd1.Def.Actions, toAdd...) + } } if slices.Contains(rd2.Def.OverrideOptions.Fields, OverrideEveryField) { rd1.Def.Every = rd2.Def.Every + wasOverridden = true } if slices.Contains(rd2.Def.OverrideOptions.Fields, OverrideTagsField) { - rd1.Def.Tags = rd2.Def.Tags + for k, tag := range rd2.Def.Tags { + rd1.Def.Tags[k] = tag + wasOverridden = true + } } if slices.Contains(rd2.Def.OverrideOptions.Fields, OverrideProductTagsField) { rd1.Def.ProductTags = rd2.Def.ProductTags } } + + if wasOverridden { + rd1.Policy.Name = rd2.Policy.Name + rd1.Policy.Source = rd2.Policy.Source + rd1.Policy.Type = rd2.Policy.Type + + } } // MergeWith merges rule r2 into r func (r *PolicyRule) MergeWith(r2 *PolicyRule) error { switch r2.Def.Combine { case OverridePolicy: - applyOverride(r, r2) + if !r2.Def.Disabled { + applyOverride(r, r2) + } default: if r.Def.Disabled == r2.Def.Disabled { return &ErrRuleLoad{Rule: r2, Err: ErrDefinitionIDConflict} } } - r.Def.Disabled = r2.Def.Disabled + + if r.Def.Disabled { + r.Def.Disabled = r2.Def.Disabled + r.Policy.Name = r2.Policy.Name + r.Policy.Source = r2.Policy.Source + r.Policy.Type = r2.Policy.Type + + } else { + if r.Policy.Type == DefaultPolicyType && r2.Policy.Type == CustomPolicyType { + r.Def.Disabled = r2.Def.Disabled + r.Policy.Name = r2.Policy.Name + r.Policy.Source = r2.Policy.Source + r.Policy.Type = r2.Policy.Type + } + } + r.ModifiedBy = append(r.ModifiedBy, r2) + return nil } +// PolicyType represents the type of a policy +type PolicyType string + +const ( + // DefaultPolicyType is the default policy type + DefaultPolicyType PolicyType = "default" + // CustomPolicyType is the custom policy type + CustomPolicyType PolicyType = "custom" + // InternalPolicyType is the policy for internal use (bundled_policy_provider) + InternalPolicyType PolicyType = "internal" + // SelftestPolicy is the policy for self tests + SelftestPolicy PolicyType = "selftest" +) + // Policy represents a policy which is composed of a list of rules, macros and on-demand hook points type Policy struct { Def *PolicyDef Name string Source string + Type PolicyType IsInternal bool // multiple macros can have the same ID but different filters (e.g. agent version) macros map[MacroID][]*PolicyMacro @@ -240,11 +301,12 @@ RULES: } // LoadPolicyFromDefinition load a policy from a definition -func LoadPolicyFromDefinition(name string, source string, def *PolicyDef, macroFilters []MacroFilter, ruleFilters []RuleFilter) (*Policy, error) { +func LoadPolicyFromDefinition(name string, source string, policyType PolicyType, def *PolicyDef, macroFilters []MacroFilter, ruleFilters []RuleFilter) (*Policy, error) { p := &Policy{ Def: def, Name: name, Source: source, + Type: policyType, macros: make(map[MacroID][]*PolicyMacro, len(def.Macros)), rules: make(map[RuleID][]*PolicyRule, len(def.Rules)), } @@ -253,12 +315,12 @@ func LoadPolicyFromDefinition(name string, source string, def *PolicyDef, macroF } // LoadPolicy load a policy -func LoadPolicy(name string, source string, reader io.Reader, macroFilters []MacroFilter, ruleFilters []RuleFilter) (*Policy, error) { +func LoadPolicy(name string, source string, policyType PolicyType, reader io.Reader, macroFilters []MacroFilter, ruleFilters []RuleFilter) (*Policy, error) { def := PolicyDef{} decoder := yaml.NewDecoder(reader) if err := decoder.Decode(&def); err != nil { return nil, &ErrPolicyLoad{Name: name, Err: err} } - return LoadPolicyFromDefinition(name, source, &def, macroFilters, ruleFilters) + return LoadPolicyFromDefinition(name, source, policyType, &def, macroFilters, ruleFilters) } diff --git a/pkg/security/secl/rules/policy_dir.go b/pkg/security/secl/rules/policy_dir.go index 3749fcdbb181ed..390e24b290f619 100644 --- a/pkg/security/secl/rules/policy_dir.go +++ b/pkg/security/secl/rules/policy_dir.go @@ -40,8 +40,14 @@ func (p *PoliciesDirProvider) loadPolicy(filename string, macroFilters []MacroFi defer f.Close() name := filepath.Base(filename) + var policyType PolicyType + if name == DefaultPolicyName { + policyType = DefaultPolicyType + } else { + policyType = CustomPolicyType + } - return LoadPolicy(name, PolicyProviderTypeDir, f, macroFilters, ruleFilters) + return LoadPolicy(name, PolicyProviderTypeDir, policyType, f, macroFilters, ruleFilters) } func (p *PoliciesDirProvider) getPolicyFiles() ([]string, error) { diff --git a/pkg/security/secl/rules/policy_loader_test.go b/pkg/security/secl/rules/policy_loader_test.go index 4c78b0bf84b0ff..64956a05f4b8b9 100644 --- a/pkg/security/secl/rules/policy_loader_test.go +++ b/pkg/security/secl/rules/policy_loader_test.go @@ -14,6 +14,9 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/go-multierror" "github.com/stretchr/testify/assert" + + "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" + "github.com/DataDog/datadog-agent/pkg/security/secl/model" ) // compare all Policy fields but the `Def` field @@ -45,8 +48,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { return testPoliciesToPolicies([]*testPolicyDef{ { - name: "myLocal.policy", - source: PolicyProviderTypeDir, + name: "myLocal.policy", + source: PolicyProviderTypeDir, + policyType: CustomPolicyType, def: PolicyDef{ Rules: []*RuleDefinition{ { @@ -61,8 +65,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { }, }, { - name: DefaultPolicyName, - source: PolicyProviderTypeDir, + name: DefaultPolicyName, + source: PolicyProviderTypeDir, + policyType: DefaultPolicyType, def: PolicyDef{ Rules: []*RuleDefinition{ { @@ -83,8 +88,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { return testPoliciesToPolicies([]*testPolicyDef{ { - name: "myRC.policy", - source: PolicyProviderTypeRC, + name: "myRC.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, def: PolicyDef{ Rules: []*RuleDefinition{ { @@ -99,8 +105,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { }, }, { - name: DefaultPolicyName, - source: PolicyProviderTypeRC, + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, def: PolicyDef{ Rules: []*RuleDefinition{ { @@ -124,6 +131,7 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { { Name: DefaultPolicyName, Source: PolicyProviderTypeRC, + Type: DefaultPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{ "foo": { @@ -149,6 +157,7 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { { Name: "myRC.policy", Source: PolicyProviderTypeRC, + Type: CustomPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{ "foo": { @@ -174,6 +183,7 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { { Name: "myLocal.policy", Source: PolicyProviderTypeDir, + Type: CustomPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{ "foo": { @@ -222,8 +232,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { return testPoliciesToPolicies([]*testPolicyDef{ { - name: "myLocal.policy", - source: PolicyProviderTypeDir, + name: "myLocal.policy", + source: PolicyProviderTypeDir, + policyType: CustomPolicyType, def: PolicyDef{ Rules: []*RuleDefinition{ { @@ -244,8 +255,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { return testPoliciesToPolicies([]*testPolicyDef{ { - name: "myRC.policy", - source: PolicyProviderTypeRC, + name: "myRC.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, def: PolicyDef{ Rules: []*RuleDefinition{ { @@ -269,6 +281,7 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { { Name: "myRC.policy", Source: PolicyProviderTypeRC, + Type: CustomPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{ "foo": { @@ -294,6 +307,7 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { { Name: "myLocal.policy", Source: PolicyProviderTypeDir, + Type: CustomPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{ "foo": { @@ -340,8 +354,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { return testPoliciesToPolicies([]*testPolicyDef{ { - name: "myLocal.policy", - source: PolicyProviderTypeDir, + name: "myLocal.policy", + source: PolicyProviderTypeDir, + policyType: CustomPolicyType, def: PolicyDef{ Version: "", Rules: []*RuleDefinition{ @@ -374,6 +389,7 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { { Name: "myLocal.policy", Source: PolicyProviderTypeDir, + Type: CustomPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{ "foo": { @@ -422,8 +438,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { return testPoliciesToPolicies([]*testPolicyDef{ { - name: "myLocal.policy", - source: PolicyProviderTypeDir, + name: "myLocal.policy", + source: PolicyProviderTypeDir, + policyType: CustomPolicyType, def: PolicyDef{ Version: "", Rules: []*RuleDefinition{ @@ -456,6 +473,7 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { { Name: "myLocal.policy", Source: PolicyProviderTypeDir, + Type: CustomPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{ "foo": { @@ -502,8 +520,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { return testPoliciesToPolicies([]*testPolicyDef{ { - name: "myLocal.policy", - source: PolicyProviderTypeDir, + name: "myLocal.policy", + source: PolicyProviderTypeDir, + policyType: CustomPolicyType, def: PolicyDef{ Version: "", Rules: []*RuleDefinition{ @@ -525,8 +544,9 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { return testPoliciesToPolicies([]*testPolicyDef{ { - name: "myRC.policy", - source: PolicyProviderTypeRC, + name: "myRC.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, def: PolicyDef{ Version: "", Rules: nil, @@ -542,12 +562,14 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { { Name: "myRC.policy", Source: PolicyProviderTypeRC, + Type: CustomPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{}, }, { Name: "myLocal.policy", Source: PolicyProviderTypeDir, + Type: CustomPolicyType, macros: map[string][]*PolicyMacro{}, rules: map[string][]*PolicyRule{ "foo": { @@ -585,117 +607,1312 @@ func TestPolicyLoader_LoadPolicies(t *testing.T) { }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p := &PolicyLoader{ - Providers: tt.fields.Providers, - } - loadedPolicies, errs := p.LoadPolicies(tt.args.opts) - - tt.want(t, loadedPolicies) - tt.wantErr(t, errs) - }) - } -} - -// Utils - -func numAndLastIdxOfDefaultPolicies(policies []*Policy) (int, int) { - var defaultPolicyCount int - var lastSeenDefaultPolicyIdx int - for idx, policy := range policies { - if policy.Name == DefaultPolicyName { - defaultPolicyCount++ - lastSeenDefaultPolicyIdx = idx - } - } - - return defaultPolicyCount, lastSeenDefaultPolicyIdx -} - -type dummyDirProvider struct { - dummyLoadPoliciesFunc func() ([]*Policy, *multierror.Error) -} - -func (d dummyDirProvider) LoadPolicies(_ []MacroFilter, _ []RuleFilter) ([]*Policy, *multierror.Error) { - return d.dummyLoadPoliciesFunc() -} - -func (dummyDirProvider) SetOnNewPoliciesReadyCb(_ func()) {} - -func (dummyDirProvider) Start() {} - -func (dummyDirProvider) Close() error { - return nil -} - -func (dummyDirProvider) Type() string { - return PolicyProviderTypeDir -} - -type dummyRCProvider struct { - dummyLoadPoliciesFunc func() ([]*Policy, *multierror.Error) -} - -func (d dummyRCProvider) LoadPolicies(_ []MacroFilter, _ []RuleFilter) ([]*Policy, *multierror.Error) { - return d.dummyLoadPoliciesFunc() -} - -func (dummyRCProvider) SetOnNewPoliciesReadyCb(_ func()) {} - -func (dummyRCProvider) Start() {} - -func (dummyRCProvider) Close() error { - return nil -} - -func (dummyRCProvider) Type() string { - return PolicyProviderTypeRC -} - -type testPolicyDef struct { - def PolicyDef - name string - source string -} - -func testPolicyToPolicy(testPolicy *testPolicyDef) (*Policy, *multierror.Error) { - policy, err := LoadPolicyFromDefinition(testPolicy.name, testPolicy.source, &testPolicy.def, nil, nil) - if err != nil { - return nil, multierror.Append(nil, err) - } - return policy, nil -} - -func testPoliciesToPolicies(testPolicies []*testPolicyDef) ([]*Policy, *multierror.Error) { - var policies []*Policy - var errs *multierror.Error - - for _, testPolicy := range testPolicies { - p, err := testPolicyToPolicy(testPolicy) - if err != nil { - errs = multierror.Append(errs, err) - continue - } - - policies = append(policies, p) - } - - return policies, errs -} - -func fixupRulesPolicy(policy *Policy) *Policy { - for _, rules := range policy.rules { - for _, rule := range rules { - rule.Policy = policy - } - } - return policy -} + overridesTestCases := []struct { + name string + fields fields + args args + want func(t assert.TestingT, got map[eval.RuleID]*Rule, msgs ...interface{}) bool + }{ + { + name: "P0.DR enabled, P1.DR enabled => P0.DR enabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", + }, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + Policy: &Policy{ + Name: DefaultPolicyName, + Source: PolicyProviderTypeRC, + Type: DefaultPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR enabled, P1.DR disabled => P0.DR enabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Disabled: true}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + Policy: &Policy{ + Name: DefaultPolicyName, + Source: PolicyProviderTypeRC, + Type: DefaultPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) -func fixupPoliciesRulesPolicy(policies []*Policy) []*Policy { - for _, policy := range policies { - fixupRulesPolicy(policy) - } - return policies + }, + }, + { + name: "P0.DR disabled, P1.DR enabled => P1.DR enabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", Disabled: true}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", Disabled: false}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + Policy: &Policy{ + Name: "P1.policy", + Source: PolicyProviderTypeRC, + Type: DefaultPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR disabled, P1.DR disabled => P0.DR disabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Disabled: true}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Disabled: true}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{} + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR enabled, P1.CR disabled => P1.CR disabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", Disabled: true}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{} + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR disabled, P1.CR disabled => CR disabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Disabled: true}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Disabled: true}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{} + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR disabled, P1.CR enabled => P1.CR enabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\"", Disabled: true}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\"", Disabled: false}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\""}, + Policy: &Policy{ + Name: "P1.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR enabled, P1.CR enabled => P0.CR enabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + Policy: &Policy{ + Name: DefaultPolicyName, + Source: PolicyProviderTypeRC, + Type: DefaultPolicyType, + }, + Accepted: true, + }, + }, + } + + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.CR enabled, P1.CR enabled => P0.CR enabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P0.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\""}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\""}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\""}, + Policy: &Policy{ + Name: "P0.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.CR disabled, P1.CR enabled => P1.CR enabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P0.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\"", Disabled: true}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\"", Disabled: false}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\""}, + Policy: &Policy{ + Name: "P1.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.CR enabled, P1.CR disabled => P0.CR enabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P0.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\""}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\"", Disabled: true}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\""}, + Policy: &Policy{ + Name: "P0.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.CR disabled, P1.CR disabled => P0.CR disabled", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P0.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Disabled: true}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Disabled: true}, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{} + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR no action , P1.DR 1 action => P1.DR + 1 action", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + {ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\""}, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", + Combine: OverridePolicy, + OverrideOptions: OverrideOptions{ + Fields: []OverrideField{OverrideActionFields}, + }, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR2", + }, + }, + }, + }, + }, + }, + }}) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ + ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", + Combine: OverridePolicy, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR2", + }, + }, + }, + }, + + Policy: &Policy{ + Name: "P1.policy", + Source: PolicyProviderTypeRC, + Type: DefaultPolicyType, + }, + Accepted: true, + }, + }, + } + + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR 1 action, P1.CR 1 action => P1.CR + 2 actions", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + }, + }, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Combine: OverridePolicy, + OverrideOptions: OverrideOptions{ + Fields: []OverrideField{OverrideActionFields}, + }, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR2", + }, + }, + }, + }, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ + ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", + Combine: OverridePolicy, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + { + Kill: &KillDefinition{ + Signal: "SIGUSR2", + }, + }, + }, + }, + Policy: &Policy{ + Name: "P1.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR 1 action, P1.CR no action => P1.DR 1 action", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + }, + }, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Combine: OverridePolicy, + OverrideOptions: OverrideOptions{ + Fields: []OverrideField{OverrideActionFields}, + }, + }, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ + ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", + Combine: OverridePolicy, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + }, + }, + Policy: &Policy{ + Name: "P1.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.CR 1 action, P1.CR no action => P1.CR + 1 action", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P0.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/custom/foo\"", + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + }, + }, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/custom/foo\"", + Combine: OverridePolicy, + OverrideOptions: OverrideOptions{ + Fields: []OverrideField{OverrideActionFields}, + }, + }, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ + ID: "rule_1", Expression: "exec.file.path == \"/etc/custom/foo\"", + Combine: OverridePolicy, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + }, + }, + Policy: &Policy{ + Name: "P0.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR enabled, P1.CR 1 actionA, enabled, P2 1 actionB, disabled => P1.CR + 1 actionA", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + }, + }, + }, + }, + }) + }, + }, + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + return testPoliciesToPolicies([]*testPolicyDef{ + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Disabled: false, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + }, + Combine: OverridePolicy, + OverrideOptions: OverrideOptions{ + Fields: []OverrideField{OverrideActionFields}, + }, + }, + }, + }, + }, + { + name: "P2.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Disabled: true, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR2", + }, + }, + }, + Combine: OverridePolicy, + OverrideOptions: OverrideOptions{ + Fields: []OverrideField{OverrideActionFields}, + }, + }, + }, + }, + }, + }) + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ + ID: "rule_1", Expression: "exec.file.path == \"/etc/default/foo\"", + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + }, + Combine: OverridePolicy, + }, + Policy: &Policy{ + Name: "P1.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + { + name: "P0.DR enabled 1 Action A, P1.CR same Action A + Action B => P1.CR + 1 Action A (not duplicated) + Action B", + fields: fields{ + Providers: []PolicyProvider{ + dummyRCProvider{ + dummyLoadPoliciesFunc: func() ([]*Policy, *multierror.Error) { + policies, _ := testPoliciesToPolicies([]*testPolicyDef{ + { + name: DefaultPolicyName, + source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + }, + }, + }, + }, + }, + { + name: "P1.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, + def: PolicyDef{ + Rules: []*RuleDefinition{ + { + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Combine: OverridePolicy, + OverrideOptions: OverrideOptions{ + Fields: []OverrideField{OverrideActionFields}, + }, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, { + Kill: &KillDefinition{ + Signal: "SIGUSR2", + }, + }, + }, + }, + }, + }, + }, + }) + return policies, nil + }, + }, + }, + }, + want: func(t assert.TestingT, got map[eval.RuleID]*Rule, _ ...interface{}) bool { + expected := map[eval.RuleID]*Rule{ + "rule_1": { + PolicyRule: &PolicyRule{ + Def: &RuleDefinition{ + ID: "rule_1", + Expression: "exec.file.path == \"/etc/default/foo\"", + Combine: OverridePolicy, + Actions: []*ActionDefinition{ + { + Kill: &KillDefinition{ + Signal: "SIGUSR1", + }, + }, + { + Kill: &KillDefinition{ + Signal: "SIGUSR2", + }, + }, + }, + }, + Policy: &Policy{ + Name: "P1.policy", + Source: PolicyProviderTypeRC, + Type: CustomPolicyType, + }, + Accepted: true, + }, + }, + } + return checkOverrideResult(t, expected, got) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &PolicyLoader{ + Providers: tt.fields.Providers, + } + loadedPolicies, errs := p.LoadPolicies(tt.args.opts) + + tt.want(t, loadedPolicies) + tt.wantErr(t, errs) + }) + } + + for _, tt := range overridesTestCases { + t.Run(tt.name, func(t *testing.T) { + ruleOpts, evalOpts := NewBothOpts(map[eval.EventType]bool{"*": true}) + rs := NewRuleSet(&model.Model{}, func() eval.Event { return model.NewFakeEvent() }, ruleOpts, evalOpts) + p := &PolicyLoader{ + Providers: tt.fields.Providers, + } + rs.LoadPolicies(p, tt.args.opts) + tt.want(t, rs.rules) + }) + } +} + +// Utils +func numAndLastIdxOfDefaultPolicies(policies []*Policy) (int, int) { + var defaultPolicyCount int + var lastSeenDefaultPolicyIdx int + for idx, policy := range policies { + if policy.Name == DefaultPolicyName { + defaultPolicyCount++ + lastSeenDefaultPolicyIdx = idx + } + } + + return defaultPolicyCount, lastSeenDefaultPolicyIdx +} + +type dummyDirProvider struct { + dummyLoadPoliciesFunc func() ([]*Policy, *multierror.Error) +} + +func (d dummyDirProvider) LoadPolicies(_ []MacroFilter, _ []RuleFilter) ([]*Policy, *multierror.Error) { + return d.dummyLoadPoliciesFunc() +} + +func (dummyDirProvider) SetOnNewPoliciesReadyCb(_ func()) {} + +func (dummyDirProvider) Start() {} + +func (dummyDirProvider) Close() error { + return nil +} + +func (dummyDirProvider) Type() string { + return PolicyProviderTypeDir +} + +type dummyRCProvider struct { + dummyLoadPoliciesFunc func() ([]*Policy, *multierror.Error) +} + +func (d dummyRCProvider) LoadPolicies(_ []MacroFilter, _ []RuleFilter) ([]*Policy, *multierror.Error) { + return d.dummyLoadPoliciesFunc() +} + +func (dummyRCProvider) SetOnNewPoliciesReadyCb(_ func()) {} + +func (dummyRCProvider) Start() {} + +func (dummyRCProvider) Close() error { + return nil +} + +func (dummyRCProvider) Type() string { + return PolicyProviderTypeRC +} + +type testPolicyDef struct { + def PolicyDef + name string + source string + policyType PolicyType +} + +func testPolicyToPolicy(testPolicy *testPolicyDef) (*Policy, *multierror.Error) { + policy, err := LoadPolicyFromDefinition(testPolicy.name, testPolicy.source, testPolicy.policyType, &testPolicy.def, nil, nil) + if err != nil { + return nil, multierror.Append(nil, err) + } + return policy, nil +} + +func testPoliciesToPolicies(testPolicies []*testPolicyDef) ([]*Policy, *multierror.Error) { + var policies []*Policy + var errs *multierror.Error + + for _, testPolicy := range testPolicies { + p, err := testPolicyToPolicy(testPolicy) + if err != nil { + errs = multierror.Append(errs, err) + continue + } + + policies = append(policies, p) + } + + return policies, errs +} + +func fixupRulesPolicy(policy *Policy) *Policy { + for _, rules := range policy.rules { + for _, rule := range rules { + rule.Policy = policy + } + } + return policy +} + +func fixupPoliciesRulesPolicy(policies []*Policy) []*Policy { + for _, policy := range policies { + fixupRulesPolicy(policy) + } + return policies +} + +func checkOverrideResult(t assert.TestingT, expected map[eval.RuleID]*Rule, got map[eval.RuleID]*Rule) bool { + if len(expected) == 0 { + return assert.Equal(t, len(expected), len(got)) + } + + // From here, we know that we expect exacly one element + var ruleID eval.RuleID + for r := range expected { + ruleID = r + break + } + return (assert.Equal(t, 1, len(got)) && + assert.Equal(t, expected[ruleID].PolicyRule.Def, got[ruleID].PolicyRule.Def) && + assert.Equal(t, expected[ruleID].PolicyRule.Policy.Name, got[ruleID].Policy.Name) && + assert.Equal(t, expected[ruleID].PolicyRule.Policy.Source, got[ruleID].Policy.Source) && + assert.Equal(t, expected[ruleID].PolicyRule.Policy.Type, got[ruleID].Policy.Type) && + assert.Equal(t, expected[ruleID].PolicyRule.Accepted, got[ruleID].PolicyRule.Accepted)) } diff --git a/pkg/security/secl/rules/policy_test.go b/pkg/security/secl/rules/policy_test.go index 53febcff74be46..8dfbb82c46310e 100644 --- a/pkg/security/secl/rules/policy_test.go +++ b/pkg/security/secl/rules/policy_test.go @@ -180,8 +180,8 @@ func TestRuleMerge(t *testing.T) { t.Run("enabled-disabled", func(t *testing.T) { rule := rs.GetRules()["test_rule_foo"] - if rule != nil { - t.Fatal("expected test_rule_foo to not be loaded") + if rule == nil { + t.Fatal("expected test_rule_foo to be loaded now") } }) @@ -1039,6 +1039,7 @@ func TestActionSetVariableInvalid(t *testing.T) { func TestLoadPolicy(t *testing.T) { type args struct { name string + policyType PolicyType source string fileContent string macroFilters []MacroFilter @@ -1055,6 +1056,7 @@ func TestLoadPolicy(t *testing.T) { args: args{ name: "myLocal.policy", source: PolicyProviderTypeRC, + policyType: DefaultPolicyType, fileContent: ``, macroFilters: nil, ruleFilters: nil, @@ -1067,8 +1069,9 @@ func TestLoadPolicy(t *testing.T) { { name: "empty yaml file with new line char", args: args{ - name: "myLocal.policy", - source: PolicyProviderTypeRC, + name: "myLocal.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, fileContent: ` `, macroFilters: nil, @@ -1082,8 +1085,9 @@ func TestLoadPolicy(t *testing.T) { { name: "no rules in yaml file", args: args{ - name: "myLocal.policy", - source: PolicyProviderTypeRC, + name: "myLocal.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, fileContent: ` rules: `, @@ -1093,6 +1097,7 @@ rules: want: &Policy{ Name: "myLocal.policy", Source: PolicyProviderTypeRC, + Type: CustomPolicyType, rules: map[string][]*PolicyRule{}, macros: map[string][]*PolicyMacro{}, }, @@ -1101,8 +1106,9 @@ rules: { name: "broken yaml file", args: args{ - name: "myLocal.policy", - source: PolicyProviderTypeRC, + name: "myLocal.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, fileContent: ` broken `, @@ -1117,8 +1123,9 @@ broken { name: "disabled tag", args: args{ - name: "myLocal.policy", - source: PolicyProviderTypeRC, + name: "myLocal.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, fileContent: `rules: - id: rule_test disabled: true @@ -1129,6 +1136,7 @@ broken want: fixupRulesPolicy(&Policy{ Name: "myLocal.policy", Source: PolicyProviderTypeRC, + Type: CustomPolicyType, rules: map[string][]*PolicyRule{ "rule_test": { { @@ -1148,8 +1156,9 @@ broken { name: "combine:override tag", args: args{ - name: "myLocal.policy", - source: PolicyProviderTypeRC, + name: "myLocal.policy", + source: PolicyProviderTypeRC, + policyType: CustomPolicyType, fileContent: `rules: - id: rule_test expression: open.file.path == "/etc/gshadow" @@ -1161,6 +1170,7 @@ broken want: fixupRulesPolicy(&Policy{ Name: "myLocal.policy", Source: PolicyProviderTypeRC, + Type: CustomPolicyType, rules: map[string][]*PolicyRule{ "rule_test": { { @@ -1182,7 +1192,7 @@ broken t.Run(tt.name, func(t *testing.T) { r := strings.NewReader(tt.args.fileContent) - got, err := LoadPolicy(tt.args.name, tt.args.source, r, tt.args.macroFilters, tt.args.ruleFilters) + got, err := LoadPolicy(tt.args.name, tt.args.source, tt.args.policyType, r, tt.args.macroFilters, tt.args.ruleFilters) if !tt.wantErr(t, err, fmt.Sprintf("LoadPolicy(%v, %v, %v, %v, %v)", tt.args.name, tt.args.source, r, tt.args.macroFilters, tt.args.ruleFilters)) { return