mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-29 22:36:21 +02:00
220 lines
6.7 KiB
Go
220 lines
6.7 KiB
Go
// Package soc defines domain entities for the SENTINEL AI SOC subsystem.
|
|
// SOC extends gomcp's alert/oracle layer with multi-source event ingestion,
|
|
// incident management, sensor lifecycle, and compliance reporting.
|
|
package soc
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// EventSeverity represents SOC-specific severity levels (extended from alert.Severity).
|
|
type EventSeverity string
|
|
|
|
const (
|
|
SeverityInfo EventSeverity = "INFO"
|
|
SeverityLow EventSeverity = "LOW"
|
|
SeverityMedium EventSeverity = "MEDIUM"
|
|
SeverityHigh EventSeverity = "HIGH"
|
|
SeverityCritical EventSeverity = "CRITICAL"
|
|
)
|
|
|
|
// SeverityRank returns numeric rank for comparison (higher = more severe).
|
|
func (s EventSeverity) Rank() int {
|
|
switch s {
|
|
case SeverityCritical:
|
|
return 5
|
|
case SeverityHigh:
|
|
return 4
|
|
case SeverityMedium:
|
|
return 3
|
|
case SeverityLow:
|
|
return 2
|
|
case SeverityInfo:
|
|
return 1
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Verdict represents a SOC decision outcome.
|
|
type Verdict string
|
|
|
|
const (
|
|
VerdictAllow Verdict = "ALLOW"
|
|
VerdictDeny Verdict = "DENY"
|
|
VerdictReview Verdict = "REVIEW"
|
|
VerdictEscalate Verdict = "ESCALATE"
|
|
)
|
|
|
|
// EventSource identifies the sensor or subsystem that generated the event.
|
|
type EventSource string
|
|
|
|
const (
|
|
SourceSentinelCore EventSource = "sentinel-core"
|
|
SourceShield EventSource = "shield"
|
|
SourceImmune EventSource = "immune"
|
|
SourceMicroSwarm EventSource = "micro-swarm"
|
|
SourceGoMCP EventSource = "gomcp"
|
|
SourceShadowAI EventSource = "shadow-ai"
|
|
SourceExternal EventSource = "external"
|
|
)
|
|
|
|
// SOCEvent represents a security event ingested into the AI SOC Event Bus.
|
|
// This is the core entity flowing through the pipeline:
|
|
// Sensor → Secret Scanner (Step 0) → DIP → Decision Logger → Queue → Correlation.
|
|
type SOCEvent struct {
|
|
ID string `json:"id"`
|
|
TenantID string `json:"tenant_id,omitempty"`
|
|
Source EventSource `json:"source"`
|
|
SensorID string `json:"sensor_id"`
|
|
SensorKey string `json:"-"` // §17.3 T-01: pre-shared key (never serialized)
|
|
Severity EventSeverity `json:"severity"`
|
|
Category string `json:"category"` // e.g., "jailbreak", "injection", "exfiltration"
|
|
Subcategory string `json:"subcategory"` // e.g., "sql_injection", "tool_abuse"
|
|
Confidence float64 `json:"confidence"` // 0.0 - 1.0
|
|
Description string `json:"description"`
|
|
Payload string `json:"payload,omitempty"` // Raw input for Secret Scanner Step 0
|
|
SessionID string `json:"session_id,omitempty"`
|
|
ContentHash string `json:"content_hash,omitempty"` // SHA-256 dedup key (§5.2)
|
|
DecisionHash string `json:"decision_hash,omitempty"` // SHA-256 chain link
|
|
Verdict Verdict `json:"verdict"`
|
|
ZeroGMode bool `json:"zero_g_mode,omitempty"` // §13.4: Strike Force operation tag
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Metadata map[string]string `json:"metadata,omitempty"` // Extensible key-value pairs
|
|
}
|
|
|
|
// ComputeContentHash generates a SHA-256 hash from source+category+description+payload
|
|
// for content-based deduplication (§5.2 step 2).
|
|
func (e *SOCEvent) ComputeContentHash() string {
|
|
h := sha256.New()
|
|
fmt.Fprintf(h, "%s|%s|%s|%s", e.Source, e.Category, e.Description, e.Payload)
|
|
e.ContentHash = fmt.Sprintf("%x", h.Sum(nil))
|
|
return e.ContentHash
|
|
}
|
|
|
|
// KnownCategories is the set of recognized event categories.
|
|
// Events with unknown categories are still accepted but logged as warnings.
|
|
var KnownCategories = map[string]bool{
|
|
"jailbreak": true,
|
|
"prompt_injection": true,
|
|
"tool_abuse": true,
|
|
"exfiltration": true,
|
|
"pii_leak": true,
|
|
"auth_bypass": true,
|
|
"encoding": true,
|
|
"persistence": true,
|
|
"sensor_anomaly": true,
|
|
"dos": true,
|
|
"model_theft": true,
|
|
"supply_chain": true,
|
|
"data_poisoning": true,
|
|
"evasion": true,
|
|
"shadow_ai_usage": true,
|
|
"integration_health": true,
|
|
"other": true,
|
|
// GenAI EDR categories (SDD-001)
|
|
"genai_child_process": true,
|
|
"genai_sensitive_file_access": true,
|
|
"genai_unusual_domain": true,
|
|
"genai_credential_access": true,
|
|
"genai_persistence": true,
|
|
"genai_config_modification": true,
|
|
}
|
|
|
|
// ValidSeverity returns true if the severity is a known value.
|
|
func ValidSeverity(s EventSeverity) bool {
|
|
switch s {
|
|
case SeverityInfo, SeverityLow, SeverityMedium, SeverityHigh, SeverityCritical:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ValidSource returns true if the source is a known value.
|
|
func ValidSource(s EventSource) bool {
|
|
switch s {
|
|
case SourceSentinelCore, SourceShield, SourceImmune, SourceMicroSwarm, SourceGoMCP, SourceShadowAI, SourceExternal:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Validate checks all required fields and enum values.
|
|
// Returns nil if valid, or a *ValidationErrors with field-level details.
|
|
func (e SOCEvent) Validate() error {
|
|
ve := &ValidationErrors{}
|
|
|
|
if e.Source == "" {
|
|
ve.Add("source", "source is required")
|
|
} else if !ValidSource(e.Source) {
|
|
ve.Add("source", fmt.Sprintf("unknown source: %q (valid: sentinel-core, shield, immune, micro-swarm, gomcp, external)", e.Source))
|
|
}
|
|
|
|
if e.Severity == "" {
|
|
ve.Add("severity", "severity is required")
|
|
} else if !ValidSeverity(e.Severity) {
|
|
ve.Add("severity", fmt.Sprintf("unknown severity: %q (valid: INFO, LOW, MEDIUM, HIGH, CRITICAL)", e.Severity))
|
|
}
|
|
|
|
if e.Category == "" {
|
|
ve.Add("category", "category is required")
|
|
}
|
|
|
|
if e.Description == "" {
|
|
ve.Add("description", "description is required")
|
|
}
|
|
|
|
if e.Confidence < 0 || e.Confidence > 1 {
|
|
ve.Add("confidence", "confidence must be between 0.0 and 1.0")
|
|
}
|
|
|
|
if ve.HasErrors() {
|
|
return ve
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewSOCEvent creates a new SOC event with auto-generated ID.
|
|
func NewSOCEvent(source EventSource, severity EventSeverity, category, description string) SOCEvent {
|
|
return SOCEvent{
|
|
ID: genID("evt"),
|
|
Source: source,
|
|
Severity: severity,
|
|
Category: category,
|
|
Description: description,
|
|
Verdict: VerdictReview, // Default: needs review
|
|
Timestamp: time.Now(),
|
|
}
|
|
}
|
|
|
|
// WithSensor sets the sensor ID.
|
|
func (e SOCEvent) WithSensor(sensorID string) SOCEvent {
|
|
e.SensorID = sensorID
|
|
return e
|
|
}
|
|
|
|
// WithConfidence sets the confidence score.
|
|
func (e SOCEvent) WithConfidence(c float64) SOCEvent {
|
|
if c < 0 {
|
|
c = 0
|
|
}
|
|
if c > 1 {
|
|
c = 1
|
|
}
|
|
e.Confidence = c
|
|
return e
|
|
}
|
|
|
|
// WithVerdict sets the verdict.
|
|
func (e SOCEvent) WithVerdict(v Verdict) SOCEvent {
|
|
e.Verdict = v
|
|
return e
|
|
}
|
|
|
|
// IsCritical returns true if severity is HIGH or CRITICAL.
|
|
func (e SOCEvent) IsCritical() bool {
|
|
return e.Severity == SeverityHigh || e.Severity == SeverityCritical
|
|
}
|
|
|