mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-28 13:56:21 +02:00
Release prep: 54 engines, self-hosted signatures, i18n, dashboard updates
This commit is contained in:
parent
694e32be26
commit
41cbfd6e0a
178 changed files with 36008 additions and 399 deletions
84
internal/domain/soc/rule_loader.go
Normal file
84
internal/domain/soc/rule_loader.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package soc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// RuleConfig is the YAML format for custom correlation rules (§7.5).
|
||||
//
|
||||
// Example rules.yaml:
|
||||
//
|
||||
// rules:
|
||||
// - id: CUSTOM-001
|
||||
// name: API Key Spray
|
||||
// required_categories: [auth_bypass, brute_force]
|
||||
// min_events: 5
|
||||
// time_window: 2m
|
||||
// severity: HIGH
|
||||
// kill_chain_phase: Reconnaissance
|
||||
// mitre_mapping: [T1110]
|
||||
// cross_sensor: true
|
||||
type RuleConfig struct {
|
||||
Rules []YAMLRule `yaml:"rules"`
|
||||
}
|
||||
|
||||
// YAMLRule is a single custom correlation rule loaded from YAML.
|
||||
type YAMLRule struct {
|
||||
ID string `yaml:"id"`
|
||||
Name string `yaml:"name"`
|
||||
RequiredCategories []string `yaml:"required_categories"`
|
||||
MinEvents int `yaml:"min_events"`
|
||||
TimeWindow string `yaml:"time_window"` // e.g., "5m", "10m", "1h"
|
||||
Severity string `yaml:"severity"`
|
||||
KillChainPhase string `yaml:"kill_chain_phase"`
|
||||
MITREMapping []string `yaml:"mitre_mapping"`
|
||||
Description string `yaml:"description"`
|
||||
CrossSensor bool `yaml:"cross_sensor"` // Allow cross-sensor correlation
|
||||
}
|
||||
|
||||
// LoadRulesFromYAML loads custom correlation rules from a YAML file.
|
||||
// Returns nil and no error if the file doesn't exist (optional config).
|
||||
func LoadRulesFromYAML(path string) ([]SOCCorrelationRule, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil // Optional — no custom rules
|
||||
}
|
||||
return nil, fmt.Errorf("read rules file: %w", err)
|
||||
}
|
||||
|
||||
var cfg RuleConfig
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, fmt.Errorf("parse rules YAML: %w", err)
|
||||
}
|
||||
|
||||
rules := make([]SOCCorrelationRule, 0, len(cfg.Rules))
|
||||
for _, yr := range cfg.Rules {
|
||||
dur, err := time.ParseDuration(yr.TimeWindow)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("rule %s: invalid time_window %q: %w", yr.ID, yr.TimeWindow, err)
|
||||
}
|
||||
|
||||
if yr.MinEvents == 0 {
|
||||
yr.MinEvents = 2 // Default
|
||||
}
|
||||
|
||||
rules = append(rules, SOCCorrelationRule{
|
||||
ID: yr.ID,
|
||||
Name: yr.Name,
|
||||
RequiredCategories: yr.RequiredCategories,
|
||||
MinEvents: yr.MinEvents,
|
||||
TimeWindow: dur,
|
||||
Severity: EventSeverity(yr.Severity),
|
||||
KillChainPhase: yr.KillChainPhase,
|
||||
MITREMapping: yr.MITREMapping,
|
||||
Description: yr.Description,
|
||||
CrossSensor: yr.CrossSensor,
|
||||
})
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue