check / check (push) Waiting to run
Each check servewaf runs on a request is a type in its own file behind one interface, heuristic, and is registered in one of four ordered lists in heuristics.go. Order and behaviour are unchanged. Every refusal now logs heuristic, the name of the heuristic that made it, and heuristic_figures, the figures it acted on. A ban by refusal_limit or byte_limits after the answer logs report_heuristic and report_heuristic_figures instead, so a line can name both. All four are in docs/request-log.md, and SPEC.md names each heuristic at its step. Model: opus-5-5
50 lines
1.5 KiB
Go
50 lines
1.5 KiB
Go
package servewaf
|
|
|
|
// The heuristic crowdsec. It reads SWWAF_CROWDSEC_LAPI_URL, and the decisions
|
|
// through the reputation lists.
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"sneak.berlin/go/smallwebwaf/internal/bans"
|
|
"sneak.berlin/go/smallwebwaf/internal/reputation"
|
|
"sneak.berlin/go/smallwebwaf/internal/requestlog"
|
|
)
|
|
|
|
// crowdSec refuses a request when a decision of the CrowdSec decision list on
|
|
// the client is in force at now. It then notes the list, as noteHit does, and
|
|
// bans the client until that decision ends. Its figures are the decision's
|
|
// scenario and when it ends.
|
|
type crowdSec struct{}
|
|
|
|
func (crowdSec) name() string { return "crowdsec" }
|
|
|
|
func (crowdSec) check(_ context.Context, rq *request, now time.Time) *verdict {
|
|
decision, listed := rq.crowdSecDecisionInForce(now)
|
|
if !listed {
|
|
return nil
|
|
}
|
|
|
|
rq.noteHit(bans.ReputationHit{Source: rq.h.config.CrowdSecDecisionsURL},
|
|
"listed by the CrowdSec decision list")
|
|
rq.banForCrowdSec(now, decision)
|
|
|
|
return &verdict{
|
|
action: requestlog.ActionBanned,
|
|
figures: map[string]any{
|
|
"scenario": decision.Scenario,
|
|
"expires": requestlog.FormatTime(decision.Expires),
|
|
},
|
|
}
|
|
}
|
|
|
|
// crowdSecDecisionInForce returns the decision of the CrowdSec decision list on
|
|
// the client that is in force at now, and reports whether there is one, which
|
|
// the heuristic crowdsec refuses the request for.
|
|
func (rq *request) crowdSecDecisionInForce(
|
|
now time.Time,
|
|
) (reputation.Decision, bool) {
|
|
return rq.h.lists.CrowdSecDecision(rq.client, now)
|
|
}
|