From 96056aace2c92b5530da7d67aceefc284fdbf3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20de=20Le=C3=B3n?= Date: Fri, 20 Feb 2026 16:09:20 -0300 Subject: [PATCH 1/2] test: add testdata and fix tests --- configstore/configstore_test.go | 16 ++-- pluginmanager/pluginmanager_test.go | 26 +++---- testdata/plugins/Makefile | 12 +++ testdata/plugins/decision/error_check.go | 23 ++++++ testdata/plugins/decision/no_check.go | 15 ++++ testdata/plugins/decision/simple.go | 92 +++++++++++++++++++++++ testdata/plugins/decision/test.go | 49 ++++++++++++ testdata/plugins/decision/weighted_sum.go | 90 ++++++++++++++++++++++ testdata/plugins/decision/wrong_check.go | 21 ++++++ testdata/plugins/model/error_init.go | 25 ++++++ testdata/plugins/model/error_req.go | 25 ++++++ testdata/plugins/model/no_init.go | 15 ++++ testdata/plugins/model/no_req.go | 10 +++ testdata/plugins/model/trivial.go | 43 +++++++++++ testdata/plugins/model/trivial2.go | 43 +++++++++++ testdata/plugins/model/trivial_async.go | 58 ++++++++++++++ testdata/plugins/model/trivial_async2.go | 58 ++++++++++++++ testdata/plugins/model/wrong_init.go | 14 ++++ testdata/plugins/model/wrong_req.go | 16 ++++ wacecore_test.go | 46 ++++++------ 20 files changed, 653 insertions(+), 44 deletions(-) create mode 100644 testdata/plugins/Makefile create mode 100644 testdata/plugins/decision/error_check.go create mode 100644 testdata/plugins/decision/no_check.go create mode 100644 testdata/plugins/decision/simple.go create mode 100644 testdata/plugins/decision/test.go create mode 100644 testdata/plugins/decision/weighted_sum.go create mode 100644 testdata/plugins/decision/wrong_check.go create mode 100644 testdata/plugins/model/error_init.go create mode 100644 testdata/plugins/model/error_req.go create mode 100644 testdata/plugins/model/no_init.go create mode 100644 testdata/plugins/model/no_req.go create mode 100644 testdata/plugins/model/trivial.go create mode 100644 testdata/plugins/model/trivial2.go create mode 100644 testdata/plugins/model/trivial_async.go create mode 100644 testdata/plugins/model/trivial_async2.go create mode 100644 testdata/plugins/model/wrong_init.go create mode 100644 testdata/plugins/model/wrong_req.go diff --git a/configstore/configstore_test.go b/configstore/configstore_test.go index e232049..c5ed707 100644 --- a/configstore/configstore_test.go +++ b/configstore/configstore_test.go @@ -13,7 +13,7 @@ logpath: "/dev/stderr" loglevel: "DEBUG" modelplugins: - id: "trivial" - path: "../_plugins/model/trivial.so" + path: "../testdata/plugins/model/trivial.so" weight: 1 threshold: 0.5 params: @@ -23,7 +23,7 @@ modelplugins: plugintype: "RequestHeaders" mode: "sync" - id: "trivial2" - path: "../_plugins/model/trivial2.so" + path: "../testdata/plugins/model/trivial2.so" weight: 2 threshold: 0.1 params: @@ -33,7 +33,7 @@ modelplugins: plugintype: "RequestHeaders" decisionplugins: - id: "test" - path: "../_plugins/decision/test.so" + path: "../testdata/plugins/decision/test.so" wafweight: 0.5 decisionbalance: 0.5 params: @@ -108,7 +108,7 @@ loglevel: ERROR logpath: /dev/null modelplugins: - id: "testplugin" - path: "../_plugins/model/trivial.so" + path: "../testdata/plugins/model/trivial.so" plugintype: InvalidPluginType `)) if err == nil { @@ -120,7 +120,7 @@ loglevel: ERROR logpath: /dev/null modelplugins: - id: "testplugin" - path: "../_plugins/model/trivial.so" + path: "../testdata/plugins/model/trivial.so" plugintype: "" `)) if err == nil { @@ -132,7 +132,7 @@ loglevel: ERROR logpath: /dev/null modelplugins: - id: "testplugin" - path: "../_plugins/model/nonexistent.so" + path: "../testdata/plugins/model/nonexistent.so" plugintype: "RequestHeaders" `)) if err == nil { @@ -167,7 +167,7 @@ loglevel: ERROR logpath: /dev/null decisionplugins: - id: "testplugin" - path: "../_plugins/decision/nonexistent.so" + path: "../testdata/plugins/decision/nonexistent.so" `)) if err == nil { t.Errorf("nonexistent decision plugin path does not return error") @@ -189,7 +189,7 @@ loglevel: ERROR logpath: /dev/null modelplugins: - id: "testplugin" - path: "../_plugins/model/trivial.so" + path: "../testdata/plugins/model/trivial.so" plugintype: "` + v + `" ` err = initialize([]byte(config)) diff --git a/pluginmanager/pluginmanager_test.go b/pluginmanager/pluginmanager_test.go index c3c3889..a00efa0 100644 --- a/pluginmanager/pluginmanager_test.go +++ b/pluginmanager/pluginmanager_test.go @@ -17,7 +17,7 @@ loglevel: "WARN" ` var trivialPlugin = ` - id: "trivial" - path: "../_plugins/model/trivial.so" + path: "../testdata/plugins/model/trivial.so" weight: 1 params: param1: "first value" @@ -28,7 +28,7 @@ var trivialPlugin = ` - id: "trivial" ` var testPlugin = ` - id: "test" - path: "../_plugins/decision/test.so" + path: "../testdata/plugins/decision/test.so" wafweight: 0.5 decisionbalance: 0.5 params: @@ -84,19 +84,19 @@ func init() { // func TestPluginInit(t *testing.T) { // cases := []struct{ id, conf string }{ // // {"invalid_path", ` - id: "invalid_path" -// // path: "../_plugins/model/nonexistent.so" +// // path: "../testdata/plugins/model/nonexistent.so" // // plugintype: "AllRequest" // // `}, // {"no_init", ` - id: "no_init" -// path: "../_plugins/model/no_init.so" +// path: "../testdata/plugins/model/no_init.so" // plugintype: "AllRequest" // `}, // {"wrong_init", ` - id: "wrong_init" -// path: "../_plugins/model/wrong_init.so" +// path: "../testdata/plugins/model/wrong_init.so" // plugintype: "AllRequest" // `}, // {"error_init", ` - id: "error_init" -// path: "../_plugins/model/error_init.so" +// path: "../testdata/plugins/model/error_init.so" // plugintype: "AllRequest" // `}, // } @@ -223,7 +223,7 @@ func init() { // for _, c := range cases { // config := baseConfig + "modelplugins:\n" + // " - id: \"" + c.id + "\"\n" + -// " path: \"../_plugins/model/trivial.so\"\n" + +// " path: \"../testdata/plugins/model/trivial.so\"\n" + // " plugintype: \"" + c.pluginType.String() + "\"\n" // err := initilize([]byte(config)) @@ -267,15 +267,15 @@ func init() { // func TestProcessRequestInvalid(t *testing.T) { // cases := []struct{ id, conf string }{ // {"no_req", ` - id: "no_req" -// path: "../_plugins/model/no_req.so" +// path: "../testdata/plugins/model/no_req.so" // plugintype: "Everything" // `}, // {"wrong_req", ` - id: "wrong_req" -// path: "../_plugins/model/wrong_req.so" +// path: "../testdata/plugins/model/wrong_req.so" // plugintype: "Everything" // `}, // {"error_req", ` - id: "error_req" -// path: "../_plugins/model/error_req.so" +// path: "../testdata/plugins/model/error_req.so" // plugintype: "Everything" // `}, // } @@ -326,13 +326,13 @@ func init() { // func TestCheckResultInvalid(t *testing.T) { // cases := []struct{ id, conf string }{ // {"no_check", ` - id: "no_check" -// path: "../_plugins/decision/no_check.so" +// path: "../testdata/plugins/decision/no_check.so" // `}, // {"wrong_check", ` - id: "wrong_check" -// path: "../_plugins/decision/wrong_check.so" +// path: "../testdata/plugins/decision/wrong_check.so" // `}, // {"error_check", ` - id: "error_check" -// path: "../_plugins/decision/error_check.so" +// path: "../testdata/plugins/decision/error_check.so" // `}, // } diff --git a/testdata/plugins/Makefile b/testdata/plugins/Makefile new file mode 100644 index 0000000..2943c3e --- /dev/null +++ b/testdata/plugins/Makefile @@ -0,0 +1,12 @@ +all: model/trivial.so model/trivial2.so \ + model/no_init.so model/wrong_init.so model/error_init.so \ + model/no_req.so model/wrong_req.so model/error_req.so \ + decision/test.so \ + decision/no_check.so decision/wrong_check.so decision/error_check.so \ + decision/simple.so + +%.so: %.go + go build $(FLAGS) -buildmode=plugin -o $@ $< + +clean: + rm -f model/*.so decision/*.so diff --git a/testdata/plugins/decision/error_check.go b/testdata/plugins/decision/error_check.go new file mode 100644 index 0000000..dfa3e5e --- /dev/null +++ b/testdata/plugins/decision/error_check.go @@ -0,0 +1,23 @@ +/* Trivial Decision Plugin that always returns no attack + */ + +package main + +import ( + "errors" + + lg "github.com/tilsor/ModSecIntl_logging/logging" +) + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string) error { + logger := lg.Get() + logger.Printf(lg.WARN, "[simple:InitPlugin] %v\n", params) + return nil +} + +// CheckResults returns true (block traffic) if WAF says so, and false +// in other case. +func CheckResults(transactionID string, modelRes map[string]float64, modelWeight map[string]float64, modelThres map[string]float64, wafData map[string]string) (bool, error) { + return false, errors.New("Some error") +} diff --git a/testdata/plugins/decision/no_check.go b/testdata/plugins/decision/no_check.go new file mode 100644 index 0000000..13d8516 --- /dev/null +++ b/testdata/plugins/decision/no_check.go @@ -0,0 +1,15 @@ +/* Trivial Decision Plugin that always returns no attack + */ + +package main + +import ( + lg "github.com/tilsor/ModSecIntl_logging/logging" +) + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string) error { + logger := lg.Get() + logger.Printf(lg.WARN, "[simple:InitPlugin] %v\n", params) + return nil +} diff --git a/testdata/plugins/decision/simple.go b/testdata/plugins/decision/simple.go new file mode 100644 index 0000000..dc0e1f2 --- /dev/null +++ b/testdata/plugins/decision/simple.go @@ -0,0 +1,92 @@ +/* Trivial Decision Plugin that always returns no attack + */ + +package main + +import ( + "context" + "strconv" + + lg "github.com/tilsor/ModSecIntl_logging/logging" + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +func InitPlugin(params map[string]string, meter metric.Meter) error { + // Create counter for plugin register + ctx := context.Background() + pluginCounter, err := meter.Int64Counter("plugin_register") + if err != nil { + return err + } + pluginCounter.Add(ctx, 1, metric.WithAttributes(attribute.String("plugin_name", "simple"), attribute.String("plugin_type", "decision"))) + return nil +} + +func CheckResults(decisionInput pm.DecisionInput) (bool, error) { + logger := lg.Get() + var totalModelW float64 = 0 + var modelDetectionCount int = 0 + var totalModelProb float64 = 0 + for key, value := range decisionInput.Results { + logger.TPrintf(lg.DEBUG, decisionInput.TransactionId, "simple | model_id: %v result: %v", key, value) + if value.ProbAttack >= 0.5 { + modelDetectionCount++ + totalModelW += decisionInput.ModelWeight[key] + } + } + + for key, value := range decisionInput.WAFdata { + logger.TPrintf(lg.DEBUG, decisionInput.TransactionId, "simple | WAF data: %v: %v", key, value) + } + + // if we have some model results + if modelDetectionCount > 0 { + totalModelProb = totalModelW / float64(modelDetectionCount) + } + if len(decisionInput.WAFdata) != 0 { + as, _ := strconv.Atoi(decisionInput.WAFdata["inbound_blocking"]) + it, _ := strconv.Atoi(decisionInput.WAFdata["inbound_threshold"]) + logger.TPrintf(lg.DEBUG, decisionInput.TransactionId, "Coraza | Anomaly score: %v Anomaly score threshold: %v ", as, it) + + if as >= it && totalModelProb > 0.5 { // coraza wants to block and models agree + return true, nil + } + } + return false, nil +} + +// func CheckResults(transactionID string, modelRes map[string]float64, modelWeight map[string]float64, modelThres map[string]float64, WAFdata map[string]string) (bool, error) { +// logger := lg.Get() +// var totalModelW float64 = 0 +// var modelDetectionCount int = 0 +// var totalModelProb float64 = 0 +// for key, value := range modelRes { +// logger.TPrintf(lg.DEBUG, transactionID, "simple | model_id: %v result: %v threshold: %v", key, value, modelThres[key]) +// if value >= modelThres[key] { +// modelDetectionCount++ +// totalModelW += modelWeight[key] +// } +// } + +// // DEBUG: print WAF data +// for key, value := range WAFdata { +// logger.TPrintf(lg.DEBUG, transactionID, "simple | WAF data: %v: %v", key, value) +// } + +// // if we have some model results +// if modelDetectionCount > 0 { +// totalModelProb = totalModelW / float64(modelDetectionCount) +// } +// if len(WAFdata) != 0 { +// as, _ := strconv.Atoi(WAFdata["inbound_blocking"]) +// it, _ := strconv.Atoi(WAFdata["inbound_threshold"]) +// logger.TPrintf(lg.DEBUG, transactionID, "Coraza | Anomaly score: %v Anomaly score threshold: %v ", as, it) + +// if as >= it && totalModelProb > 0.5 { // modsec wants to block +// return true, nil +// } +// } +// return false, nil +// } diff --git a/testdata/plugins/decision/test.go b/testdata/plugins/decision/test.go new file mode 100644 index 0000000..daaf564 --- /dev/null +++ b/testdata/plugins/decision/test.go @@ -0,0 +1,49 @@ +/* Trivial Test Decision Plugin that always returns no attack + */ + +package main + +import ( + "strconv" + + lg "github.com/tilsor/ModSecIntl_logging/logging" + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/metric" +) + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string, meter metric.Meter) error { + logger := lg.Get() + logger.Printf(lg.WARN, "[test:InitPlugin] %v\n", params) + + return nil +} + +// CheckResults returns true (block traffic) if WAF says so, and false +// in other case. +// func CheckResults(transactionID string, modelRes map[string]float64, modelWeight map[string]float64, modelThres map[string]float64, wafData map[string]string) (bool, error) { +func CheckResults(decisionInput pm.DecisionInput) (bool, error) { + logger := lg.Get() + + modelRes := decisionInput.Results + modelWeight := decisionInput.ModelWeight + wafData := decisionInput.WAFdata + transactionID := decisionInput.TransactionId + + logger.TPrintf(lg.WARN, transactionID, "[test:CheckResults]\n modelRes: %v\n modelWeight: %v\n modelThres: %v\n wafData: %v\n", modelRes, modelWeight, wafData) + + if len(wafData) != 0 { + as, err := strconv.Atoi(wafData["anomalyscore"]) + if err != nil { + return false, err + } + it, err := strconv.Atoi(wafData["inboundthreshold"]) + if err != nil { + return false, err + } + if as >= it { // modsec wants to block + return true, nil + } + } + return false, nil +} diff --git a/testdata/plugins/decision/weighted_sum.go b/testdata/plugins/decision/weighted_sum.go new file mode 100644 index 0000000..a8ea7a5 --- /dev/null +++ b/testdata/plugins/decision/weighted_sum.go @@ -0,0 +1,90 @@ +// Decision Plugin that uses weighted sum algorithm to decide if a transaction should be blocked + +package main + +import ( + "context" + "fmt" + "strconv" + + lg "github.com/tilsor/ModSecIntl_logging/logging" + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +var wafWeight float64 +var threshold float64 + +func InitPlugin(params map[string]string, meter metric.Meter) error { + stringWafWeight, ok := params["waf_weight"] + if !ok { + return fmt.Errorf("waf_weight parameter not found") + } + var err error + wafWeight, err = strconv.ParseFloat(stringWafWeight, 64) + if err != nil { + return fmt.Errorf("error parsing waf_weight parameter: %v", err) + } + + stringThreshold, ok := params["threshold"] + if !ok { + threshold = 0.5 + } else { + threshold, err = strconv.ParseFloat(stringThreshold, 64) + if err != nil { + return fmt.Errorf("error parsing threshold parameter: %v", err) + } + } + + // Create counter for plugin register + ctx := context.Background() + pluginCounter, err := meter.Int64Counter("plugin_register") + if err != nil { + return err + } + pluginCounter.Add(ctx, 1, metric.WithAttributes(attribute.String("plugin_name", "weighted_sum"), attribute.String("plugin_type", "decision"))) + return nil +} + +func CheckResults(decisionInput pm.DecisionInput) (bool, error) { + var weightedSum float64 = 0 + var weightsSum float64 = 0 + for key, value := range decisionInput.Results { + weightedSum += value.ProbAttack * decisionInput.ModelWeight[key] + weightsSum += decisionInput.ModelWeight[key] + } + + stringInboundBlocking, ok := decisionInput.WAFdata["inbound_blocking"] + if !ok { + return false, fmt.Errorf("inbound_blocking parameter not found") + } + stringInboundThreshold, ok := decisionInput.WAFdata["inbound_threshold"] + if !ok { + return false, fmt.Errorf("inbound_threshold parameter not found") + } + + as, err := strconv.ParseFloat(stringInboundBlocking, 64) + if err != nil { + return false, fmt.Errorf("error parsing anomaly score: %v", err) + } + it, err := strconv.ParseFloat(stringInboundThreshold, 64) + if err != nil { + return false, fmt.Errorf("error parsing anomaly score threshold: %v", err) + } + + logger := lg.Get() + logger.TPrintf(lg.DEBUG, decisionInput.TransactionId, "weighted_sum | anomaly score: %v anomaly score threshold: %v", as, it) + + if as >= it { + weightedSum += wafWeight + } else { + weightedSum += (as / it) * wafWeight + } + weightsSum += wafWeight + + weightedSum /= weightsSum + + logger.TPrintf(lg.DEBUG, decisionInput.TransactionId, "weighted_sum | weighted sum: %v threshold: %v", weightedSum, threshold) + return weightedSum > threshold, nil +} diff --git a/testdata/plugins/decision/wrong_check.go b/testdata/plugins/decision/wrong_check.go new file mode 100644 index 0000000..d5f897b --- /dev/null +++ b/testdata/plugins/decision/wrong_check.go @@ -0,0 +1,21 @@ +/* Trivial Decision Plugin that always returns no attack + */ + +package main + +import ( + lg "github.com/tilsor/ModSecIntl_logging/logging" +) + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string) error { + logger := lg.Get() + logger.Printf(lg.WARN, "[simple:InitPlugin] %v\n", params) + return nil +} + +// CheckResults returns true (block traffic) if WAF says so, and false +// in other case. +func CheckResults() (bool, error) { + return false, nil +} diff --git a/testdata/plugins/model/error_init.go b/testdata/plugins/model/error_init.go new file mode 100644 index 0000000..a10211d --- /dev/null +++ b/testdata/plugins/model/error_init.go @@ -0,0 +1,25 @@ +/* Error Init model plugin that raises an error in InitPlugin + */ + +package main + +import ( + "errors" + + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/metric" +) + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string, meter metric.Meter) error { + return errors.New("Some error") +} + +// Process always returns 0 probability of attack +func Process(input pm.ModelInput) (pm.ModelResults, error) { + result := pm.ModelResults{ + ProbAttack: 0.0, + Data: make(map[string]interface{}), + } + return result, errors.New("Some error") +} diff --git a/testdata/plugins/model/error_req.go b/testdata/plugins/model/error_req.go new file mode 100644 index 0000000..c3a0ffd --- /dev/null +++ b/testdata/plugins/model/error_req.go @@ -0,0 +1,25 @@ +/* Error Init model plugin that raises an error in Process + */ + +package main + +import ( + "errors" + + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/metric" +) + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string, meter metric.Meter) error { + return nil +} + +// Process always returns 0 probability of attack +func Process(input pm.ModelInput) (pm.ModelResults, error) { + result := pm.ModelResults{ + ProbAttack: 0.0, + Data: make(map[string]interface{}), + } + return result, errors.New("Some error") +} diff --git a/testdata/plugins/model/no_init.go b/testdata/plugins/model/no_init.go new file mode 100644 index 0000000..b3c7c35 --- /dev/null +++ b/testdata/plugins/model/no_init.go @@ -0,0 +1,15 @@ +/* No Init model plugin that has no InitPlugin function + */ + +package main + +import pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + +// Process always returns 0 probability of attack +func Process(input pm.ModelInput) (pm.ModelResults, error) { + result := pm.ModelResults{ + ProbAttack: 0.0, + Data: make(map[string]interface{}), + } + return result, nil +} diff --git a/testdata/plugins/model/no_req.go b/testdata/plugins/model/no_req.go new file mode 100644 index 0000000..86953f7 --- /dev/null +++ b/testdata/plugins/model/no_req.go @@ -0,0 +1,10 @@ +/* No Init model plugin that has no InitPlugin function + */ + +package main + +import "go.opentelemetry.io/otel/metric" + +func InitPlugin(params map[string]string, meter metric.Meter) error { + return nil +} diff --git a/testdata/plugins/model/trivial.go b/testdata/plugins/model/trivial.go new file mode 100644 index 0000000..228b75c --- /dev/null +++ b/testdata/plugins/model/trivial.go @@ -0,0 +1,43 @@ +/* Trivial Model Plugin that always returns 0 probability of attack + */ + +package main + +import ( + "context" + + lg "github.com/tilsor/ModSecIntl_logging/logging" + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string, meter metric.Meter) error { + logger := lg.Get() + logger.Printf(lg.WARN, "[trivial:InitPlugin] %v\n", params) + // Create counter for plugin register + ctx := context.Background() + pluginCounter, err := meter.Int64Counter("plugin_register") + if err != nil { + return err + } + pluginCounter.Add(ctx, 1, metric.WithAttributes(attribute.String("plugin_name", "trivial"), attribute.String("plugin_type", "model"))) + return nil +} + +func InitPluginAsync(params map[string]string, meter metric.Meter, natsManager func(func(pm.ModelInput) (pm.ModelResults, error))) error { + InitPlugin(params, meter) + natsManager(Process) + return nil +} + +func Process(input pm.ModelInput) (pm.ModelResults, error) { + logger := lg.Get() + logger.TPrintf(lg.WARN, input.TransactionId, "[trivial:Process] \"%s\"\n", input.Payload) + result := pm.ModelResults{ + ProbAttack: 0.0, + Data: make(map[string]interface{}), + } + return result, nil +} diff --git a/testdata/plugins/model/trivial2.go b/testdata/plugins/model/trivial2.go new file mode 100644 index 0000000..f04218c --- /dev/null +++ b/testdata/plugins/model/trivial2.go @@ -0,0 +1,43 @@ +/* Trivial Model Plugin that always returns 0 probability of attack + */ + +package main + +import ( + "context" + + lg "github.com/tilsor/ModSecIntl_logging/logging" + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string, meter metric.Meter) error { + logger := lg.Get() + logger.Printf(lg.WARN, "[trivial2:InitPlugin] %v\n", params) + // Create counter for plugin register + ctx := context.Background() + pluginCounter, err := meter.Int64Counter("plugin_register") + if err != nil { + return err + } + pluginCounter.Add(ctx, 1, metric.WithAttributes(attribute.String("plugin_name", "trivial2"), attribute.String("plugin_type", "model"))) + return nil +} + +func InitPluginAsync(params map[string]string, meter metric.Meter, natsManager func(func(pm.ModelInput) (pm.ModelResults, error))) error { + InitPlugin(params, meter) + natsManager(Process) + return nil +} + +func Process(input pm.ModelInput) (pm.ModelResults, error) { + logger := lg.Get() + logger.TPrintf(lg.WARN, input.TransactionId, "[trivial2:Proccess] \"%s\"\n", input.Payload) + result := pm.ModelResults{ + ProbAttack: 1.0, + Data: make(map[string]interface{}), + } + return result, nil +} diff --git a/testdata/plugins/model/trivial_async.go b/testdata/plugins/model/trivial_async.go new file mode 100644 index 0000000..3896fbc --- /dev/null +++ b/testdata/plugins/model/trivial_async.go @@ -0,0 +1,58 @@ +/* Trivial Async Model Plugin that always returns 0 probability of attack and sleeps for a given time, default 1 second + */ + +package main + +import ( + "context" + "fmt" + "strconv" + "time" + + lg "github.com/tilsor/ModSecIntl_logging/logging" + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +var sleepTime float64 + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string, meter metric.Meter) error { + logger := lg.Get() + logger.Printf(lg.WARN, "[trivial_async:InitPlugin] %v\n", params) + stringSleepTime, ok := params["sleep_time"] + if !ok { + sleepTime = 1.0 + } else { + var err error + sleepTime, err = strconv.ParseFloat(stringSleepTime, 64) + if err != nil { + return fmt.Errorf("error parsing sleep_time parameter: %v", err) + } + } + ctx := context.Background() + pluginCounter, err := meter.Int64Counter("plugin_register") + if err != nil { + return err + } + pluginCounter.Add(ctx, 1, metric.WithAttributes(attribute.String("plugin_name", "trivial_async"), attribute.String("plugin_type", "model"))) + return nil +} + +func InitPluginAsync(params map[string]string, meter metric.Meter, natsManager func(func(pm.ModelInput) (pm.ModelResults, error))) error { + InitPlugin(params, meter) + natsManager(Process) + return nil +} + +func Process(input pm.ModelInput) (pm.ModelResults, error) { + time.Sleep(time.Duration(sleepTime) * time.Second) + logger := lg.Get() + logger.TPrintf(lg.WARN, input.TransactionId, "[trivial_async:Process] \"%s\"\n", input.Payload) + result := pm.ModelResults{ + ProbAttack: 0.0, + Data: make(map[string]interface{}), + } + return result, nil +} diff --git a/testdata/plugins/model/trivial_async2.go b/testdata/plugins/model/trivial_async2.go new file mode 100644 index 0000000..86c0243 --- /dev/null +++ b/testdata/plugins/model/trivial_async2.go @@ -0,0 +1,58 @@ +/* Trivial Async 2 Model Plugin that always returns 1 probability of attack and sleeps for a given time, default 1 second + */ + +package main + +import ( + "context" + "fmt" + "strconv" + "time" + + lg "github.com/tilsor/ModSecIntl_logging/logging" + pm "github.com/tilsor/ModSecIntl_wace_lib/pluginmanager" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +var sleepTime float64 + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string, meter metric.Meter) error { + logger := lg.Get() + logger.Printf(lg.WARN, "[trivial_async2:InitPlugin] %v\n", params) + stringSleepTime, ok := params["sleep_time"] + if !ok { + sleepTime = 1.0 + } else { + var err error + sleepTime, err = strconv.ParseFloat(stringSleepTime, 64) + if err != nil { + return fmt.Errorf("error parsing sleep_time parameter: %v", err) + } + } + ctx := context.Background() + pluginCounter, err := meter.Int64Counter("plugin_register") + if err != nil { + return err + } + pluginCounter.Add(ctx, 1, metric.WithAttributes(attribute.String("plugin_name", "trivial_async2"), attribute.String("plugin_type", "model"))) + return nil +} + +func InitPluginAsync(params map[string]string, meter metric.Meter, natsManager func(func(pm.ModelInput) (pm.ModelResults, error))) error { + InitPlugin(params, meter) + natsManager(Process) + return nil +} + +func Process(input pm.ModelInput) (pm.ModelResults, error) { + time.Sleep(time.Duration(sleepTime) * time.Second) + logger := lg.Get() + logger.TPrintf(lg.WARN, input.TransactionId, "[trivial_async2:Process] \"%s\"\n", input.Payload) + result := pm.ModelResults{ + ProbAttack: 1.0, + Data: make(map[string]interface{}), + } + return result, nil +} diff --git a/testdata/plugins/model/wrong_init.go b/testdata/plugins/model/wrong_init.go new file mode 100644 index 0000000..51937b7 --- /dev/null +++ b/testdata/plugins/model/wrong_init.go @@ -0,0 +1,14 @@ +/* Wrong Init model plugin that has wrong InitPlugin type + */ + +package main + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin() error { + return nil +} + +// Process always returns 0 probability of attack +func Process() (float64, error) { + return 0.0, nil +} diff --git a/testdata/plugins/model/wrong_req.go b/testdata/plugins/model/wrong_req.go new file mode 100644 index 0000000..7d83724 --- /dev/null +++ b/testdata/plugins/model/wrong_req.go @@ -0,0 +1,16 @@ +/* Wrong Init model plugin that has wrong InitPlugin type + */ + +package main + +import "go.opentelemetry.io/otel/metric" + +// InitPlugin intitalizes the plugins (does nothing in this case) +func InitPlugin(params map[string]string, meter metric.Meter) error { + return nil +} + +// Process always returns 0 probability of attack +func Process() (float64, error) { + return 0.0, nil +} diff --git a/wacecore_test.go b/wacecore_test.go index 8e385a2..1c2a34a 100644 --- a/wacecore_test.go +++ b/wacecore_test.go @@ -47,7 +47,7 @@ logpath: "/dev/null" loglevel: DEBUG modelplugins: - id: "trivial" - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 1 params: d: "sds" @@ -56,7 +56,7 @@ modelplugins: # plugintype: "RequestHeaders" plugintype: "Everything" - id: "trivial2" - path: "_plugins/model/trivial2.so" + path: "testdata/plugins/model/trivial2.so" weight: 2 params: a: "sdsds" @@ -65,7 +65,7 @@ modelplugins: plugintype: "Everything" decisionplugins: - id: "simple" - path: "_plugins/decision/simple.so" + path: "testdata/plugins/decision/simple.so" wafweight: 0.5 decisionbalance: 0.5 `) @@ -79,39 +79,39 @@ loglevel: "WARN" modelplugins: - id: "trivialRequestHeaders" plugintype: RequestHeaders - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 0.1 mode: sync - id: "trivialRequestBody" plugintype: RequestBody - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 0.1 mode: sync - id: "trivialAllRequest" plugintype: AllRequest - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 0.1 mode: sync - id: "trivialResponseHeaders" plugintype: ResponseHeaders - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 0.1 mode: sync - id: "trivialResponseBody" plugintype: ResponseBody - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 0.1 mode: sync - id: "trivialAllResponse" plugintype: AllResponse - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 0.1 mode: sync #The decision plugin configuration decisionplugins: - id: "simple" - path: "_plugins/decision/simple.so" + path: "testdata/plugins/decision/simple.so" # wafweight: 0.5 decisionbalance: 0.1 `) @@ -125,19 +125,19 @@ loglevel: "WARN" modelplugins: - id: "trivial" plugintype: RequestHeaders - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 1 mode: sync - id: "trivial2" plugintype: RequestHeaders - path: "_plugins/model/trivial2.so" + path: "testdata/plugins/model/trivial2.so" weight: 2 mode: sync #The decision plugin configuration decisionplugins: - id: "simple" - path: "_plugins/decision/simple.so" + path: "testdata/plugins/decision/simple.so" # wafweight: 0.5 decisionbalance: 0.1 `) @@ -151,20 +151,20 @@ loglevel: "WARN" modelplugins: - id: "trivial" plugintype: RequestHeaders - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 1 mode: sync remote: true - id: "trivial2" plugintype: RequestHeaders - path: "_plugins/model/trivial2.so" + path: "testdata/plugins/model/trivial2.so" weight: 2 mode: sync remote: true #The decision plugin configuration decisionplugins: - id: "simple" - path: "_plugins/decision/simple.so" + path: "testdata/plugins/decision/simple.so" # wafweight: 0.5 decisionbalance: 0.1 `) @@ -178,18 +178,18 @@ loglevel: "WARN" modelplugins: - id: "trivial" plugintype: RequestHeaders - path: "_plugins/model/trivial.so" + path: "testdata/plugins/model/trivial.so" weight: 1 mode: async - id: "trivial2" plugintype: RequestHeaders - path: "_plugins/model/trivial2.so" + path: "testdata/plugins/model/trivial2.so" weight: 2 mode: async #The decision plugin configuration decisionplugins: - id: "simple" - path: "_plugins/decision/simple.so" + path: "testdata/plugins/decision/simple.so" # wafweight: 0.5 decisionbalance: 0.1 `) @@ -200,7 +200,7 @@ decisionplugins: // listenport: "50051" // modelplugins: // - id: "trivial" -// path: "_plugins/model/trivial.so" +// path: "testdata/plugins/model/trivial.so" // weight: 1 // threshold: 0.5 // params: @@ -210,7 +210,7 @@ decisionplugins: // # plugintype: "RequestHeaders" // plugintype: "Everything" // - id: "trivial2" -// path: "_plugins/model/trivial2.so" +// path: "testdata/plugins/model/trivial2.so" // weight: 2 // threshold: 0.1 // params: @@ -219,7 +219,7 @@ decisionplugins: // c: "kfoskdofnno" // plugintype: "Everything" // - id: "roberta" -// path: "_plugins/model/roberta.so" +// path: "testdata/plugins/model/roberta.so" // weight: 1 // threshold: 0.5 // params: @@ -228,7 +228,7 @@ decisionplugins: // plugintype: "AllRequest" // decisionplugins: // - id: "simple" -// path: "_plugins/decision/simple.so" +// path: "testdata/plugins/decision/simple.so" // wafweight: 0.5 // decisionbalance: 0.5 // `) From 20e1e95790a8784e1bfe5c8007d38da8e45bfb4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20de=20Le=C3=B3n?= Date: Fri, 20 Feb 2026 16:10:29 -0300 Subject: [PATCH 2/2] test: add workflow for go tests --- .github/workflows/go.yaml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/go.yaml diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml new file mode 100644 index 0000000..a27b3ba --- /dev/null +++ b/.github/workflows/go.yaml @@ -0,0 +1,30 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.22.2' + + - name: Make + run: | + cd testdata/plugins + make + + - name: Test + run: go test -v ./...