github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/rules/approvers.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the Apache License Version 2.0.
     3  // This product includes software developed at Datadog (https://www.datadoghq.com/).
     4  // Copyright 2016-present Datadog, Inc.
     5  
     6  // Package rules holds rules related files
     7  package rules
     8  
     9  import (
    10  	"errors"
    11  
    12  	"github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval"
    13  )
    14  
    15  // Approvers are just filter values indexed by field
    16  type Approvers map[eval.Field]FilterValues
    17  
    18  // isAnApprover returns whether the given value is an approver for the given rule
    19  func isAnApprover(event eval.Event, ctx *eval.Context, rule *Rule, field eval.Field, value interface{}) (bool, error) {
    20  	var readOnlyError *eval.ErrFieldReadOnly
    21  	if err := event.SetFieldValue(field, value); err != nil {
    22  		if errors.As(err, &readOnlyError) {
    23  			return false, nil
    24  		}
    25  		return false, err
    26  	}
    27  	origResult, err := rule.PartialEval(ctx, field)
    28  	if err != nil {
    29  		return false, err
    30  	}
    31  
    32  	notValue, err := eval.NotOfValue(value)
    33  	if err != nil {
    34  		return false, err
    35  	}
    36  
    37  	if err := event.SetFieldValue(field, notValue); err != nil {
    38  		if errors.As(err, &readOnlyError) {
    39  			return false, nil
    40  		}
    41  		return false, err
    42  	}
    43  	notResult, err := rule.PartialEval(ctx, field)
    44  	if err != nil {
    45  		return false, err
    46  	}
    47  
    48  	return origResult && !notResult, nil
    49  }
    50  
    51  func bitmaskCombinations(bitmasks []int) []int {
    52  	if len(bitmasks) == 0 {
    53  		return nil
    54  	}
    55  
    56  	combinationCount := 1 << len(bitmasks)
    57  	result := make([]int, 0, combinationCount)
    58  	for i := 0; i < combinationCount; i++ {
    59  		var mask int
    60  		for j, value := range bitmasks {
    61  			if (i & (1 << j)) > 0 {
    62  				mask |= value
    63  			}
    64  		}
    65  		result = append(result, mask)
    66  	}
    67  
    68  	return result
    69  }
    70  
    71  // GetApprovers returns approvers for the given rules
    72  func GetApprovers(rules []*Rule, event eval.Event, fieldCaps FieldCapabilities) (Approvers, error) {
    73  	approvers := make(Approvers)
    74  
    75  	ctx := eval.NewContext(event)
    76  
    77  	// for each rule we should at least find one approver otherwise we will return no approver for the field
    78  	for _, rule := range rules {
    79  		var bestFilterField eval.Field
    80  		var bestFilterValues FilterValues
    81  		var bestFilterWeight int
    82  
    83  	LOOP:
    84  		for _, fieldCap := range fieldCaps {
    85  			field := fieldCap.Field
    86  
    87  			var filterValues FilterValues
    88  			var bitmasks []int
    89  
    90  			for _, value := range rule.GetFieldValues(field) {
    91  				switch value.Type {
    92  				case eval.ScalarValueType, eval.PatternValueType, eval.GlobValueType:
    93  					isAnApprover, err := isAnApprover(event, ctx, rule, field, value.Value)
    94  					if err != nil {
    95  						return nil, err
    96  					}
    97  
    98  					if isAnApprover {
    99  						filterValues = filterValues.Merge(FilterValue{Field: field, Value: value.Value, Type: value.Type})
   100  					} else if fieldCap.Types&eval.BitmaskValueType == 0 {
   101  						// if not a bitmask we need to have all the value as approvers
   102  						// basically a list of values ex: in ["test123", "test456"]
   103  						continue LOOP
   104  					}
   105  				case eval.BitmaskValueType:
   106  					bitmasks = append(bitmasks, value.Value.(int))
   107  				}
   108  			}
   109  
   110  			for _, bitmask := range bitmaskCombinations(bitmasks) {
   111  				isAnApprover, err := isAnApprover(event, ctx, rule, field, bitmask)
   112  				if err != nil {
   113  					return nil, err
   114  				}
   115  
   116  				if isAnApprover {
   117  					filterValues = filterValues.Merge(FilterValue{Field: field, Value: bitmask, Type: eval.BitmaskValueType})
   118  				}
   119  			}
   120  
   121  			if len(filterValues) == 0 || !fieldCaps.Validate(filterValues) {
   122  				continue
   123  			}
   124  
   125  			if bestFilterValues == nil || fieldCap.FilterWeight > bestFilterWeight {
   126  				bestFilterField = field
   127  				bestFilterValues = filterValues
   128  				bestFilterWeight = fieldCap.FilterWeight
   129  			}
   130  		}
   131  
   132  		// no filter value for a rule thus no approver for the event type
   133  		if bestFilterValues == nil {
   134  			return nil, nil
   135  		}
   136  
   137  		approvers[bestFilterField] = append(approvers[bestFilterField], bestFilterValues...)
   138  	}
   139  
   140  	return approvers, nil
   141  }