Trap paths, and the error burst banning a client refused too often (closes #115)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
SWWAF_TRAP_PATHS: a request whose path, as a path rule sees it, is one of them is a clear sign of attack, banned as a ban rule's match is; the ban's notes give its trap_path. Checked after the rate limits, before the rule files. SWWAF_ERROR_BURST_THRESHOLD (default 30, or off): more refusals in a minute after a block or ban rule or a trap path, or for a missing or wrong token, ban the client as a broken limit does. Counted in clients.json's minute_refusals; limit_hit error_burst, notes kind refusals. A token refusal is now the offence token_refused, and smallwebwaf_offences_total counts every kind the history does. Judgement call: the threshold is not lowered by a client's limit percentage. Model: opus-5-5
This commit is contained in:
@@ -65,6 +65,7 @@ func TestLoadEmptiesBucketsWhoseTimeHasPassed(t *testing.T) {
|
||||
limiter := ratelimit.New(ratelimit.Limits{}, tableSize)
|
||||
limiter.Count(client, start, whole)
|
||||
limiter.CountBytes(client, start, 5, whole)
|
||||
limiter.CountRefusal(client, start, limit)
|
||||
limiter.AddToHistory(client, start, ratelimit.Request{Forwarded: true})
|
||||
|
||||
loaded := func(now time.Time) ratelimit.Client {
|
||||
@@ -76,9 +77,9 @@ func TestLoadEmptiesBucketsWhoseTimeHasPassed(t *testing.T) {
|
||||
return after.Snapshot()[0]
|
||||
}
|
||||
|
||||
// Two minutes on, the window that ends then covers neither of the
|
||||
// minute's buckets, of requests and of bytes, which are emptied; the
|
||||
// hour's and the day's stay, and so does the history.
|
||||
// Two minutes on, the window that ends then covers none of the
|
||||
// minute's buckets, of requests, of bytes and of refusals, which are
|
||||
// emptied; the hour's and the day's stay, and so does the history.
|
||||
got := loaded(start.Add(2 * time.Minute))
|
||||
if got.Minute != (ratelimit.Buckets{}) || got.Hour.Current != 1 ||
|
||||
got.Day.Current != 1 || got.History.Requests != 1 {
|
||||
@@ -91,11 +92,17 @@ func TestLoadEmptiesBucketsWhoseTimeHasPassed(t *testing.T) {
|
||||
got.MinuteBytes, got.HourBytes, got.DayBytes)
|
||||
}
|
||||
|
||||
if got.MinuteRefusals != (ratelimit.Buckets{}) {
|
||||
t.Errorf("loaded two minutes on with buckets of refusals %+v",
|
||||
got.MinuteRefusals)
|
||||
}
|
||||
|
||||
// A moment before, the window still covers some of the earlier one.
|
||||
got = loaded(start.Add(2*time.Minute - time.Nanosecond))
|
||||
if got.Minute.Current != 1 || got.MinuteBytes.Current != 5 {
|
||||
t.Errorf("loaded just under two minutes on with minute buckets %+v and %+v",
|
||||
got.Minute, got.MinuteBytes)
|
||||
if got.Minute.Current != 1 || got.MinuteBytes.Current != 5 ||
|
||||
got.MinuteRefusals.Current != 1 {
|
||||
t.Errorf("loaded just under two minutes on with minute buckets %+v, %+v "+
|
||||
"and %+v", got.Minute, got.MinuteBytes, got.MinuteRefusals)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user