unless proven otherwise, we will prefer injection on the `ruser` field over the `user` field;
normally by invalid user field `user` not logged at all, see for example logging from `pam_syslog(pamh, LOG_NOTICE, "authentication failure; " ...` in pam_unix-module source code:
52b33a56a9/modules/pam_unix/support.c (L822)
- fixed prefix-line for malformed RFC 5424, vulnerable against ReDoS (catastrophic backtracking) by non-conform logger or artificial "injection" attempt on STRUCTURED-DATA, unescaped new-line in PARAM-VALUE, etc (GHSA-c2mg-2r7g-c3wp)
1.1.1 was released (2026/08/15) while this branch was stale; the
new-feature and deprecation entries were still sitting under that
now-historical section instead of the current dev section.
Also drops a stray trailing blank line left in servertestcase.py from
an earlier test-file truncation.
Per review: fail2ban/server/xarfreport.py had exactly one consumer
(this action), and the only other Python action (smtp.py) is fully
self-contained with no bespoke helper module in fail2ban/server/. Move
the generator (build_login_attack and friends) directly into xarf.py
to match that convention; extract it again if a second XARF-based
transport ever needs to share it.
Test classes now load the generator functions off the dynamically
loaded action module (fail2ban/tests/action_d/test_xarf.py already
does this for the action itself) instead of importing a package
module. The module-level logger is now named explicitly
("fail2ban.xarf") since __name__ is just "xarf" for a standalone-
loaded module and would otherwise sit outside the fail2ban.* logger
hierarchy fail2ban's log handlers (and test log capture) attach to.
Addresses review feedback on the xarf action/generator:
- xarf.py: anchor source_port extraction to the banned IP itself,
rather than taking the last "port N" match in the log line. sshd's
verbose "Connection from <ip> port NNNNN on 127.0.0.1 port 22" line
otherwise yielded the local listening port instead of the attacker's.
- xarf.py: replace int(self.port) with a real parser for fail2ban's
port option (service name, comma list, or range) so destination_port
is populated for the values jail.conf actually passes (`ssh`,
`http,https`, `0:65535`), and never emits destination_port: 0, which
violates the schema's minimum of 1.
- xarf.py: skip reporting entirely when a ban ticket carries no log
evidence (e.g. a manual `fail2ban-client set <jail> banip <ip>`),
instead of sending a report claiming attempt_count: 0.
- xarf.py: pass -f <envelope_from> to the mail command by default (new
mailargs option to override), and resolve the default envelope
sender via DNSUtils.getHostname() instead of a raw socket.getfqdn().
Fixes SPF/DMARC alignment and the localhost/PTR edge cases.
- jail.conf: pass sender="%(sender)s" into action_xarf_v4, matching
action_mw/action_mwl/legacy action_xarf; it was previously dropped
on the floor.
- xarfreport.py: catch more than ImportError around the optional xarf
import - an incompatible install (e.g. pydantic mismatch) could
otherwise crash the whole action instead of falling back to stdlib.
- xarfreport.py: the xarf library ignores the timestamp we pass to
create_report() and stamps its own, leaving the document internally
inconsistent with first_seen; now enforced on the returned dict.
- xarfreport.py: distinguish "library unavailable/erroring" (fall back
to stdlib and still send) from "library validated and rejected the
report" (XarfValidationError; drop the report rather than send the
same invalid data unvalidated via the stdlib builder).
- xarf.py: fix malformed emacs modeline.
- ChangeLog: add missing gh-3820 refs.
10 new regression tests in fail2ban/tests/action_d/test_xarf.py, one
per bug above (including a real broken-xarf-install import-guard test
via module reload).
Move XarfV4ActionTest out of servertestcase.py and the generator tests
out of the standalone xarfreporttestcase.py into a single
action_d/test_xarf.py, matching the existing convention for testing
python actions (see test_smtp.py). Files under action_d/test_*.py are
auto-discovered, so the manual registration in utils.py gatherTests()
is no longer needed.
Add _iso, _aInfoToData, _sendmail helpers and wire ban() to resolve
abuse contacts, build XARF v4 report + email, set To header, and
send via sendmail. Short-circuits when no contact found.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Create xarfreport module with _build_evidence_stdlib() function to
encode log text as base64, compute SHA256 hash, and produce XARF v4
evidence dictionary. Test validates all fields including payload
decoding, hash format, and size computation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gitlab deprecated application.log in 15.10 in favour of the structured
application_json.log. Add a mode option so the filter can read either
format; text stays the default so existing setups keep working.
Closes#3566
The previous regex was written against an assumed field order/spacing
that doesn't match what Cowrie actually emits: no space after ":",
and "eventid" is not the first key (order is session, protocol,
src_ip, ..., eventid, ..., message). Rework the failregex to match
real-world json output, and correct the datepattern to wrap only the
timestamp value (not the "timestamp": key) in the capture group, since
fail2ban excises the whole captured group from the line before
failregex is applied - wrapping the key too left a broken ",," gap in
the JSON that never matched.
Verified against real Cowrie honeypot log samples (session.connect,
client.version, client.kex, login.success, log.closed) and the
existing test-suite (101 filter sample-regex tests pass).
Replace the custom Python Filter/backend hack with a plain regex
filter, per sebres's review: fail2ban filters shouldn't ship a
custom backend-style parser under config/filter.d, and the previous
prefregex/failregex pair was a no-op catch-all that didn't actually
filter anything.
- Drop cowrie_json.py entirely; parse the JSON log line directly
with a bounded, anchored failregex (no unbounded .* catch-alls)
using <ADDR> instead of <HOST> since only an IP is expected.
- Add the required sample log test file
(fail2ban/tests/files/logs/cowrie_json) covering matched,
non-matching event, and malformed lines.