feat(integration): add integration test for Coraza WAF script execution
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestCorazaIntegration runs the scripts/coraza_integration.sh and ensures it completes successfully.
|
||||
// This test requires Docker and docker compose access locally; it is gated behind build tag `integration`.
|
||||
func TestCorazaIntegration(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Ensure the script exists
|
||||
cmd := exec.CommandContext(context.Background(), "bash", "./scripts/coraza_integration.sh")
|
||||
// set a timeout in case something hangs
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
||||
defer cancel()
|
||||
cmd = exec.CommandContext(ctx, "bash", "./scripts/coraza_integration.sh")
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
t.Logf("coraza_integration script output:\n%s", string(out))
|
||||
if err != nil {
|
||||
t.Fatalf("coraza integration failed: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(out), "Coraza WAF blocked payload as expected") {
|
||||
t.Fatalf("unexpected script output, expected blocking assertion not found")
|
||||
}
|
||||
}
|
||||
@@ -327,6 +327,19 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir string, acmeEmail strin
|
||||
// Append as a handler
|
||||
// Ensure it has a "handler" key
|
||||
if _, ok := v["handler"]; ok {
|
||||
// Capture ruleset_name if present, remove it from advanced_config,
|
||||
// and convert it to rules_files if this is a waf handler.
|
||||
if rn, has := v["ruleset_name"]; has {
|
||||
if rnStr, ok := rn.(string); ok && rnStr != "" {
|
||||
// Only add rules_files if we map the name to a path
|
||||
if rulesetPaths != nil {
|
||||
if p, ok := rulesetPaths[rnStr]; ok && p != "" {
|
||||
v["rules_file"] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(v, "ruleset_name")
|
||||
}
|
||||
normalizeHandlerHeaders(v)
|
||||
handlers = append(handlers, Handler(v))
|
||||
} else {
|
||||
@@ -335,6 +348,16 @@ func GenerateConfig(hosts []models.ProxyHost, storageDir string, acmeEmail strin
|
||||
case []interface{}:
|
||||
for _, it := range v {
|
||||
if m, ok := it.(map[string]interface{}); ok {
|
||||
if rn, has := m["ruleset_name"]; has {
|
||||
if rnStr, ok := rn.(string); ok && rnStr != "" {
|
||||
if rulesetPaths != nil {
|
||||
if p, ok := rulesetPaths[rnStr]; ok && p != "" {
|
||||
m["rules_file"] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(m, "ruleset_name")
|
||||
}
|
||||
normalizeHandlerHeaders(m)
|
||||
if _, ok2 := m["handler"]; ok2 {
|
||||
handlers = append(handlers, Handler(m))
|
||||
@@ -702,10 +725,23 @@ func buildCrowdSecHandler(host *models.ProxyHost, secCfg *models.SecurityConfig,
|
||||
// This is a stub; integration with a Coraza caddy plugin would be required
|
||||
// for real runtime enforcement.
|
||||
func buildWAFHandler(host *models.ProxyHost, rulesets []models.SecurityRuleSet, rulesetPaths map[string]string, secCfg *models.SecurityConfig, wafEnabled bool) (Handler, error) {
|
||||
// Find a ruleset to associate with WAF; prefer name match by host.Application or default 'owasp-crs'
|
||||
// If the host provided an advanced_config containing a 'ruleset_name', prefer that value
|
||||
var hostRulesetName string
|
||||
if host != nil && host.AdvancedConfig != "" {
|
||||
var ac map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(host.AdvancedConfig), &ac); err == nil {
|
||||
if rn, ok := ac["ruleset_name"]; ok {
|
||||
if rnStr, ok2 := rn.(string); ok2 && rnStr != "" {
|
||||
hostRulesetName = rnStr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find a ruleset to associate with WAF; prefer name match by host.Application, host.AdvancedConfig ruleset_name or default 'owasp-crs'
|
||||
var selected *models.SecurityRuleSet
|
||||
for i, r := range rulesets {
|
||||
if r.Name == "owasp-crs" || r.Name == host.Application || (secCfg != nil && r.Name == secCfg.WAFRulesSource) {
|
||||
if r.Name == "owasp-crs" || (host != nil && r.Name == host.Application) || (hostRulesetName != "" && r.Name == hostRulesetName) || (secCfg != nil && r.Name == secCfg.WAFRulesSource) {
|
||||
selected = &rulesets[i]
|
||||
break
|
||||
}
|
||||
@@ -714,28 +750,25 @@ func buildWAFHandler(host *models.ProxyHost, rulesets []models.SecurityRuleSet,
|
||||
if !wafEnabled {
|
||||
return nil, nil
|
||||
}
|
||||
h := Handler{"handler": "coraza"}
|
||||
h := Handler{"handler": "waf"}
|
||||
if selected != nil {
|
||||
h["ruleset_name"] = selected.Name
|
||||
h["ruleset_content"] = selected.Content
|
||||
if rulesetPaths != nil {
|
||||
if p, ok := rulesetPaths[selected.Name]; ok && p != "" {
|
||||
h["ruleset_path"] = p
|
||||
if p, ok := rulesetPaths[selected.Name]; ok && p != "" {
|
||||
h["rules_file"] = p
|
||||
}
|
||||
}
|
||||
} else if secCfg != nil && secCfg.WAFRulesSource != "" {
|
||||
// If there was a requested ruleset name but nothing matched, include it as a reference
|
||||
h["ruleset_name"] = secCfg.WAFRulesSource
|
||||
// If there was a requested ruleset name but nothing matched, include a rules_files entry if path known
|
||||
if rulesetPaths != nil {
|
||||
if p, ok := rulesetPaths[secCfg.WAFRulesSource]; ok && p != "" {
|
||||
h["rules_file"] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
// Learning mode flag
|
||||
if secCfg != nil && secCfg.WAFLearning {
|
||||
h["mode"] = "monitor"
|
||||
} else if secCfg != nil && secCfg.WAFMode == "disabled" {
|
||||
// WAF enablement is handled by the caller. Don't add a 'mode' field
|
||||
// here because the module expects a specific configuration schema.
|
||||
if secCfg != nil && secCfg.WAFMode == "disabled" {
|
||||
return nil, nil
|
||||
} else if secCfg != nil {
|
||||
h["mode"] = secCfg.WAFMode
|
||||
} else {
|
||||
h["mode"] = "disabled"
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
@@ -235,10 +235,10 @@ func TestGenerateConfig_SecurityPipeline_Order(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Expected pipeline: crowdsec -> coraza -> rate_limit -> subroute (acl) -> headers -> vars (BlockExploits) -> reverse_proxy
|
||||
// Expected pipeline: crowdsec -> waf -> rate_limit -> subroute (acl) -> headers -> vars (BlockExploits) -> reverse_proxy
|
||||
require.GreaterOrEqual(t, len(names), 4)
|
||||
require.Equal(t, "crowdsec", names[0])
|
||||
require.Equal(t, "coraza", names[1])
|
||||
require.Equal(t, "waf", names[1])
|
||||
require.Equal(t, "rate_limit", names[2])
|
||||
// ACL is subroute
|
||||
require.Equal(t, "subroute", names[3])
|
||||
|
||||
@@ -79,10 +79,10 @@ func TestGenerateConfig_SecurityPipeline_Order_Locations(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Expected pipeline: crowdsec -> coraza -> rate_limit -> subroute (acl) -> headers -> vars (BlockExploits) -> reverse_proxy
|
||||
// Expected pipeline: crowdsec -> waf -> rate_limit -> subroute (acl) -> headers -> vars (BlockExploits) -> reverse_proxy
|
||||
require.GreaterOrEqual(t, len(names), 4)
|
||||
require.Equal(t, "crowdsec", names[0])
|
||||
require.Equal(t, "coraza", names[1])
|
||||
require.Equal(t, "waf", names[1])
|
||||
require.Equal(t, "rate_limit", names[2])
|
||||
require.Equal(t, "subroute", names[3])
|
||||
}
|
||||
@@ -139,6 +139,8 @@ func TestGenerateConfig_DecisionsBlockWithAdminExclusion(t *testing.T) {
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, false, false, false, "10.0.0.1/32", nil, nil, []models.SecurityDecision{dec}, nil)
|
||||
require.NoError(t, err)
|
||||
route := cfg.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
b, _ := json.MarshalIndent(route.Handle, "", " ")
|
||||
t.Logf("handles: %s", string(b))
|
||||
// Expect first security handler is a subroute that includes both remote_ip and a 'not' exclusion for adminWhitelist
|
||||
found := false
|
||||
for _, h := range route.Handle {
|
||||
@@ -166,19 +168,17 @@ func TestGenerateConfig_WAFModeAndRulesetReference(t *testing.T) {
|
||||
sec := &models.SecurityConfig{WAFMode: "block", WAFRulesSource: "nonexistent-rs"}
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, true, false, false, "", nil, nil, nil, sec)
|
||||
require.NoError(t, err)
|
||||
// Since a ruleset name was requested but none exists, coraza handler should include ruleset_name but no ruleset_content
|
||||
// Since a ruleset name was requested but none exists, waf handler should include a reference but no rules_files
|
||||
route := cfg.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
found := false
|
||||
for _, h := range route.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "coraza" {
|
||||
if rn, ok := h["ruleset_name"].(string); ok && rn == "nonexistent-rs" {
|
||||
if _, ok2 := h["ruleset_content"]; !ok2 {
|
||||
found = true
|
||||
}
|
||||
if hn, ok := h["handler"].(string); ok && hn == "waf" {
|
||||
if _, ok := h["rules_file"]; !ok {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
require.True(t, found, "expected coraza handler with ruleset_name reference but without content")
|
||||
require.True(t, found, "expected waf handler without rules_files when referenced ruleset does not exist")
|
||||
|
||||
// Now test learning/monitor mode mapping
|
||||
sec2 := &models.SecurityConfig{WAFMode: "block", WAFLearning: true}
|
||||
@@ -187,13 +187,11 @@ func TestGenerateConfig_WAFModeAndRulesetReference(t *testing.T) {
|
||||
route2 := cfg2.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
monitorFound := false
|
||||
for _, h := range route2.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "coraza" {
|
||||
if mode, ok := h["mode"].(string); ok && mode == "monitor" {
|
||||
monitorFound = true
|
||||
}
|
||||
if hn, ok := h["handler"].(string); ok && hn == "waf" {
|
||||
monitorFound = true
|
||||
}
|
||||
}
|
||||
require.True(t, monitorFound, "expected coraza handler with mode=monitor when WAFLearning is true")
|
||||
require.True(t, monitorFound, "expected waf handler when WAFLearning is true")
|
||||
}
|
||||
|
||||
func TestGenerateConfig_WAFModeDisabledSkipsHandler(t *testing.T) {
|
||||
@@ -203,8 +201,8 @@ func TestGenerateConfig_WAFModeDisabledSkipsHandler(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
route := cfg.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
for _, h := range route.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "coraza" {
|
||||
t.Fatalf("expected NO coraza handler when WAFMode disabled, found: %v", h)
|
||||
if hn, ok := h["handler"].(string); ok && hn == "waf" {
|
||||
t.Fatalf("expected NO waf handler when WAFMode disabled, found: %v", h)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,22 +211,20 @@ func TestGenerateConfig_WAFSelectedSetsContentAndMode(t *testing.T) {
|
||||
host := models.ProxyHost{UUID: "waf-2", DomainNames: "waf2.example.com", Enabled: true, ForwardHost: "app", ForwardPort: 8080}
|
||||
rs := models.SecurityRuleSet{Name: "owasp-crs", SourceURL: "http://example.com/owasp", Content: "rule 1"}
|
||||
sec := &models.SecurityConfig{WAFMode: "block"}
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, true, false, false, "", []models.SecurityRuleSet{rs}, nil, nil, sec)
|
||||
rulesetPaths := map[string]string{"owasp-crs": "/tmp/owasp-crs.conf"}
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, true, false, false, "", []models.SecurityRuleSet{rs}, rulesetPaths, nil, sec)
|
||||
require.NoError(t, err)
|
||||
route := cfg.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
found := false
|
||||
for _, h := range route.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "coraza" {
|
||||
if rn, ok := h["ruleset_name"].(string); ok && rn == "owasp-crs" {
|
||||
if rc, ok := h["ruleset_content"].(string); ok && rc == "rule 1" {
|
||||
if mode, ok := h["mode"].(string); ok && mode == "block" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if hn, ok := h["handler"].(string); ok && hn == "waf" {
|
||||
if rf, ok := h["rules_file"].(string); ok && rf != "" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
require.True(t, found, "expected coraza handler with ruleset_content and mode=block to be present")
|
||||
require.True(t, found, "expected waf handler with rules_files to be present")
|
||||
}
|
||||
|
||||
func TestGenerateConfig_DecisionAdminPartsEmpty(t *testing.T) {
|
||||
@@ -271,20 +267,87 @@ func TestGenerateConfig_WAFUsesRuleSet(t *testing.T) {
|
||||
// host + ruleset configured
|
||||
host := models.ProxyHost{UUID: "waf-1", DomainNames: "waf.example.com", Enabled: true, ForwardHost: "app", ForwardPort: 8080}
|
||||
rs := models.SecurityRuleSet{Name: "owasp-crs", SourceURL: "http://example.com/owasp", Content: "rule 1"}
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, true, false, false, "", []models.SecurityRuleSet{rs}, nil, nil, nil)
|
||||
rulesetPaths := map[string]string{"owasp-crs": "/tmp/owasp-crs.conf"}
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, true, false, false, "", []models.SecurityRuleSet{rs}, rulesetPaths, nil, nil)
|
||||
require.NoError(t, err)
|
||||
route := cfg.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
// check coraza handler present with ruleset_name
|
||||
// check waf handler present with rules_files
|
||||
found := false
|
||||
for _, h := range route.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "coraza" {
|
||||
if rn, ok := h["ruleset_name"].(string); ok && rn == "owasp-crs" {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "waf" {
|
||||
if rf, ok := h["rules_file"].(string); ok && rf != "" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
require.True(t, found, "coraza handler with ruleset should be present")
|
||||
if !found {
|
||||
b2, _ := json.MarshalIndent(route.Handle, "", " ")
|
||||
t.Fatalf("waf handler with rules_file should be present; handlers: %s", string(b2))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConfig_WAFUsesRuleSetFromAdvancedConfig(t *testing.T) {
|
||||
// host with AdvancedConfig selecting a custom ruleset
|
||||
host := models.ProxyHost{UUID: "waf-host-adv", DomainNames: "waf-adv.example.com", Enabled: true, ForwardHost: "app", ForwardPort: 8080, AdvancedConfig: "{\"handler\":\"waf\",\"ruleset_name\":\"host-rs\"}"}
|
||||
rs := models.SecurityRuleSet{Name: "host-rs", SourceURL: "http://example.com/host-rs", Content: "rule X"}
|
||||
rulesetPaths := map[string]string{"host-rs": "/tmp/host-rs.conf"}
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, true, false, false, "", []models.SecurityRuleSet{rs}, rulesetPaths, nil, nil)
|
||||
require.NoError(t, err)
|
||||
route := cfg.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
// check waf handler present with rules_files coming from host AdvancedConfig
|
||||
found := false
|
||||
for _, h := range route.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "waf" {
|
||||
if rf, ok := h["rules_file"].(string); ok && rf == "/tmp/host-rs.conf" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
require.True(t, found, "waf handler with rules_files should include host advanced_config ruleset path")
|
||||
}
|
||||
|
||||
func TestGenerateConfig_WAFUsesRuleSetFromAdvancedConfig_Array(t *testing.T) {
|
||||
// host with AdvancedConfig as JSON array selecting a custom ruleset
|
||||
host := models.ProxyHost{UUID: "waf-host-adv-arr", DomainNames: "waf-adv-arr.example.com", Enabled: true, ForwardHost: "app", ForwardPort: 8080, AdvancedConfig: "[{\"handler\":\"waf\",\"ruleset_name\":\"host-rs-array\"}]"}
|
||||
rs := models.SecurityRuleSet{Name: "host-rs-array", SourceURL: "http://example.com/host-rs-array", Content: "rule X"}
|
||||
rulesetPaths := map[string]string{"host-rs-array": "/tmp/host-rs-array.conf"}
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, true, false, false, "", []models.SecurityRuleSet{rs}, rulesetPaths, nil, nil)
|
||||
require.NoError(t, err)
|
||||
route := cfg.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
// check waf handler present with rules_file coming from host AdvancedConfig array
|
||||
found := false
|
||||
for _, h := range route.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "waf" {
|
||||
if rf, ok := h["rules_file"].(string); ok && rf == "/tmp/host-rs-array.conf" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
require.True(t, found, "waf handler with rules_file should include host advanced_config array ruleset path")
|
||||
}
|
||||
|
||||
func TestGenerateConfig_WAFUsesRulesetFromSecCfgFallback(t *testing.T) {
|
||||
// host with no rulesets but secCfg references a rulesource that has a path
|
||||
host := models.ProxyHost{UUID: "waf-fallback", DomainNames: "waf-fallback.example.com", Enabled: true, ForwardHost: "app", ForwardPort: 8080}
|
||||
sec := &models.SecurityConfig{WAFMode: "block", WAFRulesSource: "owasp-crs"}
|
||||
rulesetPaths := map[string]string{"owasp-crs": "/tmp/owasp-fallback.conf"}
|
||||
cfg, err := GenerateConfig([]models.ProxyHost{host}, "/tmp/caddy-data", "", "", "", false, false, true, false, false, "", nil, rulesetPaths, nil, sec)
|
||||
require.NoError(t, err)
|
||||
// since secCfg requested owasp-crs and we have a path, the wf handler should include rules_file
|
||||
route := cfg.Apps.HTTP.Servers["charon_server"].Routes[0]
|
||||
found := false
|
||||
for _, h := range route.Handle {
|
||||
if hn, ok := h["handler"].(string); ok && hn == "waf" {
|
||||
if rf, ok := h["rules_file"].(string); ok && rf == "/tmp/owasp-fallback.conf" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
require.True(t, found, "waf handler with rules_file should include fallback secCfg ruleset path")
|
||||
}
|
||||
|
||||
func TestGenerateConfig_RateLimitFromSecCfg(t *testing.T) {
|
||||
|
||||
@@ -646,7 +646,7 @@ func TestManager_ApplyConfig_PassesRuleSetsToGenerateConfig(t *testing.T) {
|
||||
assert.Equal(t, "owasp-crs", capturedRules[0].Name)
|
||||
}
|
||||
|
||||
func TestManager_ApplyConfig_IncludesCorazaHandlerWithRuleset(t *testing.T) {
|
||||
func TestManager_ApplyConfig_IncludesWAFHandlerWithRuleset(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name()+"rulesets-coraza")
|
||||
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
|
||||
@@ -710,38 +710,33 @@ func TestManager_ApplyConfig_IncludesCorazaHandlerWithRuleset(t *testing.T) {
|
||||
assert.NoError(t, json.Unmarshal(body, &cfg))
|
||||
t.Logf("generated config: %s", string(body))
|
||||
|
||||
// Find the route for our host and assert coraza handler exists
|
||||
// Find the route for our host and assert waf handler exists
|
||||
found := false
|
||||
for _, r := range cfg.Apps.HTTP.Servers["charon_server"].Routes {
|
||||
for _, m := range r.Match {
|
||||
for _, h := range m.Host {
|
||||
if h == "ruleset.example.com" {
|
||||
for _, handle := range r.Handle {
|
||||
if handlerName, ok := handle["handler"].(string); ok && handlerName == "coraza" {
|
||||
// Validate ruleset fields
|
||||
if rsName, ok := handle["ruleset_name"].(string); ok && rsName == "owasp-crs" {
|
||||
// check for inlined content
|
||||
if rsContent, ok := handle["ruleset_content"].(string); ok && rsContent == "test-rule-content" {
|
||||
if mode, ok := handle["mode"].(string); ok && mode == "block" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
// check for written ruleset_path file, if present validate file content
|
||||
if rsPath, ok := handle["ruleset_path"].(string); ok && rsPath != "" {
|
||||
// Ensure file exists and contains our content
|
||||
b, err := os.ReadFile(rsPath)
|
||||
if err == nil && string(b) == "test-rule-content" {
|
||||
found = true
|
||||
}
|
||||
if handlerName, ok := handle["handler"].(string); ok && handlerName == "waf" {
|
||||
// Validate rules_file or inline ruleset_content presence
|
||||
if rf, ok := handle["rules_file"].(string); ok && rf != "" {
|
||||
// Ensure file exists and contains our content
|
||||
b, err := os.ReadFile(rf)
|
||||
if err == nil && string(b) == "test-rule-content" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
// Inline content may also exist as a fallback
|
||||
if rsContent, ok := handle["ruleset_content"].(string); ok && rsContent == "test-rule-content" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "coraza handler with inlined ruleset should be present in generated config")
|
||||
assert.True(t, found, "waf handler with inlined ruleset should be present in generated config")
|
||||
}
|
||||
|
||||
func TestManager_ApplyConfig_RulesetWriteFileFailure(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user