updated event details from map to raw json data so that field order is kept.

This commit is contained in:
AndReicscs
2026-06-18 12:50:31 +00:00
parent f7110ddb3f
commit 7eb4d7b9b5
13 changed files with 110 additions and 75 deletions
+3 -1
View File
@@ -1,5 +1,7 @@
package models
import "encoding/json"
// HubVersion defines the backend protocol version for resolving compatible sensors and authenticating Agents.
const HubVersion = "2.0.0"
@@ -34,7 +36,7 @@ type Event struct {
Severity string `json:"severity"`
Source string `json:"source"`
Target string `json:"target"`
Details map[string]interface{} `json:"details"`
Details json.RawMessage `json:"details"`
IsRead bool `json:"isRead"`
IsArchived bool `json:"isArchived"`
Count int `json:"count"`
+5 -3
View File
@@ -2,7 +2,6 @@ package event
import (
"context"
"encoding/json"
"fmt"
"log"
"strconv"
@@ -79,10 +78,13 @@ func (s *Service) ProcessEvent(e *models.Event, nodeID string) error {
}
}
detailsJSON, _ := json.Marshal(e.Details)
detailsStr := "{}"
if len(e.Details) > 0 {
detailsStr = string(e.Details)
}
e.NodeID = nodeID
lastInsertID, err := s.store.InsertEvent(e, nowStr, string(detailsJSON))
lastInsertID, err := s.store.InsertEvent(e, nowStr, detailsStr)
if err != nil {
if strings.Contains(err.Error(), "FOREIGN KEY") {
return fmt.Errorf("sensor_not_registered")
+2 -3
View File
@@ -2,7 +2,6 @@ package siem
import (
"context"
"encoding/json"
"fmt"
"log"
"math"
@@ -317,8 +316,8 @@ func (s *Service) formatSyslog(event models.Event) string {
timestamp := t.UTC().Format(time.RFC3339Nano)
detailsJSON, err := json.Marshal(event.Details)
if err != nil {
detailsJSON := event.Details
if len(detailsJSON) == 0 {
detailsJSON = []byte("{}")
}
+2 -1
View File
@@ -3,6 +3,7 @@ package siem
import (
"bufio"
"context"
"encoding/json"
"net"
"regexp"
"testing"
@@ -41,7 +42,7 @@ func TestSyslogForwardingRFC5424(t *testing.T) {
Source: "192.168.1.100",
Target: "10.0.0.5",
SensorID: "sensor-01",
Details: map[string]interface{}{"file": "/etc/shadow"},
Details: json.RawMessage(`{"file": "/etc/shadow"}`),
}
// Send the event
+5 -1
View File
@@ -77,7 +77,11 @@ func (s *SQLiteStore) GetEvents(isArchived int, nodeID string, sensorID string)
e.IsRead = isReadInt == 1
e.IsArchived = isArchivedInt == 1
json.Unmarshal([]byte(detailsStr), &e.Details)
if detailsStr != "" {
e.Details = json.RawMessage(detailsStr)
} else {
e.Details = json.RawMessage("{}")
}
events = append(events, e)
}
+31 -5
View File
@@ -148,7 +148,7 @@ type Sensor struct {
testTrigger string
testSource string
testTarget string
testDetails map[string]any
testDetails EventDetails
}
func NewSensor() (*Sensor, error) {
@@ -176,7 +176,7 @@ func NewSensor() (*Sensor, error) {
}
// SetTestPayload allows community sensors to define a realistic, protocol-specific mock payload
func (s *Sensor) SetTestPayload(trigger, source, target string, details map[string]any) {
func (s *Sensor) SetTestPayload(trigger, source, target string, details EventDetails) {
s.testPayload = map[string]any{
"eventTrigger": trigger,
"source": source,
@@ -214,7 +214,7 @@ func (s *Sensor) listenForSignals() {
trigger := "test_mode_synthetic_alert"
source := "Wizard Live Test"
target := "Mock Hub"
details := map[string]any{"test_message": "Wizard triggered a live test event firedrill."}
details := EventDetails{{"test_message", "Wizard triggered a live test event firedrill."}}
if s.testTrigger != "" {
trigger = s.testTrigger
@@ -241,7 +241,7 @@ func (s *Sensor) RunTestMode() bool {
trigger := "test_mode_synthetic_alert"
source := "CI/CD Runner"
target := "Mock Hub"
details := map[string]any{"test_message": "Automated CI/CD check."}
details := EventDetails{{"test_message", "Automated CI/CD check."}}
if s.testTrigger != "" {
trigger = s.testTrigger
@@ -275,7 +275,33 @@ func (s *Sensor) RunTestMode() bool {
// PIPELINE A: EVENT WORKER
// ==========================================
func (s *Sensor) ReportEvent(trigger, source, target string, details map[string]any) bool {
// EventDetail represents an ordered key-value pair for event details
type EventDetail struct {
Key string
Value any
}
// EventDetails is an ordered slice of event details that marshals to a JSON object
type EventDetails []EventDetail
func (ed EventDetails) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
buf.WriteString("{")
for i, d := range ed {
if i > 0 {
buf.WriteString(",")
}
key, _ := json.Marshal(d.Key)
val, _ := json.Marshal(d.Value)
buf.Write(key)
buf.WriteString(":")
buf.Write(val)
}
buf.WriteString("}")
return buf.Bytes(), nil
}
func (s *Sensor) ReportEvent(trigger, source, target string, details EventDetails) bool {
payload := map[string]any{
"contractVersion": s.hubContractVersion,
"sensorId": s.SensorID,
+4 -3
View File
@@ -264,7 +264,7 @@ func TestReportEvent_Serialization(t *testing.T) {
trigger := "test_serialization"
source := "192.168.1.10"
target := "8.8.8.8:53"
details := map[string]any{"protocol": "dns"}
details := EventDetails{{"protocol", "dns"}}
// Report the event, which places it on the channel
if !s.ReportEvent(trigger, source, target, details) {
@@ -287,8 +287,9 @@ func TestReportEvent_Serialization(t *testing.T) {
if got, want := receivedPayload["target"], target; got != want {
t.Errorf("target mismatch: got %v, want %v", got, want)
}
if !reflect.DeepEqual(receivedPayload["details"], details) {
t.Errorf("details mismatch: got %v, want %v", receivedPayload["details"], details)
expectedDetails := map[string]any{"protocol": "dns"}
if !reflect.DeepEqual(receivedPayload["details"], expectedDetails) {
t.Errorf("details mismatch: got %v, want %v", receivedPayload["details"], expectedDetails)
}
case <-time.After(2 * time.Second):
t.Fatal("Timed out waiting for server to receive event payload")
+9 -9
View File
@@ -167,10 +167,10 @@ func reportFileEvent(
trigger,
"Local OS",
filepath.Base(path),
map[string]any{
"category": category,
"action": action,
"path": path,
sdk.EventDetails{
{"category", category},
{"action", action},
{"path", path},
},
)
@@ -195,11 +195,11 @@ func main() {
"decoy_file_tampered",
"Wizard Firedrill",
"Mock Canary File",
map[string]any{
"test_message": "Wizard triggered a synthetic event firedrill.",
"category": "tamper",
"action": "Decoy file modified",
"path": "/canaries/mock_passwords.txt",
sdk.EventDetails{
{"test_message", "Wizard triggered a synthetic event firedrill."},
{"category", "tamper"},
{"action", "Decoy file modified"},
{"path", "/canaries/mock_passwords.txt"},
},
)
+11 -11
View File
@@ -23,12 +23,12 @@ func main() {
"icmp_ping_received",
"Wizard Firedrill",
"ICMP Listener",
map[string]any{
"test_message": "Wizard triggered a synthetic event firedrill.",
"packet_size": 64,
"icmp_id": 1337,
"icmp_seq": 1,
"action_taken": "logged",
sdk.EventDetails{
{"test_message", "Wizard triggered a synthetic event firedrill."},
{"packet_size", 64},
{"icmp_id", 1337},
{"icmp_seq", 1},
{"action_taken", "logged"},
},
)
@@ -99,11 +99,11 @@ func listenICMP(conn *icmp.PacketConn, hw *sdk.Sensor) {
"icmp_ping_received",
sourceIP,
"ICMP Listener",
map[string]any{
"packet_size": n,
"icmp_id": echo.ID,
"icmp_seq": echo.Seq,
"action_taken": "logged",
sdk.EventDetails{
{"packet_size", n},
{"icmp_id", echo.ID},
{"icmp_seq", echo.Seq},
{"action_taken", "logged"},
},
)
}
+11 -11
View File
@@ -42,12 +42,12 @@ func main() {
"network_scan_detected",
"Wizard Firedrill",
"Multiple Ports",
map[string]any{
"test_message": "Wizard triggered a synthetic event firedrill.",
"ports_hit": []uint16{22, 80, 443, 3306, 8080},
"count": 5,
"window_sec": 5.0,
"action_taken": "logged",
sdk.EventDetails{
{"test_message", "Wizard triggered a synthetic event firedrill."},
{"ports_hit", []uint16{22, 80, 443, 3306, 8080}},
{"count", 5},
{"window_sec", 5.0},
{"action_taken", "logged"},
},
)
@@ -149,11 +149,11 @@ func processHit(hw *sdk.Sensor, srcIP string, dstPort uint16) {
"network_scan_detected",
srcIP,
"Multiple Ports",
map[string]any{
"ports_hit": uniquePortsList,
"count": len(uniquePortsList),
"window_sec": window.Seconds(),
"action_taken": "logged",
sdk.EventDetails{
{"ports_hit", uniquePortsList},
{"count", len(uniquePortsList)},
{"window_sec", window.Seconds()},
{"action_taken", "logged"},
},
)
state.history = nil
+9 -9
View File
@@ -35,11 +35,11 @@ func main() {
"tcp_connection",
"Wizard Firedrill",
"Mock Tarpit Port",
map[string]any{
"test_message": "Wizard triggered a synthetic event firedrill.",
"payload": []string{"SSH-2.0-Firedrill-Test\r\n"},
"duration_sec": 5.2,
"action_taken": "hold",
sdk.EventDetails{
{"test_message", "Wizard triggered a synthetic event firedrill."},
{"payload", []string{"SSH-2.0-Firedrill-Test\r\n"}},
{"duration_sec", 5.2},
{"action_taken", "hold"},
},
)
@@ -165,10 +165,10 @@ func handleConnection(hw *sdk.Sensor, conn net.Conn, port int) {
"tcp_connection",
srcIP,
fmt.Sprintf("Port %d", port),
map[string]any{
"duration_sec": duration,
"payload": payload,
"action_taken": tarpitMode,
sdk.EventDetails{
{"duration_sec", duration},
{"payload", payload},
{"action_taken", tarpitMode},
},
)
}
+11 -11
View File
@@ -251,12 +251,12 @@ func main() {
"web_login_attempt",
"Wizard Firedrill",
"Web Interface",
map[string]any{
"test_message": "Wizard triggered a synthetic event firedrill.",
"user_agent": "HoneyWire-Firedrill/1.0",
"attempted_username": "admin",
"attempted_password": "password123",
"action_taken": "logged",
sdk.EventDetails{
{"test_message", "Wizard triggered a synthetic event firedrill."},
{"user_agent", "HoneyWire-Firedrill/1.0"},
{"attempted_username", "admin"},
{"attempted_password", "password123"},
{"action_taken", "logged"},
},
)
@@ -350,11 +350,11 @@ func handleLogin(w http.ResponseWriter, r *http.Request) {
"web_login_attempt",
srcIP,
"Web Interface",
map[string]any{
"user_agent": userAgent,
"attempted_username": username,
"attempted_password": password,
"action_taken": "logged",
sdk.EventDetails{
{"user_agent", userAgent},
{"attempted_username", username},
{"attempted_password", password},
{"action_taken", "logged"},
},
)
+7 -7
View File
@@ -27,9 +27,9 @@ func main() {
"custom_anomaly_detected",
"Wizard Firedrill",
"Mock Custom Target",
map[string]any{
"test_message": "Wizard triggered a synthetic event firedrill.",
"action_taken": "logged",
sdk.EventDetails{
{"test_message", "Wizard triggered a synthetic event firedrill."},
{"action_taken", "logged"},
},
)
@@ -84,10 +84,10 @@ func runSensor(ctx context.Context, hw *sdk.Sensor) {
"custom_anomaly_detected", // 1. Event Trigger
sourceIP, // 2. Source
target, // 3. Target
map[string]any{ // 4. Details
"attack_type": "example_probe",
"raw_payload": "GET /etc/passwd HTTP/1.1",
"action_taken": "logged",
sdk.EventDetails{ // 4. Details
{"attack_type", "example_probe"},
{"raw_payload", "GET /etc/passwd HTTP/1.1"},
{"action_taken", "logged"},
},
)
}