Skip to content

Commit

Permalink
[CWS] fix ttl field time.Duration unmarshaller (#31742)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulcacheux authored Dec 4, 2024
1 parent 5e0c347 commit 6f3217d
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 41 deletions.
4 changes: 2 additions & 2 deletions pkg/security/events/rate_limiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ func (rl *RateLimiter) Apply(ruleSet *rules.RuleSet, customRuleIDs []eval.RuleID
for id, rule := range ruleSet.GetRules() {
every, burst := defaultEvery, defaultBurst

if rule.Def.Every != 0 {
every, burst = rule.Def.Every, 1
if rule.Def.Every.Duration != 0 {
every, burst = rule.Def.Every.Duration, 1
}

if len(rule.Def.RateLimiterToken) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/security/generators/schemas/policy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func main() {
ExpandedStruct: true,
Mapper: func(t reflect.Type) *jsonschema.Schema {
switch t {
case reflect.TypeOf(time.Duration(0)):
case reflect.TypeOf(rules.HumanReadableDuration{}), reflect.TypeOf(time.Duration(0)):
return &jsonschema.Schema{
OneOf: []*jsonschema.Schema{
{
Expand Down
4 changes: 2 additions & 2 deletions pkg/security/probe/process_killer.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ func (p *ProcessKiller) getDisarmerParams(kill *rules.KillDefinition) (*disarmer
if kill.Disarmer != nil && kill.Disarmer.Container != nil && kill.Disarmer.Container.MaxAllowed > 0 {
containerParams.enabled = true
containerParams.capacity = uint64(kill.Disarmer.Container.MaxAllowed)
containerParams.period = kill.Disarmer.Container.Period
containerParams.period = kill.Disarmer.Container.Period.Duration
} else if p.cfg.RuntimeSecurity.EnforcementDisarmerContainerEnabled {
containerParams.enabled = true
containerParams.capacity = uint64(p.cfg.RuntimeSecurity.EnforcementDisarmerContainerMaxAllowed)
Expand All @@ -435,7 +435,7 @@ func (p *ProcessKiller) getDisarmerParams(kill *rules.KillDefinition) (*disarmer
if kill.Disarmer != nil && kill.Disarmer.Executable != nil && kill.Disarmer.Executable.MaxAllowed > 0 {
executableParams.enabled = true
executableParams.capacity = uint64(kill.Disarmer.Executable.MaxAllowed)
executableParams.period = kill.Disarmer.Executable.Period
executableParams.period = kill.Disarmer.Executable.Period.Duration
} else if p.cfg.RuntimeSecurity.EnforcementDisarmerExecutableEnabled {
executableParams.enabled = true
executableParams.capacity = uint64(p.cfg.RuntimeSecurity.EnforcementDisarmerExecutableMaxAllowed)
Expand Down
6 changes: 4 additions & 2 deletions pkg/security/probe/selftests/ebpfless.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ func (o *EBPFLessSelfTest) GetRuleDefinition() *rules.RuleDefinition {
return &rules.RuleDefinition{
ID: o.ruleID,
Expression: `exec.file.path != "" && process.parent.pid == 0 && process.ppid == 0`,
Every: time.Duration(math.MaxInt64),
Silent: true,
Every: rules.HumanReadableDuration{
Duration: time.Duration(math.MaxInt64),
},
Silent: true,
}
}

Expand Down
87 changes: 63 additions & 24 deletions pkg/security/secl/rules/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
package rules

import (
"errors"
"time"

"gopkg.in/yaml.v3"
)

// MacroID represents the ID of a macro
Expand Down Expand Up @@ -60,21 +63,21 @@ type RuleID = string

// RuleDefinition holds the definition of a rule
type RuleDefinition struct {
ID RuleID `yaml:"id,omitempty" json:"id"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
Expression string `yaml:"expression" json:"expression,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Tags map[string]string `yaml:"tags,omitempty" json:"tags,omitempty"`
AgentVersionConstraint string `yaml:"agent_version,omitempty" json:"agent_version,omitempty"`
Filters []string `yaml:"filters,omitempty" json:"filters,omitempty"`
Disabled bool `yaml:"disabled,omitempty" json:"disabled,omitempty"`
Combine CombinePolicy `yaml:"combine,omitempty" json:"combine,omitempty" jsonschema:"enum=override"`
OverrideOptions OverrideOptions `yaml:"override_options,omitempty" json:"override_options,omitempty"`
Actions []*ActionDefinition `yaml:"actions,omitempty" json:"actions,omitempty"`
Every time.Duration `yaml:"every,omitempty" json:"every,omitempty"`
RateLimiterToken []string `yaml:"limiter_token,omitempty" json:"limiter_token,omitempty"`
Silent bool `yaml:"silent,omitempty" json:"silent,omitempty"`
GroupID string `yaml:"group_id,omitempty" json:"group_id,omitempty"`
ID RuleID `yaml:"id,omitempty" json:"id"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
Expression string `yaml:"expression" json:"expression,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Tags map[string]string `yaml:"tags,omitempty" json:"tags,omitempty"`
AgentVersionConstraint string `yaml:"agent_version,omitempty" json:"agent_version,omitempty"`
Filters []string `yaml:"filters,omitempty" json:"filters,omitempty"`
Disabled bool `yaml:"disabled,omitempty" json:"disabled,omitempty"`
Combine CombinePolicy `yaml:"combine,omitempty" json:"combine,omitempty" jsonschema:"enum=override"`
OverrideOptions OverrideOptions `yaml:"override_options,omitempty" json:"override_options,omitempty"`
Actions []*ActionDefinition `yaml:"actions,omitempty" json:"actions,omitempty"`
Every HumanReadableDuration `yaml:"every,omitempty" json:"every,omitempty"`
RateLimiterToken []string `yaml:"limiter_token,omitempty" json:"limiter_token,omitempty"`
Silent bool `yaml:"silent,omitempty" json:"silent,omitempty"`
GroupID string `yaml:"group_id,omitempty" json:"group_id,omitempty"`
}

// GetTag returns the tag value associated with a tag key
Expand Down Expand Up @@ -130,19 +133,19 @@ type Scope string

// SetDefinition describes the 'set' section of a rule action
type SetDefinition struct {
Name string `yaml:"name" json:"name"`
Value interface{} `yaml:"value" json:"value,omitempty" jsonschema:"oneof_required=SetWithValue,oneof_type=string;integer;boolean;array"`
Field string `yaml:"field" json:"field,omitempty" jsonschema:"oneof_required=SetWithField"`
Append bool `yaml:"append" json:"append,omitempty"`
Scope Scope `yaml:"scope" json:"scope,omitempty" jsonschema:"enum=process,enum=container"`
Size int `yaml:"size" json:"size,omitempty"`
TTL time.Duration `yaml:"ttl" json:"ttl,omitempty"`
Name string `yaml:"name" json:"name"`
Value interface{} `yaml:"value" json:"value,omitempty" jsonschema:"oneof_required=SetWithValue,oneof_type=string;integer;boolean;array"`
Field string `yaml:"field" json:"field,omitempty" jsonschema:"oneof_required=SetWithField"`
Append bool `yaml:"append" json:"append,omitempty"`
Scope Scope `yaml:"scope" json:"scope,omitempty" jsonschema:"enum=process,enum=container"`
Size int `yaml:"size" json:"size,omitempty"`
TTL HumanReadableDuration `yaml:"ttl" json:"ttl,omitempty"`
}

// KillDisarmerParamsDefinition describes the parameters of a kill action disarmer
type KillDisarmerParamsDefinition struct {
MaxAllowed int `yaml:"max_allowed" json:"max_allowed,omitempty" jsonschema:"description=The maximum number of allowed kill actions within the period,example=5"`
Period time.Duration `yaml:"period" json:"period,omitempty" jsonschema:"description=The period of time during which the maximum number of allowed kill actions is calculated,example=1m"`
MaxAllowed int `yaml:"max_allowed" json:"max_allowed,omitempty" jsonschema:"description=The maximum number of allowed kill actions within the period,example=5"`
Period HumanReadableDuration `yaml:"period" json:"period,omitempty" jsonschema:"description=The period of time during which the maximum number of allowed kill actions is calculated,example=1m"`
}

// KillDisarmerDefinition describes the 'disarmer' section of a kill action
Expand Down Expand Up @@ -189,3 +192,39 @@ type PolicyDef struct {
Rules []*RuleDefinition `yaml:"rules" json:"rules"`
OnDemandHookPoints []OnDemandHookPoint `yaml:"hooks,omitempty" json:"hooks,omitempty"`
}

// HumanReadableDuration represents a duration that can unmarshalled from YAML from a human readable format (like `10m`)
// or from a regular integer
type HumanReadableDuration struct {
time.Duration
}

// MarshalYAML marshals a duration to a human readable format
func (d HumanReadableDuration) MarshalYAML() (interface{}, error) {
return d.String(), nil
}

// UnmarshalYAML unmarshals a duration from a human readable format or from an integer
func (d *HumanReadableDuration) UnmarshalYAML(n *yaml.Node) error {
var v interface{}
if err := n.Decode(&v); err != nil {
return err
}
switch value := v.(type) {
case float64:
d.Duration = time.Duration(value)
return nil
case string:
var err error
d.Duration, err = time.ParseDuration(value)
if err != nil {
return err
}
return nil
default:
return errors.New("invalid duration")
}
}

var _ yaml.Marshaler = (*HumanReadableDuration)(nil)
var _ yaml.Unmarshaler = (*HumanReadableDuration)(nil)
4 changes: 3 additions & 1 deletion pkg/security/secl/rules/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ func TestActionSetVariableTTL(t *testing.T) {
Name: "var1",
Append: true,
Value: []string{"foo"},
TTL: 1 * time.Second,
TTL: HumanReadableDuration{
Duration: 1 * time.Second,
},
},
}},
}},
Expand Down
2 changes: 1 addition & 1 deletion pkg/security/secl/rules/ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func (rs *RuleSet) PopulateFieldsWithRuleActionsData(policyRules []*PolicyRule,
variableProvider = &rs.globalVariables
}

opts := eval.VariableOpts{TTL: actionDef.Set.TTL, Size: actionDef.Set.Size}
opts := eval.VariableOpts{TTL: actionDef.Set.TTL.Duration, Size: actionDef.Set.Size}

variable, err := variableProvider.GetVariable(actionDef.Set.Name, variableValue, opts)
if err != nil {
Expand Down
16 changes: 12 additions & 4 deletions pkg/security/tests/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -619,11 +619,15 @@ func TestActionKillDisarmFromRule(t *testing.T) {
Disarmer: &rules.KillDisarmerDefinition{
Executable: &rules.KillDisarmerParamsDefinition{
MaxAllowed: 1,
Period: enforcementDisarmerExecutablePeriod,
Period: rules.HumanReadableDuration{
Duration: enforcementDisarmerExecutablePeriod,
},
},
Container: &rules.KillDisarmerParamsDefinition{
MaxAllowed: 1,
Period: enforcementDisarmerContainerPeriod,
Period: rules.HumanReadableDuration{
Duration: enforcementDisarmerContainerPeriod,
},
},
},
},
Expand All @@ -640,11 +644,15 @@ func TestActionKillDisarmFromRule(t *testing.T) {
Disarmer: &rules.KillDisarmerDefinition{
Executable: &rules.KillDisarmerParamsDefinition{
MaxAllowed: 1,
Period: enforcementDisarmerExecutablePeriod,
Period: rules.HumanReadableDuration{
Duration: enforcementDisarmerExecutablePeriod,
},
},
Container: &rules.KillDisarmerParamsDefinition{
MaxAllowed: 1,
Period: enforcementDisarmerContainerPeriod,
Period: rules.HumanReadableDuration{
Duration: enforcementDisarmerContainerPeriod,
},
},
},
},
Expand Down
12 changes: 8 additions & 4 deletions pkg/security/tests/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,19 @@ func TestEventRaleLimiters(t *testing.T) {

ruleDefs := []*rules.RuleDefinition{
{
ID: "test_unique_id",
Expression: `open.file.path == "{{.Root}}/test-unique-id"`,
Every: 5 * time.Second,
ID: "test_unique_id",
Expression: `open.file.path == "{{.Root}}/test-unique-id"`,
Every: rules.HumanReadableDuration{
Duration: 5 * time.Second,
},
RateLimiterToken: []string{"process.file.name"},
},
{
ID: "test_std",
Expression: `open.file.path == "{{.Root}}/test-std"`,
Every: 5 * time.Second,
Every: rules.HumanReadableDuration{
Duration: 5 * time.Second,
},
},
}

Expand Down

0 comments on commit 6f3217d

Please sign in to comment.