check / check (push) Failing after 1m14s
Every setting of discriminator and rproxy is read through viper, in place of the os.LookupEnv function Main passed down. NewViper turns AutomaticEnv and AllowEmptyEnv on, so a setting that is set but empty reads as set. A setting may also be given as a file, the _FILE form, which rproxy's settings now have too; rproxy still writes nothing to disk. The settings are renamed. Discriminator's take the prefix SWW_WAF_, rproxy's take SWW_RPROXY_, and the shared SWW_COMMAND keeps SWW_. The Core Rule Set's settings take SWW_WAF_CORE_RULE_SET_ rather than SWW_WAF_WAF_. Model: deepseek-v4-pro (first version); opus-5-5 (rework)
63 lines
1.9 KiB
Go
63 lines
1.9 KiB
Go
package discriminator
|
|
|
|
// The heuristic rule_files. It reads the rules of the rule files, which
|
|
// SWW_WAF_RULES_DIR and SWW_WAF_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
|
|
}
|
|
}
|