Skip to content

Commit 17dee40

Browse files
committed
alerts-bugsfixes
1 parent 8275b3c commit 17dee40

3 files changed

Lines changed: 75 additions & 4 deletions

File tree

internal/api/handlers/dashboard_analysis.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,25 @@ func GetAlertsAnalysis(dockerClient *docker.Client, cfg *config.Config, ttlCache
270270
}
271271
}
272272

273+
// Normalize nested cscli fields to match the CrowdSecAlert contract.
274+
// cscli nests value/scope under source{} and origin/type under decisions[0].
275+
for _, item := range alerts {
276+
node, ok := item.(map[string]interface{})
277+
if !ok {
278+
continue
279+
}
280+
if src, ok := node["source"].(map[string]interface{}); ok {
281+
node["value"] = src["value"]
282+
node["scope"] = src["scope"]
283+
}
284+
if decs, ok := node["decisions"].([]interface{}); ok && len(decs) > 0 {
285+
if d, ok := decs[0].(map[string]interface{}); ok {
286+
node["origin"] = d["origin"]
287+
node["type"] = d["type"]
288+
}
289+
}
290+
}
291+
273292
logger.Info("Alerts analysis retrieved successfully", "count", len(alerts))
274293

275294
result := gin.H{"alerts": alerts, "count": len(alerts)}

internal/history/service.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,13 +363,26 @@ func parseAlertsOutput(output string) ([]AlertSnapshot, error) {
363363
if !ok {
364364
continue
365365
}
366+
367+
// value and scope are nested under the "source" sub-object
368+
source, _ := node["source"].(map[string]interface{})
369+
370+
// origin and type come from the first decision entry
371+
var origin, decType string
372+
if decs, ok := node["decisions"].([]interface{}); ok && len(decs) > 0 {
373+
if d, ok := decs[0].(map[string]interface{}); ok {
374+
origin = asString(d["origin"])
375+
decType = asString(d["type"])
376+
}
377+
}
378+
366379
alerts = append(alerts, AlertSnapshot{
367380
ID: asInt64(node["id"]),
368381
Scenario: asString(node["scenario"]),
369-
Scope: asString(node["scope"]),
370-
Value: asString(node["value"]),
371-
Origin: asString(node["origin"]),
372-
Type: asString(node["type"]),
382+
Scope: asString(source["scope"]),
383+
Value: asString(source["value"]),
384+
Origin: origin,
385+
Type: decType,
373386
EventsCount: int(asInt64(node["events_count"])),
374387
StartAt: asString(node["start_at"]),
375388
StopAt: asString(node["stop_at"]),

internal/history/store_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,42 @@ func TestParseDecisionsOutputNestedAndFlat(t *testing.T) {
136136
t.Fatalf("expected flat decision value")
137137
}
138138
}
139+
140+
func TestParseAlertsOutputNestedSource(t *testing.T) {
141+
input := `[{
142+
"id": 7,
143+
"scenario": "crowdsecurity/http-probing",
144+
"events_count": 11,
145+
"start_at": "2024-01-01T00:00:00Z",
146+
"stop_at": "2024-01-01T00:05:00Z",
147+
"source": {"scope": "Ip", "value": "1.2.3.4"},
148+
"decisions": [{"origin": "crowdsec", "type": "ban"}]
149+
}]`
150+
151+
alerts, err := parseAlertsOutput(input)
152+
if err != nil {
153+
t.Fatalf("unexpected error: %v", err)
154+
}
155+
if len(alerts) != 1 {
156+
t.Fatalf("expected 1 alert, got %d", len(alerts))
157+
}
158+
a := alerts[0]
159+
if a.Value != "1.2.3.4" {
160+
t.Errorf("Value: got %q, want %q", a.Value, "1.2.3.4")
161+
}
162+
if a.Scope != "Ip" {
163+
t.Errorf("Scope: got %q, want %q", a.Scope, "Ip")
164+
}
165+
if a.Origin != "crowdsec" {
166+
t.Errorf("Origin: got %q, want %q", a.Origin, "crowdsec")
167+
}
168+
if a.Type != "ban" {
169+
t.Errorf("Type: got %q, want %q", a.Type, "ban")
170+
}
171+
if a.Scenario != "crowdsecurity/http-probing" {
172+
t.Errorf("Scenario: got %q, want crowdsecurity/http-probing", a.Scenario)
173+
}
174+
if a.EventsCount != 11 {
175+
t.Errorf("EventsCount: got %d, want 11", a.EventsCount)
176+
}
177+
}

0 commit comments

Comments
 (0)