chore: clean .gitignore cache
This commit is contained in:
@@ -1,276 +0,0 @@
|
||||
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":"<script>alert(1)</script>"}`,
|
||||
`{"ruleset_name":"<img src=x onerror=alert(1)>"}`,
|
||||
`{"ruleset_name":"javascript:alert(1)"}`,
|
||||
`{"ruleset_name":"<svg/onload=alert(1)>"}`,
|
||||
}
|
||||
|
||||
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, "<script>")
|
||||
require.NotContains(t, directives, "javascript:")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuildWAFHandler_HugePayload tests handling of very large inputs
|
||||
func TestBuildWAFHandler_HugePayload(t *testing.T) {
|
||||
// Create a very large ruleset name (1MB)
|
||||
hugeName := strings.Repeat("A", 1024*1024)
|
||||
|
||||
host := &models.ProxyHost{UUID: "test-host"}
|
||||
rulesets := []models.SecurityRuleSet{{Name: hugeName}, {Name: "owasp-crs"}}
|
||||
rulesetPaths := map[string]string{
|
||||
"owasp-crs": "/app/data/caddy/coraza/rulesets/owasp-crs.conf",
|
||||
}
|
||||
secCfg := &models.SecurityConfig{WAFMode: "block"}
|
||||
|
||||
// Should not panic or crash
|
||||
handler, err := buildWAFHandler(host, rulesets, rulesetPaths, secCfg, true)
|
||||
require.NoError(t, err)
|
||||
// Falls back to owasp-crs since huge name has no path
|
||||
require.NotNil(t, handler)
|
||||
directives := handler["directives"].(string)
|
||||
require.Contains(t, directives, "owasp-crs")
|
||||
}
|
||||
|
||||
// TestBuildWAFHandler_EmptyAndWhitespaceInputs tests boundary conditions
|
||||
func TestBuildWAFHandler_EmptyAndWhitespaceInputs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rulesetName string
|
||||
wafRulesSource string
|
||||
expectNil bool
|
||||
}{
|
||||
{
|
||||
name: "Empty string WAFRulesSource",
|
||||
rulesetName: "owasp-crs",
|
||||
wafRulesSource: "",
|
||||
expectNil: false, // Falls back to owasp-crs
|
||||
},
|
||||
{
|
||||
name: "Whitespace-only WAFRulesSource",
|
||||
rulesetName: "owasp-crs",
|
||||
wafRulesSource: " ",
|
||||
expectNil: false, // Falls back to owasp-crs (whitespace doesn't match, but fallback exists)
|
||||
},
|
||||
{
|
||||
name: "Tab and newline in WAFRulesSource",
|
||||
rulesetName: "owasp-crs",
|
||||
wafRulesSource: "\t\n",
|
||||
expectNil: false, // Falls back to owasp-crs (special chars don't match, but fallback exists)
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
host := &models.ProxyHost{UUID: "test-host"}
|
||||
rulesets := []models.SecurityRuleSet{{Name: tc.rulesetName}}
|
||||
rulesetPaths := map[string]string{
|
||||
tc.rulesetName: "/app/data/caddy/coraza/rulesets/" + tc.rulesetName + ".conf",
|
||||
}
|
||||
secCfg := &models.SecurityConfig{WAFMode: "block", WAFRulesSource: tc.wafRulesSource}
|
||||
|
||||
handler, err := buildWAFHandler(host, rulesets, rulesetPaths, secCfg, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.expectNil {
|
||||
require.Nil(t, handler)
|
||||
} else {
|
||||
require.NotNil(t, handler)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuildWAFHandler_ConcurrentRulesetSelection tests that selection is deterministic
|
||||
func TestBuildWAFHandler_ConcurrentRulesetSelection(t *testing.T) {
|
||||
host := &models.ProxyHost{UUID: "test-host"}
|
||||
rulesets := []models.SecurityRuleSet{
|
||||
{Name: "ruleset-a"},
|
||||
{Name: "ruleset-b"},
|
||||
{Name: "ruleset-c"},
|
||||
{Name: "owasp-crs"},
|
||||
}
|
||||
rulesetPaths := map[string]string{
|
||||
"ruleset-a": "/path/ruleset-a.conf",
|
||||
"ruleset-b": "/path/ruleset-b.conf",
|
||||
"ruleset-c": "/path/ruleset-c.conf",
|
||||
"owasp-crs": "/path/owasp.conf",
|
||||
}
|
||||
secCfg := &models.SecurityConfig{WAFMode: "block", WAFRulesSource: "ruleset-b"}
|
||||
|
||||
// Run 100 times to verify determinism
|
||||
for i := 0; i < 100; i++ {
|
||||
handler, err := buildWAFHandler(host, rulesets, rulesetPaths, secCfg, true)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, handler)
|
||||
directives := handler["directives"].(string)
|
||||
require.Contains(t, directives, "ruleset-b", "Selection should always pick WAFRulesSource")
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuildWAFHandler_NilSecCfg tests handling when secCfg is nil
|
||||
func TestBuildWAFHandler_NilSecCfg(t *testing.T) {
|
||||
host := &models.ProxyHost{UUID: "test-host"}
|
||||
rulesets := []models.SecurityRuleSet{{Name: "owasp-crs"}}
|
||||
rulesetPaths := map[string]string{
|
||||
"owasp-crs": "/app/data/caddy/coraza/rulesets/owasp-crs.conf",
|
||||
}
|
||||
|
||||
// nil secCfg should not panic, should fall back to owasp-crs
|
||||
handler, err := buildWAFHandler(host, rulesets, rulesetPaths, nil, true)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, handler)
|
||||
directives := handler["directives"].(string)
|
||||
require.Contains(t, directives, "owasp-crs")
|
||||
}
|
||||
|
||||
// TestBuildWAFHandler_NilHost tests handling when host is nil
|
||||
func TestBuildWAFHandler_NilHost(t *testing.T) {
|
||||
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"}
|
||||
|
||||
// nil host should not panic
|
||||
handler, err := buildWAFHandler(nil, rulesets, rulesetPaths, secCfg, true)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, handler)
|
||||
directives := handler["directives"].(string)
|
||||
require.Contains(t, directives, "owasp-crs")
|
||||
}
|
||||
|
||||
// TestBuildWAFHandler_SpecialCharactersInRulesetName tests handling of special chars
|
||||
func TestBuildWAFHandler_SpecialCharactersInRulesetName(t *testing.T) {
|
||||
specialNames := []struct {
|
||||
name string
|
||||
safeName string
|
||||
}{
|
||||
{"ruleset with spaces", "ruleset-with-spaces"},
|
||||
{"ruleset/with/slashes", "ruleset-with-slashes"},
|
||||
{"UPPERCASE-RULESET", "uppercase-ruleset"},
|
||||
{"ruleset_with_underscores", "ruleset_with_underscores"},
|
||||
{"ruleset.with.dots", "ruleset.with.dots"},
|
||||
}
|
||||
|
||||
for _, tc := range specialNames {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
host := &models.ProxyHost{UUID: "test-host"}
|
||||
rulesets := []models.SecurityRuleSet{{Name: tc.name}}
|
||||
// Simulate path that would be generated by manager.go
|
||||
rulesetPaths := map[string]string{
|
||||
tc.name: "/app/data/caddy/coraza/rulesets/" + tc.safeName + "-abc123.conf",
|
||||
}
|
||||
secCfg := &models.SecurityConfig{WAFMode: "block", WAFRulesSource: tc.name}
|
||||
|
||||
handler, err := buildWAFHandler(host, rulesets, rulesetPaths, secCfg, true)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, handler)
|
||||
directives := handler["directives"].(string)
|
||||
require.Contains(t, directives, tc.safeName)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user