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]interface{} err := json.Unmarshal(w.Body.Bytes(), &resp) require.NoError(t, err) exclusions := resp["exclusions"].([]interface{}) 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]interface{} err := json.Unmarshal(w.Body.Bytes(), &resp) require.NoError(t, err) exclusions := resp["exclusions"].([]interface{}) assert.Len(t, exclusions, 2) // Verify first exclusion first := exclusions[0].(map[string]interface{}) 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]interface{} err := json.Unmarshal(w.Body.Bytes(), &resp) require.NoError(t, err) exclusions := resp["exclusions"].([]interface{}) 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]interface{}{ "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]interface{} err := json.Unmarshal(w.Body.Bytes(), &resp) require.NoError(t, err) exclusion := resp["exclusion"].(map[string]interface{}) 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]interface{}{ "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]interface{} err := json.Unmarshal(w.Body.Bytes(), &resp) require.NoError(t, err) exclusion := resp["exclusion"].(map[string]interface{}) 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]interface{}{ "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]interface{} json.Unmarshal(w.Body.Bytes(), &resp) exclusions := resp["exclusions"].([]interface{}) 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]interface{}{ "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]interface{}{ "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]interface{}{ "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]interface{}{ "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]interface{}{ "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]interface{} 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"].([]interface{}) assert.Len(t, exclusions, 1) first := exclusions[0].(map[string]interface{}) 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]interface{} json.Unmarshal(w.Body.Bytes(), &resp) exclusions := resp["exclusions"].([]interface{}) assert.Len(t, exclusions, 1) first := exclusions[0].(map[string]interface{}) 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]interface{} json.Unmarshal(w.Body.Bytes(), &resp) assert.Len(t, resp["exclusions"].([]interface{}), 0) // Step 2: Add first exclusion (full rule removal) payload := map[string]interface{}{ "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]interface{}{ "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"].([]interface{}), 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"].([]interface{}) assert.Len(t, exclusions, 1) first := exclusions[0].(map[string]interface{}) 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]interface{} 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"]) }