package caddy import ( "strings" "testing" "github.com/stretchr/testify/require" "github.com/Wikid82/charon/backend/internal/models" ) // TestBuildWAFHandler_PathTraversalAttack tests path traversal attempts in ruleset names // WAF without rules returns nil, so malicious ruleset names that don't have paths should return nil. func TestBuildWAFHandler_PathTraversalAttack(t *testing.T) { tests := []struct { name string rulesetName string description string }{ { name: "Path traversal in ruleset name", rulesetName: "../../../etc/passwd", description: "Ruleset with path traversal should not match any legitimate path", }, { name: "Null byte injection", rulesetName: "rules\x00.conf", description: "Ruleset with null bytes should not match", }, { name: "URL encoded traversal", rulesetName: "..%2F..%2Fetc%2Fpasswd", description: "URL encoded path traversal should not match", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { host := &models.ProxyHost{UUID: "test-host"} rulesets := []models.SecurityRuleSet{{Name: tc.rulesetName}} // Only provide paths for legitimate rulesets rulesetPaths := map[string]string{ "owasp-crs": "/app/data/caddy/coraza/rulesets/owasp-crs.conf", } secCfg := &models.SecurityConfig{WAFMode: "block", WAFRulesSource: tc.rulesetName} handler, err := buildWAFHandler(host, rulesets, rulesetPaths, secCfg, true) require.NoError(t, err) // Handler should be nil since no matching path exists for malicious names require.Nil(t, handler, tc.description) }) } } // TestBuildWAFHandler_SQLInjectionInRulesetName tests SQL injection patterns in ruleset names // WAF without rules returns nil, so malicious patterns without paths should return nil. func TestBuildWAFHandler_SQLInjectionInRulesetName(t *testing.T) { sqlInjectionPatterns := []string{ "'; DROP TABLE rulesets; --", "1' OR '1'='1", "UNION SELECT * FROM users--", "admin'/*", } for _, pattern := range sqlInjectionPatterns { t.Run(pattern, func(t *testing.T) { host := &models.ProxyHost{UUID: "test-host"} // Create ruleset with malicious name but only provide path for safe ruleset rulesets := []models.SecurityRuleSet{{Name: pattern}, {Name: "owasp-crs"}} rulesetPaths := map[string]string{ "owasp-crs": "/app/data/caddy/coraza/rulesets/owasp-crs.conf", } secCfg := &models.SecurityConfig{WAFMode: "block", WAFRulesSource: pattern} handler, err := buildWAFHandler(host, rulesets, rulesetPaths, secCfg, true) require.NoError(t, err) // Should return nil since the malicious name has no corresponding path require.Nil(t, handler, "SQL injection pattern should not produce valid handler") }) } } // TestBuildWAFHandler_XSSInAdvancedConfig tests XSS patterns in advanced_config JSON func TestBuildWAFHandler_XSSInAdvancedConfig(t *testing.T) { xssPatterns := []string{ `{"ruleset_name":""}`, `{"ruleset_name":""}`, `{"ruleset_name":"javascript:alert(1)"}`, `{"ruleset_name":""}`, } for _, pattern := range xssPatterns { t.Run(pattern, func(t *testing.T) { host := &models.ProxyHost{ UUID: "test-host", AdvancedConfig: pattern, } rulesets := []models.SecurityRuleSet{{Name: "owasp-crs"}} rulesetPaths := map[string]string{ "owasp-crs": "/app/data/caddy/coraza/rulesets/owasp-crs.conf", } secCfg := &models.SecurityConfig{WAFMode: "block"} handler, err := buildWAFHandler(host, rulesets, rulesetPaths, secCfg, true) require.NoError(t, err) // Should fall back to owasp-crs since XSS pattern won't match any ruleset require.NotNil(t, handler) directives := handler["directives"].(string) require.Contains(t, directives, "owasp-crs") // Ensure XSS content is NOT in the output require.NotContains(t, directives, "