A ban in bans.json that ends at or before its start is refused, as other malformed entries are (closes #150)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
A netblock's next ban for a broken limit within SWWAF_LIMIT_BAN_REPEAT_WINDOW lasts three times its last limit or admin ban. A hand-written entry with an expires at or before its start made that length zero or less, so the next limit ban refused nothing. The bans.json checks now refuse such an entry: it stops the start, and an edit holding one is set aside as bans.json.bad while running. README's "State files" lists it with the other refused entries. Model: opus-5-5
This commit is contained in:
@@ -61,6 +61,7 @@ var (
|
||||
// errMissing is for an entry without a field it needs.
|
||||
errMissing = errors.New("has no")
|
||||
errCause = errors.New("is not limit, attack, admin or crowdsec")
|
||||
errExpires = errors.New("is not after its start")
|
||||
errDestination = errors.New("is not webhook, slack or ntfy")
|
||||
errScope = errors.New("is not client, net, asn, total or watch")
|
||||
errWaitingList = errors.New(`waiting is a list, but now lists the alerts by ` +
|
||||
@@ -646,8 +647,10 @@ func (e BanEntry) ban() bans.Ban {
|
||||
// client, a start, from which the length of the netblock's next ban is
|
||||
// worked out, or an expires, which would make it permanent. A permanent
|
||||
// ban's expires is null, which Bans cannot tell from a missing one, so
|
||||
// each expires is read again as written. A cause other than limit,
|
||||
// attack, admin or crowdsec, most likely misspelt, is refused too.
|
||||
// each expires is read again as written. An expires not after the start
|
||||
// is refused too, since the netblock's next ban for a broken limit, three
|
||||
// times as long as this one, would then refuse nothing; and so is a cause
|
||||
// other than limit, attack, admin or crowdsec, most likely misspelt.
|
||||
func (f *bansFile) check(data []byte) error {
|
||||
var written struct {
|
||||
Bans []struct {
|
||||
@@ -668,6 +671,8 @@ func (f *bansFile) check(data []byte) error {
|
||||
return missing(i, "start")
|
||||
case written.Bans[i].Expires == nil:
|
||||
return missing(i, "expires")
|
||||
case entry.Expires != nil && !entry.Expires.After(entry.Start):
|
||||
return fmt.Errorf("entry %d's expires %w", i+1, errExpires)
|
||||
case entry.Cause != "" && entry.Cause != bans.CauseLimit &&
|
||||
entry.Cause != bans.CauseAttack && entry.Cause != bans.CauseAdmin &&
|
||||
entry.Cause != bans.CauseCrowdSec:
|
||||
|
||||
@@ -865,6 +865,21 @@ func TestBanWithAnotherCauseStopsTheStart(t *testing.T) {
|
||||
`: entry 4's cause "atack" is not limit, attack, admin or crowdsec`)
|
||||
}
|
||||
|
||||
func TestBanThatDoesNotEndAfterItsStartStopsTheStart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// The first ban ends a second after its start; the second ends as it
|
||||
// starts, or an hour before.
|
||||
for _, expires := range []string{`"2026-10-06T00:00:00Z"`, `"2026-10-05T23:00:00Z"`} {
|
||||
wantRefused(t, bansJSON, `{"version": 1, "bans": [`+
|
||||
`{"netblock": "203.0.113.9/32", "start": "2026-10-06T00:00:00Z", `+
|
||||
`"expires": "2026-10-06T00:00:01Z"}, `+
|
||||
`{"netblock": "203.0.113.10/32", "start": "2026-10-06T00:00:00Z", `+
|
||||
`"expires": `+expires+`}]}`,
|
||||
`: entry 2's expires is not after its start`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnknownVersionStopsTheStart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user