diff --git a/pkg/acquisition/modules/waap/waap_runner.go b/pkg/acquisition/modules/waap/waap_runner.go index a8f83d41e..27d53985d 100644 --- a/pkg/acquisition/modules/waap/waap_runner.go +++ b/pkg/acquisition/modules/waap/waap_runner.go @@ -6,7 +6,6 @@ import ( "time" "github.com/crowdsecurity/coraza/v3" - "github.com/crowdsecurity/coraza/v3/experimental" corazatypes "github.com/crowdsecurity/coraza/v3/types" "github.com/crowdsecurity/crowdsec/pkg/types" "github.com/crowdsecurity/crowdsec/pkg/waf" @@ -77,7 +76,7 @@ func (r *WaapRunner) Init(datadir string) error { return nil } -func (r *WaapRunner) processRequest(tx experimental.FullTransaction, request *waf.ParsedRequest) error { +func (r *WaapRunner) processRequest(tx waf.ExtendedTransaction, request *waf.ParsedRequest) error { var in *corazatypes.Interruption var err error request.Tx = tx @@ -92,7 +91,14 @@ func (r *WaapRunner) processRequest(tx experimental.FullTransaction, request *wa //We don't close the transaction here, as it will reset coraza internal state and break variable tracking }() - request.Tx.ProcessConnection(request.RemoteAddr, 0, "", 0) + //pre eval (expr) rules + err = r.WaapRuntime.ProcessPreEvalRules(request) + if err != nil { + r.logger.Errorf("unable to process PreEval rules: %s", err) + //FIXME: should we abort here ? + } + + request.Tx.Tx.ProcessConnection(request.RemoteAddr, 0, "", 0) for k, v := range request.Args { for _, vv := range v { @@ -151,14 +157,16 @@ func (r *WaapRunner) processRequest(tx experimental.FullTransaction, request *wa } func (r *WaapRunner) ProcessInBandRules(request *waf.ParsedRequest) error { - tx := r.WaapInbandEngine.NewTransactionWithID(request.UUID) - err := r.processRequest(tx.(experimental.FullTransaction), request) + tx := waf.NewExtendedTransaction(r.WaapInbandEngine, request.UUID) + r.WaapRuntime.InBandTx = tx + err := r.processRequest(tx, request) return err } func (r *WaapRunner) ProcessOutOfBandRules(request *waf.ParsedRequest) error { - tx := r.WaapOutbandEngine.NewTransactionWithID(request.UUID) - err := r.processRequest(tx.(experimental.FullTransaction), request) + tx := waf.NewExtendedTransaction(r.WaapInbandEngine, request.UUID) + r.WaapRuntime.OutOfBandTx = tx + err := r.processRequest(tx, request) return err } @@ -180,14 +188,8 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error { //to measure the time spent in the WAF startParsing := time.Now() - //pre eval (expr) rules - err := r.WaapRuntime.ProcessPreEvalRules(request) - if err != nil { - r.logger.Errorf("unable to process PreEval rules: %s", err) - continue - } //inband WAAP rules - err = r.ProcessInBandRules(&request) + err := r.ProcessInBandRules(&request) if err != nil { r.logger.Errorf("unable to process InBand rules: %s", err) continue @@ -206,7 +208,7 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error { r.logger.Debugf("inband rules matched : %d", in.RuleID) r.WaapRuntime.Response.InBandInterrupt = true - err = r.WaapRuntime.ProcessOnMatchRules(request) + err = r.WaapRuntime.ProcessOnMatchRules(&request) if err != nil { r.logger.Errorf("unable to process OnMatch rules: %s", err) continue @@ -236,7 +238,7 @@ func (r *WaapRunner) Run(t *tomb.Tomb) error { if in := request.Tx.Interruption(); in != nil { r.logger.Debugf("outband rules matched : %d", in.RuleID) r.WaapRuntime.Response.OutOfBandInterrupt = true - err = r.WaapRuntime.ProcessOnMatchRules(request) + err = r.WaapRuntime.ProcessOnMatchRules(&request) if err != nil { r.logger.Errorf("unable to process OnMatch rules: %s", err) continue diff --git a/pkg/waf/request.go b/pkg/waf/request.go index f7b7a11b7..904d5fdd3 100644 --- a/pkg/waf/request.go +++ b/pkg/waf/request.go @@ -6,7 +6,6 @@ import ( "net/http" "net/url" - "github.com/crowdsecurity/coraza/v3/experimental" "github.com/google/uuid" ) @@ -74,7 +73,7 @@ type ParsedRequest struct { Body []byte TransferEncoding []string UUID string - Tx experimental.FullTransaction + Tx ExtendedTransaction ResponseChannel chan WaapTempResponse IsInBand bool IsOutBand bool diff --git a/pkg/waf/tx.go b/pkg/waf/tx.go index 416ad953e..ce44aec95 100644 --- a/pkg/waf/tx.go +++ b/pkg/waf/tx.go @@ -3,6 +3,8 @@ package waf import ( "github.com/crowdsecurity/coraza/v3" "github.com/crowdsecurity/coraza/v3/experimental" + "github.com/crowdsecurity/coraza/v3/experimental/plugins/plugintypes" + "github.com/crowdsecurity/coraza/v3/types" ) type ExtendedTransaction struct { @@ -24,3 +26,63 @@ func (t *ExtendedTransaction) RemoveRuleByIDWithError(id int) error { t.Tx.RemoveRuleByID(id) return nil } + +func (t *ExtendedTransaction) IsRuleEngineOff() bool { + return t.Tx.IsRuleEngineOff() +} + +func (t *ExtendedTransaction) ProcessLogging() { + t.Tx.ProcessLogging() +} + +func (t *ExtendedTransaction) ProcessConnection(client string, cPort int, server string, sPort int) { + t.Tx.ProcessConnection(client, cPort, server, sPort) +} + +func (t *ExtendedTransaction) AddGetRequestArgument(name string, value string) { + t.Tx.AddGetRequestArgument(name, value) +} + +func (t *ExtendedTransaction) ProcessURI(uri string, method string, httpVersion string) { + t.Tx.ProcessURI(uri, method, httpVersion) +} + +func (t *ExtendedTransaction) AddRequestHeader(name string, value string) { + t.Tx.AddRequestHeader(name, value) +} + +func (t *ExtendedTransaction) SetServerName(name string) { + t.Tx.SetServerName(name) +} + +func (t *ExtendedTransaction) ProcessRequestHeaders() *types.Interruption { + return t.Tx.ProcessRequestHeaders() +} + +func (t *ExtendedTransaction) ProcessRequestBody() (*types.Interruption, error) { + return t.Tx.ProcessRequestBody() +} + +func (t *ExtendedTransaction) WriteRequestBody(body []byte) (*types.Interruption, int, error) { + return t.Tx.WriteRequestBody(body) +} + +func (t *ExtendedTransaction) Interruption() *types.Interruption { + return t.Tx.Interruption() +} + +func (t *ExtendedTransaction) IsInterrupted() bool { + return t.Tx.IsInterrupted() +} + +func (t *ExtendedTransaction) Variables() plugintypes.TransactionVariables { + return t.Tx.Variables() +} + +func (t *ExtendedTransaction) MatchedRules() []types.MatchedRule { + return t.Tx.MatchedRules() +} + +func (t *ExtendedTransaction) ID() string { + return t.Tx.ID() +} diff --git a/pkg/waf/waap.go b/pkg/waf/waap.go index 449920508..b58d414c1 100644 --- a/pkg/waf/waap.go +++ b/pkg/waf/waap.go @@ -21,18 +21,34 @@ type Hook struct { ApplyExpr []*vm.Program `yaml:"-"` } -// @tko : todo - debug mode -func (h *Hook) Build() error { +const ( + hookOnLoad = iota + hookPreEval + hookOnMatch +) +// @tko : todo - debug mode +func (h *Hook) Build(hookStage int) error { + + ctx := map[string]interface{}{} + switch hookStage { + case hookOnLoad: + ctx = GetOnLoadEnv(&WaapRuntimeConfig{}) + case hookPreEval: + ctx = GetPreEvalEnv(&WaapRuntimeConfig{}, &ParsedRequest{}) + case hookOnMatch: + ctx = GetOnMatchEnv(&WaapRuntimeConfig{}, &ParsedRequest{}) + } + opts := GetExprWAFOptions(ctx) if h.Filter != "" { - program, err := expr.Compile(h.Filter) //FIXME: opts + program, err := expr.Compile(h.Filter, opts...) //FIXME: opts if err != nil { return fmt.Errorf("unable to compile filter %s : %w", h.Filter, err) } h.FilterExpr = program } for _, apply := range h.Apply { - program, err := expr.Compile(apply, GetExprWAFOptions(GetHookEnv(&WaapRuntimeConfig{}, ParsedRequest{}))...) + program, err := expr.Compile(apply, opts...) if err != nil { return fmt.Errorf("unable to compile apply %s : %w", apply, err) } @@ -204,7 +220,7 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) { //load hooks for _, hook := range wc.OnLoad { - err := hook.Build() + err := hook.Build(hookOnLoad) if err != nil { return nil, fmt.Errorf("unable to build on_load hook : %s", err) } @@ -212,7 +228,7 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) { } for _, hook := range wc.PreEval { - err := hook.Build() + err := hook.Build(hookPreEval) if err != nil { return nil, fmt.Errorf("unable to build pre_eval hook : %s", err) } @@ -220,7 +236,7 @@ func (wc *WaapConfig) Build() (*WaapRuntimeConfig, error) { } for _, hook := range wc.OnMatch { - err := hook.Build() + err := hook.Build(hookOnMatch) if err != nil { return nil, fmt.Errorf("unable to build on_match hook : %s", err) } @@ -243,7 +259,7 @@ func (w *WaapRuntimeConfig) ProcessOnLoadRules() error { if rule.FilterExpr != nil { output, err := expr.Run(rule.FilterExpr, GetOnLoadEnv(w)) if err != nil { - return fmt.Errorf("unable to run filter %s : %w", rule.Filter, err) + return fmt.Errorf("unable to run waap on_load filter %s : %w", rule.Filter, err) } switch t := output.(type) { case bool: @@ -259,7 +275,7 @@ func (w *WaapRuntimeConfig) ProcessOnLoadRules() error { for _, applyExpr := range rule.ApplyExpr { _, err := expr.Run(applyExpr, GetOnLoadEnv(w)) if err != nil { - log.Errorf("unable to apply filter: %s", err) + log.Errorf("unable to apply waap on_load expr: %s", err) continue } } @@ -267,13 +283,13 @@ func (w *WaapRuntimeConfig) ProcessOnLoadRules() error { return nil } -func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest) error { +func (w *WaapRuntimeConfig) ProcessOnMatchRules(request *ParsedRequest) error { for _, rule := range w.CompiledOnMatch { if rule.FilterExpr != nil { output, err := expr.Run(rule.FilterExpr, GetOnMatchEnv(w, request)) if err != nil { - return fmt.Errorf("unable to run filter %s : %w", rule.Filter, err) + return fmt.Errorf("unable to run waap on_match filter %s : %w", rule.Filter, err) } switch t := output.(type) { case bool: @@ -289,7 +305,7 @@ func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest) error { for _, applyExpr := range rule.ApplyExpr { _, err := expr.Run(applyExpr, GetOnMatchEnv(w, request)) if err != nil { - log.Errorf("unable to apply filter: %s", err) + log.Errorf("unable to apply waap on_match expr: %s", err) continue } } @@ -297,12 +313,12 @@ func (w *WaapRuntimeConfig) ProcessOnMatchRules(request ParsedRequest) error { return nil } -func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error { +func (w *WaapRuntimeConfig) ProcessPreEvalRules(request *ParsedRequest) error { for _, rule := range w.CompiledPreEval { if rule.FilterExpr != nil { output, err := expr.Run(rule.FilterExpr, GetPreEvalEnv(w, request)) if err != nil { - return fmt.Errorf("unable to run filter %s : %w", rule.Filter, err) + return fmt.Errorf("unable to run waap pre_eval filter %s : %w", rule.Filter, err) } switch t := output.(type) { case bool: @@ -319,7 +335,7 @@ func (w *WaapRuntimeConfig) ProcessPreEvalRules(request ParsedRequest) error { for _, applyExpr := range rule.ApplyExpr { _, err := expr.Run(applyExpr, GetPreEvalEnv(w, request)) if err != nil { - log.Errorf("unable to apply filter: %s", err) + log.Errorf("unable to apply waap pre_eval expr: %s", err) continue } } @@ -343,6 +359,13 @@ func (w *WaapRuntimeConfig) RemoveInbandRuleByID(params ...any) (any, error) { return nil, nil } +// func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(id int) error { +func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(params ...any) (any, error) { + id := params[0].(int) + _ = w.OutOfBandTx.RemoveRuleByIDWithError(id) + return nil, nil +} + func (w *WaapRuntimeConfig) CancelEvent(params ...any) (any, error) { w.Response.SendEvent = false return nil, nil @@ -399,13 +422,6 @@ func (w *WaapRuntimeConfig) SetActionByID(params ...any) (any, error) { return nil, nil } -// func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(id int) error { -func (w *WaapRuntimeConfig) RemoveOutbandRuleByID(params ...any) (any, error) { - id := params[0].(int) - _ = w.OutOfBandTx.RemoveRuleByIDWithError(id) - return nil, nil -} - // func (w *WaapRuntimeConfig) SetAction(action string) error { func (w *WaapRuntimeConfig) SetAction(params ...any) (any, error) { //log.Infof("setting to %s", action) diff --git a/pkg/waf/waf_expr_lib.go b/pkg/waf/waf_expr_lib.go index 48685957d..23e2c711b 100644 --- a/pkg/waf/waf_expr_lib.go +++ b/pkg/waf/waf_expr_lib.go @@ -8,7 +8,26 @@ type exprCustomFunc struct { signature []interface{} } -var onLoadExprFuncs = []exprCustomFunc{} +/* +func GetOnLoadEnv(w *WaapRuntimeConfig) map[string]interface{} { + return map[string]interface{}{ + "DisableInBandRuleByID": w.DisableInBandRuleByID, + "DisableOutBandRuleByID": w.DisableOutBandRuleByID, + "DisableInBandRuleByTag": w.DisableInBandRuleByTag, + "DisableOutBandRuleByTag": w.DisableOutBandRuleByTag, + } +} +*/ + +/*var onLoadExprFuncs = []exprCustomFunc{ + { + name: "DisableInBandRuleByID", + function: w.DisableInBandRuleByID, + signature: []interface{}{ + new(func(int) error), + }, + }, +}*/ var preEvalExprFuncs = []exprCustomFunc{} diff --git a/pkg/waf/waf_helpers.go b/pkg/waf/waf_helpers.go index 22a35a8b9..968d8deca 100644 --- a/pkg/waf/waf_helpers.go +++ b/pkg/waf/waf_helpers.go @@ -32,6 +32,7 @@ func GetExprWAFOptions(ctx map[string]interface{}) []expr.Option { } func GetOnLoadEnv(w *WaapRuntimeConfig) map[string]interface{} { + //FIXME: use expr.Function instead of this return map[string]interface{}{ "DisableInBandRuleByID": w.DisableInBandRuleByID, "DisableOutBandRuleByID": w.DisableOutBandRuleByID, @@ -40,7 +41,8 @@ func GetOnLoadEnv(w *WaapRuntimeConfig) map[string]interface{} { } } -func GetPreEvalEnv(w *WaapRuntimeConfig, request ParsedRequest) map[string]interface{} { +func GetPreEvalEnv(w *WaapRuntimeConfig, request *ParsedRequest) map[string]interface{} { + //FIXME: use expr.Function instead of this return map[string]interface{}{ "IsInBand": request.IsInBand, "IsOutBand": request.IsOutBand, @@ -51,7 +53,8 @@ func GetPreEvalEnv(w *WaapRuntimeConfig, request ParsedRequest) map[string]inter } } -func GetOnMatchEnv(w *WaapRuntimeConfig, request ParsedRequest) map[string]interface{} { +func GetOnMatchEnv(w *WaapRuntimeConfig, request *ParsedRequest) map[string]interface{} { + //FIXME: use expr.Function instead of this return map[string]interface{}{ "req": request, "IsInBand": request.IsInBand,