aboutsummaryrefslogtreecommitdiff
path: root/rules_manager.go
diff options
context:
space:
mode:
authorEmiliano Ciavatta2020-04-13 10:29:00 +0000
committerEmiliano Ciavatta2020-04-13 10:29:00 +0000
commit7113463dead05631339fdab94de9440201c42489 (patch)
tree0da5b2ca4b7fc100bb843bb277a816aca0f42421 /rules_manager.go
parent25bd17a2147d7169695772c2a887cdd54caff770 (diff)
Update rules
Diffstat (limited to 'rules_manager.go')
-rw-r--r--rules_manager.go175
1 files changed, 107 insertions, 68 deletions
diff --git a/rules_manager.go b/rules_manager.go
index 0750d53..388aeee 100644
--- a/rules_manager.go
+++ b/rules_manager.go
@@ -6,45 +6,44 @@ import (
"errors"
"fmt"
"github.com/flier/gohs/hyperscan"
+ "github.com/go-playground/validator/v10"
log "github.com/sirupsen/logrus"
"sync"
"time"
)
type RegexFlags struct {
- Caseless bool `json:"caseless"` // Set case-insensitive matching.
- DotAll bool `json:"dot_all"` // Matching a `.` will not exclude newlines.
- MultiLine bool `json:"multi_line"` // Set multi-line anchoring.
- SingleMatch bool `json:"single_match"` // Set single-match only mode.
- Utf8Mode bool `json:"utf_8_mode"` // Enable UTF-8 mode for this expression.
- UnicodeProperty bool `json:"unicode_property"` // Enable Unicode property support for this expression
+ Caseless bool `json:"caseless" bson:"caseless,omitempty"` // Set case-insensitive matching.
+ DotAll bool `json:"dot_all" bson:"dot_all,omitempty"` // Matching a `.` will not exclude newlines.
+ MultiLine bool `json:"multi_line" bson:"multi_line,omitempty"` // Set multi-line anchoring.
+ SingleMatch bool `json:"single_match" bson:"single_match,omitempty"` // Set single-match only mode.
+ Utf8Mode bool `json:"utf_8_mode" bson:"utf_8_mode,omitempty"` // Enable UTF-8 mode for this expression.
+ UnicodeProperty bool `json:"unicode_property" bson:"unicode_property,omitempty"` // Enable Unicode property support for this expression
}
type Pattern struct {
- Regex string `json:"regex"`
- Flags RegexFlags `json:"flags"`
- MinOccurrences int `json:"min_occurrences"`
- MaxOccurrences int `json:"max_occurrences"`
+ Regex string `json:"regex" binding:"min=1" bson:"regex"`
+ Flags RegexFlags `json:"flags" bson:"flags,omitempty"`
+ MinOccurrences uint `json:"min_occurrences" bson:"min_occurrences,omitempty"`
+ MaxOccurrences uint `json:"max_occurrences" binding:"omitempty,gtefield=MinOccurrences" bson:"max_occurrences,omitempty"`
internalID int
compiledPattern *hyperscan.Pattern
}
type Filter struct {
- ServicePort int
- ClientAddress string
- ClientPort int
- MinDuration int
- MaxDuration int
- MinPackets int
- MaxPackets int
- MinSize int
- MaxSize int
+ ServicePort uint16 `json:"service_port" bson:"service_port,omitempty"`
+ ClientAddress string `json:"client_address" binding:"ip_addr" bson:"client_address,omitempty"`
+ ClientPort uint16 `json:"client_port" bson:"client_port,omitempty"`
+ MinDuration uint `json:"min_duration" bson:"min_duration,omitempty"`
+ MaxDuration uint `json:"max_duration" binding:"omitempty,gtefield=MinDuration" bson:"max_duration,omitempty"`
+ MinBytes uint `json:"min_bytes" bson:"min_bytes,omitempty"`
+ MaxBytes uint `json:"max_bytes" binding:"omitempty,gtefield=MinBytes" bson:"max_bytes,omitempty"`
}
type Rule struct {
- ID RowID `json:"-" bson:"_id,omitempty"`
- Name string `json:"name" binding:"required,min=3" bson:"name"`
- Color string `json:"color" binding:"required,hexcolor" bson:"color"`
+ ID RowID `json:"id" bson:"_id,omitempty"`
+ Name string `json:"name" binding:"min=3" bson:"name"`
+ Color string `json:"color" binding:"hexcolor" bson:"color"`
Notes string `json:"notes" bson:"notes,omitempty"`
Enabled bool `json:"enabled" bson:"enabled"`
Patterns []Pattern `json:"patterns" binding:"required,min=1" bson:"patterns"`
@@ -53,38 +52,44 @@ type Rule struct {
}
type RulesDatabase struct {
- database hyperscan.StreamDatabase
+ database hyperscan.StreamDatabase
databaseSize int
- version RowID
+ version RowID
}
type RulesManager interface {
LoadRules() error
- AddRule(context context.Context, rule Rule) (string, error)
+ AddRule(context context.Context, rule Rule) (RowID, error)
+ GetRule(id RowID) (Rule, bool)
+ UpdateRule(context context.Context, rule Rule) bool
+ GetRules() []Rule
FillWithMatchedRules(connection *Connection, clientMatches map[uint][]PatternSlice, serverMatches map[uint][]PatternSlice)
DatabaseUpdateChannel() chan RulesDatabase
}
type rulesManagerImpl struct {
storage Storage
- rules map[string]Rule
+ rules map[RowID]Rule
rulesByName map[string]Rule
- ruleIndex int
patterns map[string]Pattern
- mPatterns sync.Mutex
+ mutex sync.Mutex
databaseUpdated chan RulesDatabase
+ validate *validator.Validate
}
func NewRulesManager(storage Storage) RulesManager {
return &rulesManagerImpl{
- storage: storage,
- rules: make(map[string]Rule),
- patterns: make(map[string]Pattern),
- mPatterns: sync.Mutex{},
+ storage: storage,
+ rules: make(map[RowID]Rule),
+ rulesByName: make(map[string]Rule),
+ patterns: make(map[string]Pattern),
+ mutex: sync.Mutex{},
+ databaseUpdated: make(chan RulesDatabase),
+ validate: validator.New(),
}
}
-func (rm rulesManagerImpl) LoadRules() error {
+func (rm *rulesManagerImpl) LoadRules() error {
var rules []Rule
if err := rm.storage.Find(Rules).Sort("_id", true).All(&rules); err != nil {
return err
@@ -96,48 +101,96 @@ func (rm rulesManagerImpl) LoadRules() error {
}
}
- rm.ruleIndex = len(rules)
return rm.generateDatabase(rules[len(rules)-1].ID)
}
-func (rm rulesManagerImpl) AddRule(context context.Context, rule Rule) (string, error) {
- rm.mPatterns.Lock()
+func (rm *rulesManagerImpl) AddRule(context context.Context, rule Rule) (RowID, error) {
+ rm.mutex.Lock()
- rule.ID = rm.storage.NewCustomRowID(uint64(rm.ruleIndex), time.Now())
+ rule.ID = rm.storage.NewCustomRowID(uint64(len(rm.rules)), time.Now())
rule.Enabled = true
if err := rm.validateAndAddRuleLocal(&rule); err != nil {
- rm.mPatterns.Unlock()
- return "", err
+ rm.mutex.Unlock()
+ return rule.ID, err
}
if err := rm.generateDatabase(rule.ID); err != nil {
- rm.mPatterns.Unlock()
+ rm.mutex.Unlock()
log.WithError(err).WithField("rule", rule).Panic("failed to generate database")
}
- rm.mPatterns.Unlock()
+ rm.mutex.Unlock()
if _, err := rm.storage.Insert(Rules).Context(context).One(rule); err != nil {
log.WithError(err).WithField("rule", rule).Panic("failed to insert rule on database")
}
- return rule.ID.Hex(), nil
+ return rule.ID, nil
}
-func (rm rulesManagerImpl) validateAndAddRuleLocal(rule *Rule) error {
+func (rm *rulesManagerImpl) GetRule(id RowID) (Rule, bool) {
+ rule, isPresent := rm.rules[id]
+ return rule, isPresent
+}
+
+func (rm *rulesManagerImpl) UpdateRule(context context.Context, rule Rule) bool {
+ updated, err := rm.storage.Update(Rules).Context(context).Filter(OrderedDocument{{"_id", rule.ID}}).
+ One(UnorderedDocument{"name": rule.Name, "color": rule.Color})
+ if err != nil {
+ log.WithError(err).WithField("rule", rule).Panic("failed to update rule on database")
+ }
+
+ if updated {
+ rm.mutex.Lock()
+ newRule := rm.rules[rule.ID]
+ newRule.Name = rule.Name
+ newRule.Color = rule.Color
+
+ delete(rm.rulesByName, newRule.Name)
+ rm.rulesByName[rule.Name] = newRule
+ rm.rules[rule.ID] = newRule
+ rm.mutex.Unlock()
+ }
+
+ return updated
+}
+
+func (rm *rulesManagerImpl) GetRules() []Rule {
+ rules := make([]Rule, 0, len(rm.rules))
+
+ for _, rule := range rm.rules {
+ rules = append(rules, rule)
+ }
+
+ return rules
+}
+
+func (rm *rulesManagerImpl) FillWithMatchedRules(connection *Connection, clientMatches map[uint][]PatternSlice,
+ serverMatches map[uint][]PatternSlice) {
+}
+
+func (rm *rulesManagerImpl) DatabaseUpdateChannel() chan RulesDatabase {
+ return rm.databaseUpdated
+}
+
+func (rm *rulesManagerImpl) validateAndAddRuleLocal(rule *Rule) error {
if _, alreadyPresent := rm.rulesByName[rule.Name]; alreadyPresent {
return errors.New("rule name must be unique")
}
newPatterns := make(map[string]Pattern)
for i, pattern := range rule.Patterns {
+ if err := rm.validate.Struct(pattern); err != nil {
+ return err
+ }
+
hash := pattern.Hash()
if existingPattern, isPresent := rm.patterns[hash]; isPresent {
rule.Patterns[i] = existingPattern
continue
}
- err := pattern.BuildPattern()
- if err != nil {
+
+ if err := pattern.BuildPattern(); err != nil {
return err
}
pattern.internalID = len(rm.patterns) + len(newPatterns)
@@ -148,18 +201,16 @@ func (rm rulesManagerImpl) validateAndAddRuleLocal(rule *Rule) error {
rm.patterns[key] = value
}
- rm.rules[rule.ID.Hex()] = *rule
+ rm.rules[rule.ID] = *rule
rm.rulesByName[rule.Name] = *rule
return nil
}
-func (rm rulesManagerImpl) generateDatabase(version RowID) error {
- patterns := make([]*hyperscan.Pattern, len(rm.patterns))
- var i int
+func (rm *rulesManagerImpl) generateDatabase(version RowID) error {
+ patterns := make([]*hyperscan.Pattern, 0, len(rm.patterns))
for _, pattern := range rm.patterns {
- patterns[i] = pattern.compiledPattern
- i++
+ patterns = append(patterns, pattern.compiledPattern)
}
database, err := hyperscan.NewStreamDatabase(patterns...)
if err != nil {
@@ -167,31 +218,17 @@ func (rm rulesManagerImpl) generateDatabase(version RowID) error {
}
rm.databaseUpdated <- RulesDatabase{
- database: database,
+ database: database,
databaseSize: len(patterns),
- version: version,
+ version: version,
}
return nil
}
-func (rm rulesManagerImpl) FillWithMatchedRules(connection *Connection, clientMatches map[uint][]PatternSlice,
- serverMatches map[uint][]PatternSlice) {
-}
-
-func (rm rulesManagerImpl) DatabaseUpdateChannel() chan RulesDatabase {
- return rm.databaseUpdated
-}
-
-func (p Pattern) BuildPattern() error {
+func (p *Pattern) BuildPattern() error {
if p.compiledPattern != nil {
return nil
}
- if p.MinOccurrences <= 0 {
- return errors.New("min_occurrences can't be lower than zero")
- }
- if p.MaxOccurrences != -1 && p.MinOccurrences < p.MinOccurrences {
- return errors.New("max_occurrences can't be lower than min_occurrences")
- }
hp, err := hyperscan.ParsePattern(fmt.Sprintf("/%s/", p.Regex))
if err != nil {
@@ -221,6 +258,8 @@ func (p Pattern) BuildPattern() error {
return errors.New("can't validate the pattern")
}
+ p.compiledPattern = hp
+
return nil
}