- Added unit tests for CrowdSec handler, including listing, banning, and unbanning IPs. - Implemented mock command executor for testing command execution. - Created tests for various scenarios including successful operations, error handling, and invalid inputs. - Developed CrowdSec configuration tests to ensure proper handler setup and JSON output. - Documented security features and identified gaps in CrowdSec, WAF, and Rate Limiting implementations. - Established acceptance criteria for feature completeness and outlined implementation phases for future work.
165 lines
4.7 KiB
Go
165 lines
4.7 KiB
Go
package caddy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/Wikid82/charon/backend/internal/models"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestBuildCrowdSecHandler_Disabled(t *testing.T) {
|
|
// When crowdsecEnabled is false, should return nil
|
|
h, err := buildCrowdSecHandler(nil, nil, false)
|
|
require.NoError(t, err)
|
|
assert.Nil(t, h)
|
|
}
|
|
|
|
func TestBuildCrowdSecHandler_EnabledWithoutConfig(t *testing.T) {
|
|
// When crowdsecEnabled is true but no secCfg, should use default localhost URL
|
|
h, err := buildCrowdSecHandler(nil, nil, true)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, h)
|
|
|
|
assert.Equal(t, "crowdsec", h["handler"])
|
|
assert.Equal(t, "http://localhost:8080", h["api_url"])
|
|
}
|
|
|
|
func TestBuildCrowdSecHandler_EnabledWithEmptyAPIURL(t *testing.T) {
|
|
// When crowdsecEnabled is true but CrowdSecAPIURL is empty, should use default
|
|
secCfg := &models.SecurityConfig{
|
|
CrowdSecAPIURL: "",
|
|
}
|
|
h, err := buildCrowdSecHandler(nil, secCfg, true)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, h)
|
|
|
|
assert.Equal(t, "crowdsec", h["handler"])
|
|
assert.Equal(t, "http://localhost:8080", h["api_url"])
|
|
}
|
|
|
|
func TestBuildCrowdSecHandler_EnabledWithCustomAPIURL(t *testing.T) {
|
|
// When crowdsecEnabled is true and CrowdSecAPIURL is set, should use custom URL
|
|
secCfg := &models.SecurityConfig{
|
|
CrowdSecAPIURL: "http://crowdsec-lapi:8081",
|
|
}
|
|
h, err := buildCrowdSecHandler(nil, secCfg, true)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, h)
|
|
|
|
assert.Equal(t, "crowdsec", h["handler"])
|
|
assert.Equal(t, "http://crowdsec-lapi:8081", h["api_url"])
|
|
}
|
|
|
|
func TestBuildCrowdSecHandler_JSONFormat(t *testing.T) {
|
|
// Test that the handler produces valid JSON matching caddy-crowdsec-bouncer schema
|
|
secCfg := &models.SecurityConfig{
|
|
CrowdSecAPIURL: "http://localhost:8080",
|
|
}
|
|
h, err := buildCrowdSecHandler(nil, secCfg, true)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, h)
|
|
|
|
// Marshal to JSON and verify structure
|
|
b, err := json.Marshal(h)
|
|
require.NoError(t, err)
|
|
s := string(b)
|
|
|
|
// Verify expected JSON content
|
|
assert.Contains(t, s, `"handler":"crowdsec"`)
|
|
assert.Contains(t, s, `"api_url":"http://localhost:8080"`)
|
|
// Should NOT contain old "mode" field
|
|
assert.NotContains(t, s, `"mode"`)
|
|
}
|
|
|
|
func TestBuildCrowdSecHandler_WithHost(t *testing.T) {
|
|
// Test that host parameter is accepted (even if not currently used)
|
|
host := &models.ProxyHost{
|
|
UUID: "test-uuid",
|
|
DomainNames: "example.com",
|
|
}
|
|
secCfg := &models.SecurityConfig{
|
|
CrowdSecAPIURL: "http://custom-crowdsec:8080",
|
|
}
|
|
|
|
h, err := buildCrowdSecHandler(host, secCfg, true)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, h)
|
|
|
|
assert.Equal(t, "crowdsec", h["handler"])
|
|
assert.Equal(t, "http://custom-crowdsec:8080", h["api_url"])
|
|
}
|
|
|
|
func TestGenerateConfig_WithCrowdSec(t *testing.T) {
|
|
// Test that CrowdSec handler is included in generated config when enabled
|
|
hosts := []models.ProxyHost{
|
|
{
|
|
UUID: "test-uuid",
|
|
DomainNames: "example.com",
|
|
ForwardHost: "app",
|
|
ForwardPort: 8080,
|
|
Enabled: true,
|
|
},
|
|
}
|
|
|
|
secCfg := &models.SecurityConfig{
|
|
CrowdSecMode: "local",
|
|
CrowdSecAPIURL: "http://localhost:8080",
|
|
}
|
|
|
|
// crowdsecEnabled=true should include the handler
|
|
config, err := GenerateConfig(hosts, "/tmp/caddy-data", "admin@example.com", "", "", false, true, false, false, false, "", nil, nil, nil, secCfg)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config.Apps.HTTP)
|
|
|
|
server := config.Apps.HTTP.Servers["charon_server"]
|
|
require.NotNil(t, server)
|
|
require.Len(t, server.Routes, 1)
|
|
|
|
route := server.Routes[0]
|
|
// Handlers should include crowdsec + reverse_proxy
|
|
require.GreaterOrEqual(t, len(route.Handle), 2)
|
|
|
|
// Find the crowdsec handler
|
|
var foundCrowdSec bool
|
|
for _, h := range route.Handle {
|
|
if h["handler"] == "crowdsec" {
|
|
foundCrowdSec = true
|
|
// Verify it has api_url
|
|
assert.Equal(t, "http://localhost:8080", h["api_url"])
|
|
break
|
|
}
|
|
}
|
|
require.True(t, foundCrowdSec, "crowdsec handler should be present")
|
|
}
|
|
|
|
func TestGenerateConfig_CrowdSecDisabled(t *testing.T) {
|
|
// Test that CrowdSec handler is NOT included when disabled
|
|
hosts := []models.ProxyHost{
|
|
{
|
|
UUID: "test-uuid",
|
|
DomainNames: "example.com",
|
|
ForwardHost: "app",
|
|
ForwardPort: 8080,
|
|
Enabled: true,
|
|
},
|
|
}
|
|
|
|
// crowdsecEnabled=false should NOT include the handler
|
|
config, err := GenerateConfig(hosts, "/tmp/caddy-data", "admin@example.com", "", "", false, false, false, false, false, "", nil, nil, nil, nil)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, config.Apps.HTTP)
|
|
|
|
server := config.Apps.HTTP.Servers["charon_server"]
|
|
require.NotNil(t, server)
|
|
require.Len(t, server.Routes, 1)
|
|
|
|
route := server.Routes[0]
|
|
|
|
// Verify no crowdsec handler
|
|
for _, h := range route.Handle {
|
|
assert.NotEqual(t, "crowdsec", h["handler"], "crowdsec handler should not be present when disabled")
|
|
}
|
|
}
|