Ban notes name the reputation sources that listed the client (closes #109)
check / check (push) Waiting to run

A ban's notes, in bans.json and in its alert, gain `reputation`: each
blocklist, DNSBL zone or AbuseIPDB that listed the client when the ban
was made, as its `source`, named and ordered as in the request log's
`reputation`, with AbuseIPDB's `score`. It is left out when none did.
README.md shows it in a bans.json example.

Notes now hold a list, so bans can no longer be compared with ==: the
tests compare them with reflect.DeepEqual.

Judgement call: the score is a pointer, so a score of 0, a hit while
SWWAF_ABUSEIPDB_MIN_SCORE is 0, is still written.

Model: opus-5-5
This commit was merged in pull request #114.
This commit is contained in:
2026-10-08 03:08:01 +02:00
parent a6634454cd
commit 04e66d2069
16 changed files with 243 additions and 68 deletions
+17 -3
View File
@@ -75,6 +75,15 @@ const permanentBansJSON = `{
"limit": 1000,
"window": "minute",
"count": 1000.5,
"reputation": [
{
"source": "https://lists.example/drop.txt"
},
{
"source": "abuseipdb",
"score": 100
}
],
"request": {
"time": "2026-10-06T00:00:00Z",
"method": "GET",
@@ -919,7 +928,7 @@ func TestBansWrittenOnceWriteDelayAfterABan(t *testing.T) {
load(t, read)
want := []bans.Ban{first, second}
if got := read.Ledger.Snapshot(); !slices.Equal(got, want) {
if got := read.Ledger.Snapshot(); !reflect.DeepEqual(got, want) {
t.Errorf("bans.json holds %+v, want %+v", got, want)
}
@@ -1808,6 +1817,8 @@ func fill(params state.Params) {
// permanentBan is the ban permanentBansJSON holds.
func permanentBan() bans.Ban {
score := int64(100)
return bans.Ban{
Netblock: netip.MustParsePrefix("2001:db8::/64"),
Start: midnight(),
@@ -1820,6 +1831,9 @@ func permanentBan() bans.Ban {
Limit: 1000,
Window: "minute",
Count: 1000.5,
Reputation: []bans.ReputationHit{
{Source: blocklistURL}, {Source: reputation.AbuseIPDBSource, Score: &score},
},
Request: bans.Request{
Time: midnight(),
Method: "GET",
@@ -1995,10 +2009,10 @@ func edit(t *testing.T, dir, name, content string) {
// wantEqual checks that the entries read back from file are those
// written.
func wantEqual[E comparable](t *testing.T, file string, got, want []E) {
func wantEqual[E any](t *testing.T, file string, got, want []E) {
t.Helper()
if !slices.Equal(got, want) {
if !reflect.DeepEqual(got, want) {
t.Errorf("%s read back\n%+v\nwant\n%+v", file, got, want)
}
}