mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-06-26 15:39:38 +02:00
chore: add copyright headers, CI tests, and sanitize gitignore
This commit is contained in:
parent
5cbb3d89d3
commit
d1f844235e
325 changed files with 2267 additions and 902 deletions
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package soc provides SOC analytics: event trends, severity distribution,
|
||||
// top sources, MITRE ATT&CK coverage, and time-series aggregation.
|
||||
package soc
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package soc
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package soc
|
||||
|
||||
import (
|
||||
|
|
@ -524,4 +528,3 @@ func TestE2E_CrescendoEscalation(t *testing.T) {
|
|||
assert.Equal(t, domsoc.SeverityCritical, lastInc.Severity)
|
||||
assert.Contains(t, lastInc.MITREMapping, "T1059")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package soc
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package soc
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package soc provides application services for the SENTINEL AI SOC subsystem.
|
||||
package soc
|
||||
|
||||
|
|
@ -29,14 +33,14 @@ const (
|
|||
// Service orchestrates the SOC event pipeline:
|
||||
// Step 0: Secret Scanner (INVARIANT) → DIP → Decision Logger → Persist → Correlation.
|
||||
type Service struct {
|
||||
mu sync.RWMutex
|
||||
repo domsoc.SOCRepository
|
||||
logger *audit.DecisionLogger
|
||||
rules []domsoc.SOCCorrelationRule
|
||||
mu sync.RWMutex
|
||||
repo domsoc.SOCRepository
|
||||
logger *audit.DecisionLogger
|
||||
rules []domsoc.SOCCorrelationRule
|
||||
playbookEngine *domsoc.PlaybookEngine
|
||||
executorRegistry *domsoc.ExecutorRegistry
|
||||
sensors map[string]*domsoc.Sensor
|
||||
draining bool // §15.7: graceful shutdown mode — rejects new events
|
||||
sensors map[string]*domsoc.Sensor
|
||||
draining bool // §15.7: graceful shutdown mode — rejects new events
|
||||
|
||||
// Alert Clustering engine (§7.6): groups related alerts.
|
||||
clusterEngine *domsoc.ClusterEngine
|
||||
|
|
@ -45,8 +49,8 @@ type Service struct {
|
|||
eventBus *domsoc.EventBus
|
||||
|
||||
// Rate limiting per sensor (§17.3): sensorID → timestamps of recent events.
|
||||
sensorRates map[string][]time.Time
|
||||
rateLimitDisabled bool
|
||||
sensorRates map[string][]time.Time
|
||||
rateLimitDisabled bool
|
||||
|
||||
// Sensor authentication (§17.3 T-01): sensorID → pre-shared key.
|
||||
sensorKeys map[string]string
|
||||
|
|
@ -85,9 +89,9 @@ func NewService(repo domsoc.SOCRepository, logger *audit.DecisionLogger) *Servic
|
|||
// Build executor registry with all SOAR action handlers
|
||||
reg := domsoc.NewExecutorRegistry()
|
||||
reg.Register(&domsoc.BlockIPExecutor{})
|
||||
reg.Register(domsoc.NewNotifyExecutor("")) // URL configured via SetNotifyURL()
|
||||
reg.Register(domsoc.NewNotifyExecutor("")) // URL configured via SetNotifyURL()
|
||||
reg.Register(domsoc.NewQuarantineExecutor())
|
||||
reg.Register(domsoc.NewEscalateExecutor("")) // URL configured via SetEscalateURL()
|
||||
reg.Register(domsoc.NewEscalateExecutor("")) // URL configured via SetEscalateURL()
|
||||
// Webhook executor configured separately via SetWebhookConfig()
|
||||
|
||||
// Create playbook engine with live executor handler (not just logging)
|
||||
|
|
@ -100,21 +104,21 @@ func NewService(repo domsoc.SOCRepository, logger *audit.DecisionLogger) *Servic
|
|||
)
|
||||
|
||||
return &Service{
|
||||
repo: repo,
|
||||
logger: logger,
|
||||
rules: domsoc.DefaultSOCCorrelationRules(),
|
||||
playbookEngine: pe,
|
||||
executorRegistry: reg,
|
||||
sensors: make(map[string]*domsoc.Sensor),
|
||||
clusterEngine: domsoc.NewClusterEngine(domsoc.DefaultClusterConfig()),
|
||||
eventBus: domsoc.NewEventBus(256),
|
||||
sensorRates: make(map[string][]time.Time),
|
||||
zeroG: domsoc.NewZeroGMode(),
|
||||
p2pSync: domsoc.NewP2PSyncService(),
|
||||
anomaly: domsoc.NewAnomalyDetector(),
|
||||
repo: repo,
|
||||
logger: logger,
|
||||
rules: domsoc.DefaultSOCCorrelationRules(),
|
||||
playbookEngine: pe,
|
||||
executorRegistry: reg,
|
||||
sensors: make(map[string]*domsoc.Sensor),
|
||||
clusterEngine: domsoc.NewClusterEngine(domsoc.DefaultClusterConfig()),
|
||||
eventBus: domsoc.NewEventBus(256),
|
||||
sensorRates: make(map[string][]time.Time),
|
||||
zeroG: domsoc.NewZeroGMode(),
|
||||
p2pSync: domsoc.NewP2PSyncService(),
|
||||
anomaly: domsoc.NewAnomalyDetector(),
|
||||
threatIntelEngine: domsoc.NewThreatIntelEngine(),
|
||||
retention: domsoc.NewDataRetentionPolicy(),
|
||||
scanSemaphore: make(chan struct{}, 8), // §20.1: max 8 concurrent scans
|
||||
retention: domsoc.NewDataRetentionPolicy(),
|
||||
scanSemaphore: make(chan struct{}, 8), // §20.1: max 8 concurrent scans
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +217,6 @@ func (s *Service) TestWebhook() []WebhookResult {
|
|||
return wh.NotifyIncident("webhook_test", testIncident)
|
||||
}
|
||||
|
||||
|
||||
// Drain puts the service into drain mode (§15.7 Stage 1).
|
||||
// New events are rejected with ErrDraining; existing processing continues.
|
||||
func (s *Service) Drain() {
|
||||
|
|
@ -301,6 +304,7 @@ func (s *Service) runRetentionPurge() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IngestEvent processes an incoming security event through the SOC pipeline.
|
||||
// Returns the event ID and any incident created by correlation.
|
||||
//
|
||||
|
|
@ -523,7 +527,7 @@ func (s *Service) isRateLimited(sensorID string) bool {
|
|||
pruned = append(pruned, ts)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rateLimited := len(pruned) >= MaxEventsPerSecondPerSensor
|
||||
if !rateLimited {
|
||||
pruned = append(pruned, now)
|
||||
|
|
@ -768,9 +772,9 @@ func (s *Service) GetRecentDecisions(limit int) []map[string]any {
|
|||
return []map[string]any{
|
||||
{
|
||||
"total_decisions": s.logger.Count(),
|
||||
"hash_chain": s.logger.PrevHash(),
|
||||
"log_path": s.logger.Path(),
|
||||
"status": "operational",
|
||||
"hash_chain": s.logger.PrevHash(),
|
||||
"log_path": s.logger.Path(),
|
||||
"status": "operational",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -979,10 +983,10 @@ func (s *Service) ListIncidentsAdvanced(f IncidentFilter) (*IncidentFilterResult
|
|||
|
||||
// BulkAction defines a batch operation on incidents.
|
||||
type BulkAction struct {
|
||||
Action string `json:"action"` // assign, status, close, delete
|
||||
Action string `json:"action"` // assign, status, close, delete
|
||||
IncidentIDs []string `json:"incident_ids"`
|
||||
Value string `json:"value"` // analyst email, new status
|
||||
Actor string `json:"actor"` // who initiated
|
||||
Value string `json:"value"` // analyst email, new status
|
||||
Actor string `json:"actor"` // who initiated
|
||||
}
|
||||
|
||||
// BulkActionResult is the result of a batch operation.
|
||||
|
|
@ -1030,12 +1034,12 @@ type SLAThreshold struct {
|
|||
|
||||
// SLAStatus represents an incident's SLA compliance state.
|
||||
type SLAStatus struct {
|
||||
ResponseBreached bool `json:"response_breached"`
|
||||
ResolutionBreached bool `json:"resolution_breached"`
|
||||
ResponseRemaining float64 `json:"response_remaining_min"` // minutes remaining (negative = breached)
|
||||
ResponseBreached bool `json:"response_breached"`
|
||||
ResolutionBreached bool `json:"resolution_breached"`
|
||||
ResponseRemaining float64 `json:"response_remaining_min"` // minutes remaining (negative = breached)
|
||||
ResolutionRemaining float64 `json:"resolution_remaining_min"`
|
||||
ResponseTarget float64 `json:"response_target_min"`
|
||||
ResolutionTarget float64 `json:"resolution_target_min"`
|
||||
ResponseTarget float64 `json:"response_target_min"`
|
||||
ResolutionTarget float64 `json:"resolution_target_min"`
|
||||
}
|
||||
|
||||
// DefaultSLAThresholds returns SLA targets per severity.
|
||||
|
|
@ -1161,7 +1165,7 @@ func (s *Service) Dashboard(tenantID string) (*DashboardData, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
lastHourEvents, err := s.repo.CountEventsSince(tenantID, time.Now().Add(-1 * time.Hour))
|
||||
lastHourEvents, err := s.repo.CountEventsSince(tenantID, time.Now().Add(-1*time.Hour))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package soc
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package soc
|
||||
|
||||
import (
|
||||
|
|
@ -17,7 +21,7 @@ type STIXBundle struct {
|
|||
|
||||
// STIXObject represents a generic STIX 2.1 object.
|
||||
type STIXObject struct {
|
||||
Type string `json:"type"` // indicator, malware, attack-pattern, etc.
|
||||
Type string `json:"type"` // indicator, malware, attack-pattern, etc.
|
||||
ID string `json:"id"`
|
||||
Created time.Time `json:"created"`
|
||||
Modified time.Time `json:"modified"`
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package soc
|
||||
|
||||
import (
|
||||
|
|
@ -111,8 +115,8 @@ func TestProcessBundle_FiltersNonIndicators(t *testing.T) {
|
|||
Objects: []STIXObject{
|
||||
{Type: "indicator", Pattern: "[ipv4-addr:value = '10.0.0.1']", Modified: time.Now()},
|
||||
{Type: "malware", Name: "BadMalware"}, // should be skipped
|
||||
{Type: "indicator", Pattern: ""}, // empty pattern → skipped
|
||||
{Type: "attack-pattern", Name: "Phish"}, // should be skipped
|
||||
{Type: "indicator", Pattern: ""}, // empty pattern → skipped
|
||||
{Type: "attack-pattern", Name: "Phish"}, // should be skipped
|
||||
{Type: "indicator", Pattern: "[domain-name:value = 'bad.com']", Modified: time.Now()},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package soc provides a threat intelligence feed integration
|
||||
// for enriching SOC events and correlation rules.
|
||||
//
|
||||
|
|
@ -36,9 +40,9 @@ const (
|
|||
type IOC struct {
|
||||
Type IOCType `json:"type"`
|
||||
Value string `json:"value"`
|
||||
Source string `json:"source"` // Feed name
|
||||
Severity string `json:"severity"` // critical/high/medium/low
|
||||
Tags []string `json:"tags"` // MITRE ATT&CK, campaign, etc.
|
||||
Source string `json:"source"` // Feed name
|
||||
Severity string `json:"severity"` // critical/high/medium/low
|
||||
Tags []string `json:"tags"` // MITRE ATT&CK, campaign, etc.
|
||||
FirstSeen time.Time `json:"first_seen"`
|
||||
LastSeen time.Time `json:"last_seen"`
|
||||
Confidence float64 `json:"confidence"` // 0.0-1.0
|
||||
|
|
@ -46,31 +50,31 @@ type IOC struct {
|
|||
|
||||
// ThreatFeed represents a configured threat intelligence source.
|
||||
type ThreatFeed struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Type string `json:"type"` // stix, csv, json
|
||||
Enabled bool `json:"enabled"`
|
||||
Interval time.Duration `json:"interval"`
|
||||
APIKey string `json:"api_key,omitempty"`
|
||||
LastFetch time.Time `json:"last_fetch"`
|
||||
IOCCount int `json:"ioc_count"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Type string `json:"type"` // stix, csv, json
|
||||
Enabled bool `json:"enabled"`
|
||||
Interval time.Duration `json:"interval"`
|
||||
APIKey string `json:"api_key,omitempty"`
|
||||
LastFetch time.Time `json:"last_fetch"`
|
||||
IOCCount int `json:"ioc_count"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
}
|
||||
|
||||
// ─── Threat Intel Store ─────────────────────────────────
|
||||
|
||||
// ThreatIntelStore manages IOCs from multiple feeds.
|
||||
type ThreatIntelStore struct {
|
||||
mu sync.RWMutex
|
||||
iocs map[string]*IOC // key: type:value
|
||||
feeds []ThreatFeed
|
||||
mu sync.RWMutex
|
||||
iocs map[string]*IOC // key: type:value
|
||||
feeds []ThreatFeed
|
||||
client *http.Client
|
||||
|
||||
// Stats
|
||||
TotalIOCs int `json:"total_iocs"`
|
||||
TotalFeeds int `json:"total_feeds"`
|
||||
TotalIOCs int `json:"total_iocs"`
|
||||
TotalFeeds int `json:"total_feeds"`
|
||||
LastRefresh time.Time `json:"last_refresh"`
|
||||
MatchesFound int64 `json:"matches_found"`
|
||||
MatchesFound int64 `json:"matches_found"`
|
||||
}
|
||||
|
||||
// NewThreatIntelStore creates an empty threat intel store.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package soc
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2026 Syntrex Lab. All rights reserved.
|
||||
// Use of this source code is governed by an Apache-2.0 license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package webhook provides outbound SOAR webhook notifications
|
||||
// for the SOC pipeline. Fires HTTP POST on incident creation/update.
|
||||
package soc
|
||||
|
|
@ -38,8 +42,8 @@ type WebhookConfig struct {
|
|||
type WebhookPayload struct {
|
||||
EventType string `json:"event_type"` // incident_created, incident_updated, sensor_offline
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Source string `json:"source"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
Source string `json:"source"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
// WebhookResult tracks delivery status per endpoint.
|
||||
|
|
@ -80,8 +84,6 @@ func NewWebhookNotifier(config WebhookConfig) *WebhookNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NotifyIncident sends an incident webhook to all configured endpoints.
|
||||
// Non-blocking: fires goroutines for each endpoint.
|
||||
func (w *WebhookNotifier) NotifyIncident(eventType string, incident *domsoc.Incident) []WebhookResult {
|
||||
|
|
@ -105,7 +107,7 @@ func (w *WebhookNotifier) NotifyIncident(eventType string, incident *domsoc.Inci
|
|||
EventType: eventType,
|
||||
Timestamp: time.Now().UTC(),
|
||||
Source: "sentinel-soc",
|
||||
Data: data,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
|
|
@ -151,7 +153,7 @@ func (w *WebhookNotifier) NotifySensorOffline(sensor domsoc.Sensor) []WebhookRes
|
|||
EventType: "sensor_offline",
|
||||
Timestamp: time.Now().UTC(),
|
||||
Source: "sentinel-soc",
|
||||
Data: data,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(payload)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue