crowdsec/pkg/types/event.go

298 lines
7.4 KiB
Go

package types
import (
"fmt"
"regexp"
"time"
log "github.com/sirupsen/logrus"
"github.com/antonmedv/expr/vm"
"github.com/crowdsecurity/crowdsec/pkg/models"
)
const (
LOG = iota
OVFLW
)
/*
1. If user triggered a rule that is for a CVE, that has high confidence and that is blocking, ban
2. If user triggered 3 distinct rules with medium confidence accross 3 different requests, ban
any(evt.Waf.ByTag("CVE"), {.confidence == "high" && .action == "block"})
len(evt.Waf.ByTagRx("*CVE*").ByConfidence("high").ByAction("block")) > 1
*/
type WaapEvent []map[string]interface{}
type Field string
func (f Field) String() string {
return fmt.Sprintf("%s", f)
}
const (
ID Field = "id"
RuleType Field = "rule_type"
Tags Field = "tags"
File Field = "file"
Confidence Field = "confidence"
Revision Field = "revision"
SecMark Field = "secmark"
Accuracy Field = "accuracy"
Msg Field = "msg"
Severity Field = "severity"
Kind Field = "kind"
)
// getters
func (w WaapEvent) GetField(field Field) []interface{} {
ret := make([]interface{}, 0)
for _, rule := range w {
ret = append(ret, rule[field.String()])
}
return ret
}
func (w WaapEvent) GetURI() string {
for _, rule := range w {
return rule["uri"].(string)
}
return ""
}
func (w WaapEvent) GetMethod() string {
for _, rule := range w {
return rule["method"].(string)
}
return ""
}
func (w WaapEvent) GetRuleIDs() []int {
ret := make([]int, 0)
for _, rule := range w {
ret = append(ret, rule["id"].(int))
}
return ret
}
func (w WaapEvent) Kinds() []string {
ret := make([]string, 0)
for _, rule := range w {
exists := false
for _, val := range ret {
if val == rule["kind"] {
exists = true
break
}
}
if !exists {
ret = append(ret, rule["kind"].(string))
}
}
return ret
}
// filters
func (w WaapEvent) ByID(id int) WaapEvent {
waap := WaapEvent{}
for _, rule := range w {
if rule["id"] == id {
waap = append(waap, rule)
}
}
return waap
}
func (w WaapEvent) ByKind(kind string) WaapEvent {
waap := WaapEvent{}
for _, rule := range w {
if rule["kind"] == kind {
waap = append(waap, rule)
}
}
return waap
}
func (w WaapEvent) ByTags(match []string) WaapEvent {
waap := WaapEvent{}
for _, rule := range w {
for _, tag := range rule["tags"].([]string) {
for _, match_tag := range match {
if tag == match_tag {
waap = append(waap, rule)
break
}
}
}
}
return waap
}
func (w WaapEvent) ByTag(match string) WaapEvent {
waap := WaapEvent{}
for _, rule := range w {
for _, tag := range rule["tags"].([]string) {
if tag == match {
waap = append(waap, rule)
break
}
}
}
return waap
}
func (w WaapEvent) ByTagRx(rx string) WaapEvent {
waap := WaapEvent{}
re := regexp.MustCompile(rx)
if re == nil {
return waap
}
for _, rule := range w {
for _, tag := range rule["tags"].([]string) {
log.Infof("ByTagRx: %s = %s -> %t", rx, tag, re.MatchString(tag))
if re.MatchString(tag) {
waap = append(waap, rule)
break
}
}
}
return waap
}
func (w WaapEvent) ByDisruptiveness(is bool) WaapEvent {
log.Infof("%s", w)
wap := WaapEvent{}
for _, rule := range w {
if rule["disruptive"] == is {
wap = append(wap, rule)
}
}
log.Infof("ByDisruptiveness(%t) -> %d", is, len(wap))
return wap
}
func (w WaapEvent) BySeverity(severity string) WaapEvent {
wap := WaapEvent{}
for _, rule := range w {
if rule["severity"] == severity {
wap = append(wap, rule)
}
}
log.Infof("BySeverity(%s) -> %d", severity, len(wap))
return wap
}
func (w WaapEvent) ByAccuracy(accuracy string) WaapEvent {
wap := WaapEvent{}
for _, rule := range w {
if rule["accuracy"] == accuracy {
wap = append(wap, rule)
}
}
log.Infof("ByAccuracy(%s) -> %d", accuracy, len(wap))
return wap
}
// Event is the structure representing a runtime event (log or overflow)
type Event struct {
/* is it a log or an overflow */
Type int `yaml:"Type,omitempty" json:"Type,omitempty"` //Can be types.LOG (0) or types.OVFLOW (1)
ExpectMode int `yaml:"ExpectMode,omitempty" json:"ExpectMode,omitempty"` //how to buckets should handle event : types.TIMEMACHINE or types.LIVE
Whitelisted bool `yaml:"Whitelisted,omitempty" json:"Whitelisted,omitempty"`
WhitelistReason string `yaml:"WhitelistReason,omitempty" json:"whitelist_reason,omitempty"`
//should add whitelist reason ?
/* the current stage of the line being parsed */
Stage string `yaml:"Stage,omitempty" json:"Stage,omitempty"`
/* original line (produced by acquisition) */
Line Line `yaml:"Line,omitempty" json:"Line,omitempty"`
/* output of groks */
Parsed map[string]string `yaml:"Parsed,omitempty" json:"Parsed,omitempty"`
/* output of enrichment */
Enriched map[string]string `yaml:"Enriched,omitempty" json:"Enriched,omitempty"`
/* output of Unmarshal */
Unmarshaled map[string]interface{} `yaml:"Unmarshaled,omitempty" json:"Unmarshaled,omitempty"`
/* Overflow */
Overflow RuntimeAlert `yaml:"Overflow,omitempty" json:"Alert,omitempty"`
Time time.Time `yaml:"Time,omitempty" json:"Time,omitempty"` //parsed time `json:"-"` ``
StrTime string `yaml:"StrTime,omitempty" json:"StrTime,omitempty"`
StrTimeFormat string `yaml:"StrTimeFormat,omitempty" json:"StrTimeFormat,omitempty"`
MarshaledTime string `yaml:"MarshaledTime,omitempty" json:"MarshaledTime,omitempty"`
Process bool `yaml:"Process,omitempty" json:"Process,omitempty"` //can be set to false to avoid processing line
Waap WaapEvent `yaml:"Waap,omitempty" json:"Waap,omitempty"`
/* Meta is the only part that will make it to the API - it should be normalized */
Meta map[string]string `yaml:"Meta,omitempty" json:"Meta,omitempty"`
}
func (e *Event) GetType() string {
if e.Type == OVFLW {
return "overflow"
} else if e.Type == LOG {
return "log"
} else {
log.Warningf("unknown event type for %+v", e)
return "unknown"
}
}
func (e *Event) GetMeta(key string) string {
if e.Type == OVFLW {
for _, alert := range e.Overflow.APIAlerts {
for _, event := range alert.Events {
if event.GetMeta(key) != "" {
return event.GetMeta(key)
}
}
}
} else if e.Type == LOG {
for k, v := range e.Meta {
if k == key {
return v
}
}
}
return ""
}
// Move in leakybuckets
const (
Undefined = ""
Ip = "Ip"
Range = "Range"
Filter = "Filter"
Country = "Country"
AS = "AS"
)
// Move in leakybuckets
type ScopeType struct {
Scope string `yaml:"type"`
Filter string `yaml:"expression"`
RunTimeFilter *vm.Program
}
type RuntimeAlert struct {
Mapkey string `yaml:"MapKey,omitempty" json:"MapKey,omitempty"`
BucketId string `yaml:"BucketId,omitempty" json:"BucketId,omitempty"`
Whitelisted bool `yaml:"Whitelisted,omitempty" json:"Whitelisted,omitempty"`
Reprocess bool `yaml:"Reprocess,omitempty" json:"Reprocess,omitempty"`
Sources map[string]models.Source `yaml:"Sources,omitempty" json:"Sources,omitempty"`
Alert *models.Alert `yaml:"Alert,omitempty" json:"Alert,omitempty"` //this one is a pointer to APIAlerts[0] for convenience.
//APIAlerts will be populated at the end when there is more than one source
APIAlerts []models.Alert `yaml:"APIAlerts,omitempty" json:"APIAlerts,omitempty"`
}
func (r RuntimeAlert) GetSources() []string {
ret := make([]string, 0)
for key := range r.Sources {
ret = append(ret, key)
}
return ret
}