Skip to content

feat: ship a Fail2Ban filter and guard the spam log format against it - #845

Open
2ndkauboy wants to merge 1 commit into
v3from
feat/808-fail2ban-filter
Open

feat: ship a Fail2Ban filter and guard the spam log format against it#845
2ndkauboy wants to merge 1 commit into
v3from
feat/808-fail2ban-filter

Conversation

@2ndkauboy

Copy link
Copy Markdown
Member

Fixes #808.

Antispam Bee wrote a spam log for Fail2Ban but shipped no filter, so every user invented their own regex. #808 makes the case that the important half of the problem is not compatibility but detectability: a jail whose filter stops matching reports no error on either side. Fail2Ban simply stops banning, nothing on our side notices, and the site owner finds out from a rise in spam, if at all. A format version token or a promise to keep the line shape would not have changed that.

What ships

fail2ban/filter.d/antispam-bee.conf
fail2ban/jail.d/antispam-bee.local

Deliberately not under docs/, which .distignore strips — a filter that does not reach an installed plugin is no filter. The readme FAQ now points at both files with the two cp commands and the fail2ban-regex invocation to check the result, instead of only describing the expression.

The filter is the short, field-name-anchored form the readme already recommended:

failregex = ^.*?\bip=<HOST>\b

No datepattern. I checked that against the real thing rather than assuming it:

Failregex: 3 total
|   1) [3] ^.*?\bip=<HOST>\b

Date template hits:
|  [3] {^LN-BEG}ExYear(?P<_sep>[-/.])Month(?P=_sep)Day(?:T|  ?)24hour:Minute:Second(?:[.,]Microseconds)?(?:\s*Zone offset)?

Lines: 3 lines, 0 ignored, 3 matched, 0 missed

fail2ban-regex autodetects the ISO 8601 timestamp with its offset and strips it before applying failregex, so pinning a pattern would only add a way to get it wrong.

The part that answers the issue

Five tests in UpdateSpamLogTest read failregex out of the shipped file, strip the timestamp the way Fail2Ban does, and apply it to a line written by UpdateSpamLog::process() — asserting not merely that something matched but which address the jail would ban:

  • a logged comment
  • a line that is all placeholders (post=-, reasons=-), which a reaction with no post produces
  • an IPv6 address
  • a field appended through antispam_bee_spam_log_fields, the case that broke the marked as spam$ regex feat: log the spam reasons in the spam log file #797 found
  • the greedy trap: a later field whose value contains ip= must not decide who gets banned

Because the expression is read from the file and the line from the code, either drifting from the other fails the build. Renaming the ip field fails all five:

Tests: 5, Assertions: 25, Failures: 5.

That is the guard #808 asked for, and the reason the readme now tells users to prefer the shipped filter over a hand-written one.

Independent of #807

Worth saying, since #808 was filed expecting to wait for it. A filter.d file holds only failregex and ignoreregex; the path lives in the admin's jail.d logpath. With ANTISPAM_BEE_LOG_FILE the path is always explicit, so nothing here needs the constants rework. #807's later ANTISPAM_BEE_SPAM_LOG=true generated name stays compatible: it already decided against a date in the file name precisely so Fail2Ban's logpath globs keep matching.

Tests

165 unit tests, 352 assertions. PHPStan clean, phpcs clean.

@2ndkauboy 2ndkauboy added this to the 3.0.0-beta.3 milestone Aug 21, 2026
@2ndkauboy 2ndkauboy added the v3 This issue is for the new version (v3) of the plugin label Aug 21, 2026
Antispam Bee wrote a spam log for Fail2Ban but shipped no filter, so every user
invented their own regex. That had two consequences: we could not know who a
format change affects, and breakage was silent on both sides — a jail whose filter
stops matching reports no error, it simply stops banning, and the site owner finds
out from a rise in spam, if at all.

`fail2ban/filter.d/antispam-bee.conf` and `fail2ban/jail.d/antispam-bee.local` now
ship with the plugin, outside the paths `.distignore` strips, so they reach an
installed copy. The readme points at them instead of only describing the
expression, and says how to check a jail with `fail2ban-regex`.

The filter needs no `datepattern`: verified against the real `fail2ban-regex`,
which picks `{^LN-BEG}...Zone offset` for the ISO 8601 timestamp and strips it
before applying `failregex`.

## The part that makes a mismatch detectable

Compatibility alone does not help, because nothing notices when a filter stops
matching. So five tests in `UpdateSpamLogTest` read `failregex` out of the shipped
file, strip the timestamp the way Fail2Ban does, and apply it to a line written by
`UpdateSpamLog::process()` — asserting not just a match but which address the jail
would ban. They cover the placeholder-only line, an IPv6 address, a field appended
through `antispam_bee_spam_log_fields`, and the greedy trap where a later field
value contains `ip=`.

Renaming the `ip` field fails all five. A format change that would stop users'
jails matching can no longer pass CI unnoticed, which is the reason to prefer the
shipped filter over a hand-written one.

Fixes #808
@2ndkauboy
2ndkauboy force-pushed the feat/808-fail2ban-filter branch from a898def to 3c54482 Compare August 21, 2026 20:45
@github-actions

Copy link
Copy Markdown

🔍 WordPress Plugin Check Report

❌ Status: Failed

📊 Report

🎯 Total Issues ❌ Errors ⚠️ Warnings
1 1 0

❌ Errors (1)

📁 readme.txt (1 error)
📍 Line 🔖 Check 💬 Message
0 outdated_tested_upto_header Tested up to: 7.0 < 7.1. The "Tested up to" value in your plugin is not set to the current version of WordPress. This means your plugin will not show up in searches, as we require plugins to be compatible and documented as tested up to the most recent version of WordPress.

🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check

@2ndkauboy 2ndkauboy modified the milestones: 3.0.0-beta.3, 3.0.0-RC.1 Aug 23, 2026
@2ndkauboy 2ndkauboy modified the milestones: 3.0.0-RC.1, 3.0.0-beta.4 Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v3 This issue is for the new version (v3) of the plugin

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant