package discriminator // The heuristic rule_files. It reads the rules of the rule files, which // SWW_RULES_DIR and SWW_RULES_ENABLED name. import ( "context" "time" "sneak.berlin/go/smallwebwaf/internal/bans" "sneak.berlin/go/smallwebwaf/internal/config" "sneak.berlin/go/smallwebwaf/internal/requestlog" "sneak.berlin/go/smallwebwaf/internal/rules" ) // ruleFiles checks the request against the rules of the rule files at now, // notes the ids of those it matches in the log line, adds the tag // exploitsignature when it matches any, and refuses the request // when the last rule it matches refuses it: with ActionBlockedByBlockRule for a // block rule, and for a ban rule with the action for the ban it makes. A ban // rule bans the client's netblock for a clear sign of attack, or in observe // mode raises the alert for the ban // it would have made. Either rule's match is noted as an offence, for the // client's history. Its figures are the rule's id and action. type ruleFiles struct{} func (ruleFiles) name() string { return "rule_files" } func (ruleFiles) check(_ context.Context, rq *request, now time.Time) *verdict { matched := rq.h.rules.Match(rq.in) for _, rule := range matched { rq.line.RuleIDs = append(rq.line.RuleIDs, rule.ID) rq.h.metrics.RuleMatched(rule.ID, rule.Action) } if len(matched) == 0 { return nil } rq.addTag(config.ExploitSignatureTag) // Only the last rule matched can refuse the request. last := matched[len(matched)-1] figures := map[string]any{"rule_id": last.ID, "rule_action": last.Action} switch last.Action { case rules.ActionBlock: rq.blockedByBlockRule = true return &verdict{action: requestlog.ActionBlockedByBlockRule, figures: figures} case rules.ActionBan: rq.attack = true return &verdict{ action: rq.banForAttack(now, bans.Notes{RuleID: last.ID, Target: last.Target}), figures: figures, } default: return nil } }