rules

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: MIT Imports: 16 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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

func Dump(dir string) ([]string, error)

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

type Duration time.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.

func (Duration) Std

func (d Duration) Std() time.Duration

Std converts to a standard library duration.

func (*Duration) UnmarshalYAML

func (d *Duration) UnmarshalYAML(n *yaml.Node) error

UnmarshalYAML implements yaml.Unmarshaler.

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.

func (*Exception) Matches

func (e *Exception) Matches(a *model.Alert) bool

Matches reports whether the alert falls under this exception.

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.

func (*Rule) IsEnabled

func (r *Rule) IsEnabled() bool

IsEnabled reports whether the rule is active. Absent means enabled: a rule pack should not need boilerplate to express the common case.

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 Builtin

func Builtin() (*Set, error)

Builtin returns the embedded rule pack.

func Load

func Load(dir string) (*Set, error)

Load reads rules from dir, or the embedded pack when dir is empty.

func LoadDir

func LoadDir(dir string) (*Set, error)

LoadDir reads every .yaml and .yml file in dir.

func (*Set) All

func (s *Set) All() []*Rule

All returns the rules in load order.

func (*Set) Detectors

func (s *Set) Detectors() ([]detect.Detector, *detect.Inventory, error)

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) Enabled

func (s *Set) Enabled() int

Enabled counts the active rules.

func (*Set) ForDetector

func (s *Set) ForDetector(name string) []*Rule

ForDetector returns the rules targeting one detector.

func (*Set) Get

func (s *Set) Get(id string) (*Rule, bool)

Get returns a rule by ID.

func (*Set) Len

func (s *Set) Len() int

Len reports how many rules were loaded.

func (*Set) Policy

func (s *Set) Policy() func(*model.Alert) bool

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

func (s *Set) Validate() error

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

type TuningError struct {
	Rule   string
	Source string
	Err    error
}

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL