github.com/crowdsecurity/crowdsec@v1.6.1/pkg/acquisition/modules/appsec/rx_operator.go (about)

     1  package appsecacquisition
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"unicode/utf8"
     7  
     8  	"github.com/crowdsecurity/coraza/v3/experimental/plugins"
     9  	"github.com/crowdsecurity/coraza/v3/experimental/plugins/plugintypes"
    10  	"github.com/wasilibs/go-re2"
    11  	"github.com/wasilibs/go-re2/experimental"
    12  )
    13  
    14  type rx struct {
    15  	re *re2.Regexp
    16  }
    17  
    18  var _ plugintypes.Operator = (*rx)(nil)
    19  
    20  func newRX(options plugintypes.OperatorOptions) (plugintypes.Operator, error) {
    21  	// (?sm) enables multiline mode which makes 942522-7 work, see
    22  	// - https://stackoverflow.com/a/27680233
    23  	// - https://groups.google.com/g/golang-nuts/c/jiVdamGFU9E
    24  	data := fmt.Sprintf("(?sm)%s", options.Arguments)
    25  
    26  	var re *re2.Regexp
    27  	var err error
    28  
    29  	if matchesArbitraryBytes(data) {
    30  		re, err = experimental.CompileLatin1(data)
    31  	} else {
    32  		re, err = re2.Compile(data)
    33  	}
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	return &rx{re: re}, nil
    38  }
    39  
    40  func (o *rx) Evaluate(tx plugintypes.TransactionState, value string) bool {
    41  	if tx.Capturing() {
    42  		match := o.re.FindStringSubmatch(value)
    43  		if len(match) == 0 {
    44  			return false
    45  		}
    46  		for i, c := range match {
    47  			if i == 9 {
    48  				return true
    49  			}
    50  			tx.CaptureField(i, c)
    51  		}
    52  		return true
    53  	} else {
    54  		return o.re.MatchString(value)
    55  	}
    56  }
    57  
    58  // RegisterRX registers the rx operator using a WASI implementation instead of Go.
    59  func RegisterRX() {
    60  	plugins.RegisterOperator("rx", newRX)
    61  }
    62  
    63  // matchesArbitraryBytes checks for control sequences for byte matches in the expression.
    64  // If the sequences are not valid utf8, it returns true.
    65  func matchesArbitraryBytes(expr string) bool {
    66  	decoded := make([]byte, 0, len(expr))
    67  	for i := 0; i < len(expr); i++ {
    68  		c := expr[i]
    69  		if c != '\\' {
    70  			decoded = append(decoded, c)
    71  			continue
    72  		}
    73  		if i+3 >= len(expr) {
    74  			decoded = append(decoded, expr[i:]...)
    75  			break
    76  		}
    77  		if expr[i+1] != 'x' {
    78  			decoded = append(decoded, expr[i])
    79  			continue
    80  		}
    81  
    82  		v, mb, _, err := strconv.UnquoteChar(expr[i:], 0)
    83  		if err != nil || mb {
    84  			// Wasn't a byte escape sequence, shouldn't happen in practice.
    85  			decoded = append(decoded, expr[i])
    86  			continue
    87  		}
    88  
    89  		decoded = append(decoded, byte(v))
    90  		i += 3
    91  	}
    92  
    93  	return !utf8.Valid(decoded)
    94  }