- Implemented `useManualChallenge`, `useChallengePoll`, and `useManualChallengeMutations` hooks for managing manual DNS challenges. - Created tests for the `useManualChallenge` hooks to ensure correct fetching and mutation behavior. - Added `ManualDNSChallenge` component for displaying challenge details and actions. - Developed end-to-end tests for the Manual DNS Provider feature, covering provider selection, challenge UI, and accessibility compliance. - Included error handling tests for verification failures and network errors.
692 lines
22 KiB
Go
692 lines
22 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/Wikid82/charon/backend/internal/config"
|
|
"github.com/Wikid82/charon/backend/internal/models"
|
|
)
|
|
|
|
// Tests for GetWAFExclusions handler
|
|
func TestSecurityHandler_GetWAFExclusions_Empty(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.GET("/security/waf/exclusions", handler.GetWAFExclusions)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var resp map[string]any
|
|
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
exclusions := resp["exclusions"].([]any)
|
|
assert.Len(t, exclusions, 0)
|
|
}
|
|
|
|
func TestSecurityHandler_GetWAFExclusions_WithExclusions(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
// Create config with exclusions
|
|
exclusionsJSON := `[{"rule_id":942100,"description":"SQL Injection rule"},{"rule_id":941100,"target":"ARGS:password"}]`
|
|
cfg := models.SecurityConfig{Name: "default", WAFExclusions: exclusionsJSON}
|
|
db.Create(&cfg)
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.GET("/security/waf/exclusions", handler.GetWAFExclusions)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var resp map[string]any
|
|
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
exclusions := resp["exclusions"].([]any)
|
|
assert.Len(t, exclusions, 2)
|
|
|
|
// Verify first exclusion
|
|
first := exclusions[0].(map[string]any)
|
|
assert.Equal(t, float64(942100), first["rule_id"])
|
|
assert.Equal(t, "SQL Injection rule", first["description"])
|
|
}
|
|
|
|
func TestSecurityHandler_GetWAFExclusions_InvalidJSON(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
// Create config with invalid JSON
|
|
cfg := models.SecurityConfig{Name: "default", WAFExclusions: "invalid json"}
|
|
db.Create(&cfg)
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.GET("/security/waf/exclusions", handler.GetWAFExclusions)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Should return empty array on parse failure
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var resp map[string]any
|
|
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
exclusions := resp["exclusions"].([]any)
|
|
assert.Len(t, exclusions, 0)
|
|
}
|
|
|
|
// Tests for AddWAFExclusion handler
|
|
func TestSecurityHandler_AddWAFExclusion_Success(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityAudit{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
|
|
payload := map[string]any{
|
|
"rule_id": 942100,
|
|
"description": "SQL Injection false positive",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var resp map[string]any
|
|
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
|
|
exclusion := resp["exclusion"].(map[string]any)
|
|
assert.Equal(t, float64(942100), exclusion["rule_id"])
|
|
assert.Equal(t, "SQL Injection false positive", exclusion["description"])
|
|
}
|
|
|
|
func TestSecurityHandler_AddWAFExclusion_WithTarget(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityAudit{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
|
|
payload := map[string]any{
|
|
"rule_id": 942100,
|
|
"target": "ARGS:password",
|
|
"description": "Skip password field for SQL injection",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var resp map[string]any
|
|
err := json.Unmarshal(w.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
|
|
exclusion := resp["exclusion"].(map[string]any)
|
|
assert.Equal(t, "ARGS:password", exclusion["target"])
|
|
}
|
|
|
|
func TestSecurityHandler_AddWAFExclusion_ToExistingConfig(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityAudit{}))
|
|
|
|
// Create config with existing exclusion
|
|
existingExclusions := `[{"rule_id":941100,"description":"XSS rule"}]`
|
|
cfg := models.SecurityConfig{Name: "default", WAFExclusions: existingExclusions}
|
|
db.Create(&cfg)
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
router.GET("/security/waf/exclusions", handler.GetWAFExclusions)
|
|
|
|
// Add new exclusion
|
|
payload := map[string]any{
|
|
"rule_id": 942100,
|
|
"description": "SQL Injection rule",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Verify both exclusions exist
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
var resp map[string]any
|
|
_ = json.Unmarshal(w.Body.Bytes(), &resp)
|
|
exclusions := resp["exclusions"].([]any)
|
|
assert.Len(t, exclusions, 2)
|
|
}
|
|
|
|
func TestSecurityHandler_AddWAFExclusion_Duplicate(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityAudit{}))
|
|
|
|
// Create config with existing exclusion
|
|
existingExclusions := `[{"rule_id":942100,"description":"SQL Injection rule"}]`
|
|
cfg := models.SecurityConfig{Name: "default", WAFExclusions: existingExclusions}
|
|
db.Create(&cfg)
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
|
|
// Try to add duplicate
|
|
payload := map[string]any{
|
|
"rule_id": 942100,
|
|
"description": "Another description",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusConflict, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_AddWAFExclusion_DuplicateWithDifferentTarget(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityAudit{}))
|
|
|
|
// Create config with existing exclusion (no target)
|
|
existingExclusions := `[{"rule_id":942100}]`
|
|
cfg := models.SecurityConfig{Name: "default", WAFExclusions: existingExclusions}
|
|
db.Create(&cfg)
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
|
|
// Add same rule_id with different target - should succeed
|
|
payload := map[string]any{
|
|
"rule_id": 942100,
|
|
"target": "ARGS:password",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_AddWAFExclusion_MissingRuleID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
|
|
payload := map[string]any{
|
|
"description": "Missing rule_id",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_AddWAFExclusion_InvalidRuleID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
|
|
// Zero rule_id
|
|
payload := map[string]any{
|
|
"rule_id": 0,
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_AddWAFExclusion_NegativeRuleID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
|
|
payload := map[string]any{
|
|
"rule_id": -1,
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_AddWAFExclusion_InvalidPayload(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("POST", "/security/waf/exclusions", strings.NewReader("invalid json"))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// Tests for DeleteWAFExclusion handler
|
|
func TestSecurityHandler_DeleteWAFExclusion_Success(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityAudit{}))
|
|
|
|
// Create config with exclusions
|
|
exclusionsJSON := `[{"rule_id":942100},{"rule_id":941100}]`
|
|
cfg := models.SecurityConfig{Name: "default", WAFExclusions: exclusionsJSON}
|
|
db.Create(&cfg)
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.DELETE("/security/waf/exclusions/:rule_id", handler.DeleteWAFExclusion)
|
|
router.GET("/security/waf/exclusions", handler.GetWAFExclusions)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/security/waf/exclusions/942100", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var resp map[string]any
|
|
_ = json.Unmarshal(w.Body.Bytes(), &resp)
|
|
assert.True(t, resp["deleted"].(bool))
|
|
|
|
// Verify only one exclusion remains
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
_ = json.Unmarshal(w.Body.Bytes(), &resp)
|
|
exclusions := resp["exclusions"].([]any)
|
|
assert.Len(t, exclusions, 1)
|
|
first := exclusions[0].(map[string]any)
|
|
assert.Equal(t, float64(941100), first["rule_id"])
|
|
}
|
|
|
|
func TestSecurityHandler_DeleteWAFExclusion_WithTarget(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityAudit{}))
|
|
|
|
// Create config with targeted exclusion
|
|
exclusionsJSON := `[{"rule_id":942100,"target":"ARGS:password"},{"rule_id":942100}]`
|
|
cfg := models.SecurityConfig{Name: "default", WAFExclusions: exclusionsJSON}
|
|
db.Create(&cfg)
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.DELETE("/security/waf/exclusions/:rule_id", handler.DeleteWAFExclusion)
|
|
router.GET("/security/waf/exclusions", handler.GetWAFExclusions)
|
|
|
|
// Delete exclusion with target
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/security/waf/exclusions/942100?target=ARGS:password", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Verify only the non-targeted exclusion remains
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
var resp map[string]any
|
|
_ = json.Unmarshal(w.Body.Bytes(), &resp)
|
|
exclusions := resp["exclusions"].([]any)
|
|
assert.Len(t, exclusions, 1)
|
|
first := exclusions[0].(map[string]any)
|
|
assert.Equal(t, float64(942100), first["rule_id"])
|
|
assert.Empty(t, first["target"])
|
|
}
|
|
|
|
func TestSecurityHandler_DeleteWAFExclusion_NotFound(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
// Create config with exclusions
|
|
exclusionsJSON := `[{"rule_id":942100}]`
|
|
cfg := models.SecurityConfig{Name: "default", WAFExclusions: exclusionsJSON}
|
|
db.Create(&cfg)
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.DELETE("/security/waf/exclusions/:rule_id", handler.DeleteWAFExclusion)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/security/waf/exclusions/999999", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_DeleteWAFExclusion_NoConfig(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.DELETE("/security/waf/exclusions/:rule_id", handler.DeleteWAFExclusion)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/security/waf/exclusions/942100", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_DeleteWAFExclusion_InvalidRuleID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.DELETE("/security/waf/exclusions/:rule_id", handler.DeleteWAFExclusion)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/security/waf/exclusions/invalid", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_DeleteWAFExclusion_ZeroRuleID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.DELETE("/security/waf/exclusions/:rule_id", handler.DeleteWAFExclusion)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/security/waf/exclusions/0", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestSecurityHandler_DeleteWAFExclusion_NegativeRuleID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.DELETE("/security/waf/exclusions/:rule_id", handler.DeleteWAFExclusion)
|
|
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("DELETE", "/security/waf/exclusions/-1", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
// Integration test: Full WAF exclusion workflow
|
|
func TestSecurityHandler_WAFExclusion_FullWorkflow(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityAudit{}))
|
|
|
|
handler := NewSecurityHandler(config.SecurityConfig{}, db, nil)
|
|
router := gin.New()
|
|
router.GET("/security/waf/exclusions", handler.GetWAFExclusions)
|
|
router.POST("/security/waf/exclusions", handler.AddWAFExclusion)
|
|
router.DELETE("/security/waf/exclusions/:rule_id", handler.DeleteWAFExclusion)
|
|
|
|
// Step 1: Start with empty exclusions
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
var resp map[string]any
|
|
_ = json.Unmarshal(w.Body.Bytes(), &resp)
|
|
assert.Len(t, resp["exclusions"].([]any), 0)
|
|
|
|
// Step 2: Add first exclusion (full rule removal)
|
|
payload := map[string]any{
|
|
"rule_id": 942100,
|
|
"description": "SQL Injection false positive",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Step 3: Add second exclusion (targeted)
|
|
payload = map[string]any{
|
|
"rule_id": 941100,
|
|
"target": "ARGS:content",
|
|
"description": "XSS false positive in content field",
|
|
}
|
|
body, _ = json.Marshal(payload)
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("POST", "/security/waf/exclusions", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Step 4: Verify both exclusions exist
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
assert.Len(t, resp["exclusions"].([]any), 2)
|
|
|
|
// Step 5: Delete first exclusion
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("DELETE", "/security/waf/exclusions/942100", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// Step 6: Verify only second exclusion remains
|
|
w = httptest.NewRecorder()
|
|
req, _ = http.NewRequest("GET", "/security/waf/exclusions", http.NoBody)
|
|
router.ServeHTTP(w, req)
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
exclusions := resp["exclusions"].([]any)
|
|
assert.Len(t, exclusions, 1)
|
|
first := exclusions[0].(map[string]any)
|
|
assert.Equal(t, float64(941100), first["rule_id"])
|
|
assert.Equal(t, "ARGS:content", first["target"])
|
|
}
|
|
|
|
// Test WAFDisabled field on ProxyHost
|
|
func TestProxyHost_WAFDisabled_DefaultFalse(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.ProxyHost{}))
|
|
|
|
host := models.ProxyHost{
|
|
UUID: "test-uuid",
|
|
DomainNames: "example.com",
|
|
ForwardHost: "backend",
|
|
ForwardPort: 8080,
|
|
Enabled: true,
|
|
}
|
|
db.Create(&host)
|
|
|
|
var retrieved models.ProxyHost
|
|
db.First(&retrieved, host.ID)
|
|
|
|
assert.False(t, retrieved.WAFDisabled, "WAFDisabled should default to false")
|
|
}
|
|
|
|
func TestProxyHost_WAFDisabled_SetTrue(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.ProxyHost{}))
|
|
|
|
host := models.ProxyHost{
|
|
UUID: "test-uuid",
|
|
DomainNames: "example.com",
|
|
ForwardHost: "backend",
|
|
ForwardPort: 8080,
|
|
Enabled: true,
|
|
WAFDisabled: true,
|
|
}
|
|
db.Create(&host)
|
|
|
|
var retrieved models.ProxyHost
|
|
db.First(&retrieved, host.ID)
|
|
|
|
assert.True(t, retrieved.WAFDisabled, "WAFDisabled should be true when set")
|
|
}
|
|
|
|
// Test WAFParanoiaLevel field on SecurityConfig
|
|
func TestSecurityConfig_WAFParanoiaLevel_Default(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
cfg := models.SecurityConfig{
|
|
Name: "default",
|
|
WAFMode: "block",
|
|
}
|
|
db.Create(&cfg)
|
|
|
|
var retrieved models.SecurityConfig
|
|
db.First(&retrieved, cfg.ID)
|
|
|
|
// GORM default is 1
|
|
assert.Equal(t, 1, retrieved.WAFParanoiaLevel, "WAFParanoiaLevel should default to 1")
|
|
}
|
|
|
|
func TestSecurityConfig_WAFParanoiaLevel_CustomValue(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
cfg := models.SecurityConfig{
|
|
Name: "default",
|
|
WAFMode: "block",
|
|
WAFParanoiaLevel: 3,
|
|
}
|
|
db.Create(&cfg)
|
|
|
|
var retrieved models.SecurityConfig
|
|
db.First(&retrieved, cfg.ID)
|
|
|
|
assert.Equal(t, 3, retrieved.WAFParanoiaLevel, "WAFParanoiaLevel should be 3")
|
|
}
|
|
|
|
// Test WAFExclusions field on SecurityConfig
|
|
func TestSecurityConfig_WAFExclusions_Empty(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
cfg := models.SecurityConfig{
|
|
Name: "default",
|
|
WAFMode: "block",
|
|
}
|
|
db.Create(&cfg)
|
|
|
|
var retrieved models.SecurityConfig
|
|
db.First(&retrieved, cfg.ID)
|
|
|
|
assert.Empty(t, retrieved.WAFExclusions, "WAFExclusions should be empty by default")
|
|
}
|
|
|
|
func TestSecurityConfig_WAFExclusions_JSONArray(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db := setupTestDB(t)
|
|
require.NoError(t, db.AutoMigrate(&models.SecurityConfig{}))
|
|
|
|
exclusions := `[{"rule_id":942100,"target":"ARGS:password","description":"Skip password field"}]`
|
|
cfg := models.SecurityConfig{
|
|
Name: "default",
|
|
WAFMode: "block",
|
|
WAFExclusions: exclusions,
|
|
}
|
|
db.Create(&cfg)
|
|
|
|
var retrieved models.SecurityConfig
|
|
db.First(&retrieved, cfg.ID)
|
|
|
|
assert.Equal(t, exclusions, retrieved.WAFExclusions)
|
|
|
|
// Verify it can be parsed
|
|
var parsed []map[string]any
|
|
err := json.Unmarshal([]byte(retrieved.WAFExclusions), &parsed)
|
|
require.NoError(t, err)
|
|
assert.Len(t, parsed, 1)
|
|
assert.Equal(t, float64(942100), parsed[0]["rule_id"])
|
|
}
|