package caddy import ( "encoding/json" "strings" "testing" "github.com/Wikid82/charon/backend/internal/models" "github.com/stretchr/testify/require" ) func TestGenerateConfig_CustomCertsAndTLS(t *testing.T) { hosts := []models.ProxyHost{ { UUID: "h1", DomainNames: "a.example.com", ForwardHost: "127.0.0.1", ForwardPort: 8080, Enabled: true, Certificate: &models.SSLCertificate{ID: 1, UUID: "c1", Name: "CustomCert", Provider: "custom", Certificate: "cert", PrivateKey: "key"}, CertificateID: ptrUint(1), HSTSEnabled: true, HSTSSubdomains: true, BlockExploits: true, Locations: []models.Location{{Path: "/app", ForwardHost: "127.0.0.1", ForwardPort: 8081}}, }, } cfg, err := GenerateConfig(hosts, "/data/caddy/data", "admin@example.com", "/frontend/dist", "letsencrypt", true, false, false, false, false, "", nil, nil, nil, nil, nil) require.NoError(t, err) require.NotNil(t, cfg) // TLS should be configured require.NotNil(t, cfg.Apps.TLS) // Custom cert load require.NotNil(t, cfg.Apps.TLS.Certificates) // One route for the host (with location) plus catch-all -> at least 2 routes server := cfg.Apps.HTTP.Servers["charon_server"] require.GreaterOrEqual(t, len(server.Routes), 2) // Check HSTS header exists in JSON representation b, _ := json.Marshal(cfg) require.Contains(t, string(b), "Strict-Transport-Security") } func ptrUint(v uint) *uint { return &v } func TestGenerateConfig_EmergencyRoutesBypassSecurity(t *testing.T) { hosts := []models.ProxyHost{ { UUID: "h1", DomainNames: "example.com", ForwardHost: "127.0.0.1", ForwardPort: 8080, Enabled: true, AccessList: &models.AccessList{ Enabled: true, Type: "whitelist", IPRules: `[ { "cidr": "10.0.0.0/8", "description": "allow" } ]`, }, AccessListID: ptrUint(1), }, } secCfg := &models.SecurityConfig{ WAFMode: "enabled", WAFRulesSource: "owasp-crs", RateLimitMode: "enabled", RateLimitRequests: 10, RateLimitWindowSec: 60, } rulesets := []models.SecurityRuleSet{ {Name: "owasp-crs", Content: "SecRuleEngine On"}, } rulesetPaths := map[string]string{"owasp-crs": "/tmp/owasp-crs.conf"} cfg, err := GenerateConfig(hosts, "/data/caddy/data", "admin@example.com", "/frontend/dist", "letsencrypt", false, false, true, true, true, "", rulesets, rulesetPaths, nil, secCfg, nil) require.NoError(t, err) require.NotNil(t, cfg) server := cfg.Apps.HTTP.Servers["charon_server"] require.NotNil(t, server) var emergencyRoute *Route for _, route := range server.Routes { if route == nil { continue } for _, match := range route.Match { for _, path := range match.Path { if strings.Contains(path, "/api/v1/emergency") || strings.Contains(path, "/emergency/") { emergencyRoute = route break } } } } require.NotNil(t, emergencyRoute, "expected emergency bypass route") for _, handler := range emergencyRoute.Handle { name, _ := handler["handler"].(string) require.NotEqual(t, "rate_limit", name) require.NotEqual(t, "waf", name) require.NotEqual(t, "crowdsec", name) } } func TestApplyOptionalServerKeepalive_OmitsWhenUnset(t *testing.T) { cfg := &Config{ Apps: Apps{ HTTP: &HTTPApp{Servers: map[string]*Server{ "charon_server": { Listen: []string{":80", ":443"}, Routes: []*Route{}, }, }}, }, } applyOptionalServerKeepalive(cfg, "", 0) server := cfg.Apps.HTTP.Servers["charon_server"] require.Nil(t, server.KeepaliveIdle) require.Nil(t, server.KeepaliveCount) } func TestApplyOptionalServerKeepalive_AppliesValidValues(t *testing.T) { cfg := &Config{ Apps: Apps{ HTTP: &HTTPApp{Servers: map[string]*Server{ "charon_server": { Listen: []string{":80", ":443"}, Routes: []*Route{}, }, }}, }, } applyOptionalServerKeepalive(cfg, "45s", 7) server := cfg.Apps.HTTP.Servers["charon_server"] require.NotNil(t, server.KeepaliveIdle) require.Equal(t, "45s", *server.KeepaliveIdle) require.NotNil(t, server.KeepaliveCount) require.Equal(t, 7, *server.KeepaliveCount) }