feat: ship a Fail2Ban filter and guard the spam log format against it - #845
Open
2ndkauboy wants to merge 1 commit into
Open
feat: ship a Fail2Ban filter and guard the spam log format against it#8452ndkauboy wants to merge 1 commit into
2ndkauboy wants to merge 1 commit into
Conversation
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
force-pushed
the
feat/808-fail2ban-filter
branch
from
August 21, 2026 20:45
a898def to
3c54482
Compare
🔍 WordPress Plugin Check Report
📊 Report
❌ Errors (1)📁 readme.txt (1 error)
🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
Deliberately not under
docs/, which.distignorestrips — a filter that does not reach an installed plugin is no filter. The readme FAQ now points at both files with the twocpcommands and thefail2ban-regexinvocation to check the result, instead of only describing the expression.The filter is the short, field-name-anchored form the readme already recommended:
No
datepattern. I checked that against the real thing rather than assuming it:fail2ban-regexautodetects the ISO 8601 timestamp with its offset and strips it before applyingfailregex, so pinning a pattern would only add a way to get it wrong.The part that answers the issue
Five tests in
UpdateSpamLogTestreadfailregexout of the shipped file, strip the timestamp the way Fail2Ban does, and apply it to a line written byUpdateSpamLog::process()— asserting not merely that something matched but which address the jail would ban:post=-,reasons=-), which a reaction with no post producesantispam_bee_spam_log_fields, the case that broke themarked as spam$regex feat: log the spam reasons in the spam log file #797 foundip=must not decide who gets bannedBecause the expression is read from the file and the line from the code, either drifting from the other fails the build. Renaming the
ipfield fails all five: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.dfile holds onlyfailregexandignoreregex; the path lives in the admin'sjail.dlogpath. WithANTISPAM_BEE_LOG_FILEthe path is always explicit, so nothing here needs the constants rework. #807's laterANTISPAM_BEE_SPAM_LOG=truegenerated name stays compatible: it already decided against a date in the file name precisely so Fail2Ban'slogpathglobs keep matching.Tests
165 unit tests, 352 assertions. PHPStan clean,phpcsclean.