package proxy import ( "time" "sneak.berlin/go/smallwebwaf/internal/alerts" "sneak.berlin/go/smallwebwaf/internal/config" "sneak.berlin/go/smallwebwaf/internal/requestlog" "sneak.berlin/go/smallwebwaf/internal/waf" ) // checkCoreRuleSet inspects the request with the Core Rule Set, unless // SWWAF_WAF_MODE is off or SWWAF_WAF_EXEMPT_PATHS exempts its path, as // pathExempt decides, and notes the rules it matched and its score in the // log line, and the rules in the metrics. A score at or over // SWWAF_WAF_ANOMALY_THRESHOLD is a match: it raises the waf_block alert, // and in block mode refuses the request, which is an offence its client's // history counts, and so returns ActionWAFBlocked. It returns "" for a // request it does not refuse. func (rq *request) checkCoreRuleSet() string { cfg := rq.h.config if cfg.WAFMode == config.WAFModeOff || pathExempt(rq.in.URL, cfg.WAFExemptPaths) { return "" } start := time.Now() result := rq.h.coreRuleSet.Inspect(rq.in, rq.client) rq.line.DurationWAF = new(requestlog.Milliseconds(time.Since(start))) rq.line.WAFRuleIDs = result.RuleIDs rq.line.WAFScore = &result.Score for _, id := range result.RuleIDs { rq.h.metrics.WAFMatched(cfg.WAFMode, id) } threshold := cfg.WAFAnomalyThreshold if threshold == 0 || result.Score < threshold { return "" } rq.alertWAFBlock(result) if cfg.WAFMode == config.WAFModeDetect { return "" } rq.wafBlocked = true return requestlog.ActionWAFBlocked } // alertWAFBlock raises the waf_block alert for the request, which the Core // Rule Set scored at result, at or over SWWAF_WAF_ANOMALY_THRESHOLD. Its // detail gives the rule ids, the score, the method and the path with the // query, and, for a request that is not refused for it, the mode: detect, // or observe in observe mode. func (rq *request) alertWAFBlock(result waf.Result) { detail := map[string]any{ "rule_ids": result.RuleIDs, "score": result.Score, "method": rq.in.Method, "path": rq.in.URL.RequestURI(), } switch { case rq.h.config.WAFMode == config.WAFModeDetect: detail["mode"] = config.WAFModeDetect case rq.h.config.Observe: detail["mode"] = "observe" } rq.h.alerts.Raise(alerts.Alert{ Event: alerts.EventWAFBlock, Client: rq.client, Netblock: rq.h.clientGroup(rq.client), ASN: rq.line.ASN, ASName: rq.line.ASName, Country: rq.line.Country, Reason: "scored by the Core Rule Set at or over SWWAF_WAF_ANOMALY_THRESHOLD", Detail: detail, }) }