Documentation
¶
Overview ¶
Package rules loads the YAML rule pack that governs detection policy.
The split between this package and internal/detect is deliberate. A detector answers "is this traffic periodic?" — a question about arithmetic, which changes rarely and is worth testing exhaustively. A rule answers "do I care, on this network, today?" — a question about policy, which changes constantly and belongs in a file an analyst can edit at 2am without a Go toolchain.
Mixing the two is how detection engines become unmaintainable: the tuning ends up compiled in, so the only way to silence a false positive is a release, and the only way to record why something was silenced is a comment nobody reads.
Index ¶
- Variables
- func Dump(dir string) ([]string, error)
- type Duration
- type Exception
- type Rule
- type Set
- func (s *Set) All() []*Rule
- func (s *Set) Detectors() ([]detect.Detector, *detect.Inventory, error)
- func (s *Set) Enabled() int
- func (s *Set) ForDetector(name string) []*Rule
- func (s *Set) Get(id string) (*Rule, bool)
- func (s *Set) Len() int
- func (s *Set) Policy() func(*model.Alert) bool
- func (s *Set) Validate() error
- type TuningError
Constants ¶
This section is empty.
Variables ¶
var KnownDetectors = map[string]bool{ "beaconing": true, "dns-tunnel": true, "port-scan": true, "exfiltration": true, "inventory": true, }
KnownDetectors is the set of detector names a rule may target. Rules naming anything else are rejected at load time rather than silently ignored — a typo in `detector:` would otherwise disable a rule without saying so.
Functions ¶
func Dump ¶
Dump writes the embedded pack into dir so it can be edited.
This is the on-ramp: an operator who wants to tune something should not have to find the rules in a source tree, and should start from exactly what the binary is already running rather than from documentation that may have drifted.
Types ¶
type Duration ¶
Duration is a time.Duration that reads Go duration strings from YAML, so a rule can say `max_interval: 6h` instead of a count of nanoseconds.
type Exception ¶
type Exception struct {
Description string `yaml:"description"`
Src string `yaml:"src,omitempty"` // IP or CIDR
Dst string `yaml:"dst,omitempty"` // IP or CIDR
DstPort uint16 `yaml:"dst_port,omitempty"` // 0 means any
Domain string `yaml:"domain,omitempty"` // suffix match
JA4 string `yaml:"ja4,omitempty"` // exact match
// contains filtered or unexported fields
}
Exception silences a rule for traffic the operator has already judged.
Every field that is set must match for the exception to apply, so an exception is an AND of its conditions. Recording a Description is required: an undocumented exception is indistinguishable from a bug six months later.
type Rule ¶
type Rule struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
Detector string `yaml:"detector"`
Enabled *bool `yaml:"enabled,omitempty"`
Severity *model.Severity `yaml:"severity,omitempty"`
Description string `yaml:"description,omitempty"`
References []string `yaml:"references,omitempty"`
Techniques []model.Technique `yaml:"techniques,omitempty"`
Tuning yaml.Node `yaml:"tuning,omitempty"`
Exceptions []Exception `yaml:"exceptions,omitempty"`
// Source records which file the rule came from, for error messages.
Source string `yaml:"-"`
}
Rule is one entry in the pack.
type Set ¶
type Set struct {
// Origin describes where the pack came from, for the startup banner.
Origin string
// contains filtered or unexported fields
}
Set is a loaded, validated rule pack.
func (*Set) Detectors ¶
Detectors builds the detector set described by the pack.
Tuning is merged per detector rather than per rule, because several rules can share one detector: the vertical and horizontal scan rules are separate findings produced by the same code, and each tunes its own threshold.
func (*Set) ForDetector ¶
ForDetector returns the rules targeting one detector.
func (*Set) Policy ¶
Policy returns the function the detection engine consults before emitting an alert. It applies, in order: rule enablement, exceptions, and then any metadata the rule overrides.
An alert whose rule ID is not in the pack is allowed through unchanged. That is the safe direction: a detector emitting a rule ID nobody has written a rule for is a gap in the pack, and silently discarding its findings would turn a documentation problem into a blind spot.
func (*Set) Validate ¶
Validate decodes every tuning block, surfacing malformed keys and durations.
This runs at load time rather than when the detectors are built. Tuning is decoded lazily by the config builders, so without an eager pass a misspelled key would go unreported until a detector happened to be constructed — and `tracehound rules`, which builds no detectors at all, would cheerfully print a rule that is silently broken.
type TuningError ¶
TuningError reports a malformed tuning block, naming the rule so the operator knows which file to open.
func (*TuningError) Error ¶
func (e *TuningError) Error() string
func (*TuningError) Unwrap ¶
func (e *TuningError) Unwrap() error