Fix blocklist import: detect UnknownJailException and abort early
_is_not_found_error in jail_service did not match the concatenated form 'unknownjailexception' that fail2ban produces when it serialises UnknownJailException, so JailOperationError was raised instead of JailNotFoundError and every ban attempt in the import loop failed individually, skipping all 27 840 IPs before returning an error. Two changes: - Add 'unknownjail' to the phrase list in _is_not_found_error so that UnknownJailException is correctly mapped to JailNotFoundError. - In blocklist_service.import_source, catch JailNotFoundError explicitly and break out of the loop immediately with a warning log instead of retrying on every IP.
This commit is contained in:
@@ -187,6 +187,33 @@ class TestImport:
|
||||
assert result.ips_imported == 0
|
||||
assert result.error is not None
|
||||
|
||||
async def test_import_source_aborts_on_jail_not_found(self, db: aiosqlite.Connection) -> None:
|
||||
"""import_source aborts immediately and records an error when the target jail
|
||||
does not exist in fail2ban instead of silently skipping every IP."""
|
||||
from app.services.jail_service import JailNotFoundError
|
||||
|
||||
content = "\n".join(f"1.2.3.{i}" for i in range(100))
|
||||
session = _make_session(content)
|
||||
source = await blocklist_service.create_source(db, "Missing Jail", "https://mj.test/")
|
||||
|
||||
call_count = 0
|
||||
|
||||
async def _raise_jail_not_found(socket_path: str, jail: str, ip: str) -> None:
|
||||
nonlocal call_count
|
||||
call_count += 1
|
||||
raise JailNotFoundError(jail)
|
||||
|
||||
with patch("app.services.jail_service.ban_ip", side_effect=_raise_jail_not_found):
|
||||
result = await blocklist_service.import_source(
|
||||
source, session, "/tmp/fake.sock", db
|
||||
)
|
||||
|
||||
# Must abort after the first JailNotFoundError — only one ban attempt.
|
||||
assert call_count == 1
|
||||
assert result.ips_imported == 0
|
||||
assert result.error is not None
|
||||
assert "not found" in result.error.lower() or "blocklist-import" in result.error
|
||||
|
||||
async def test_import_all_runs_all_enabled(self, db: aiosqlite.Connection) -> None:
|
||||
"""import_all aggregates results across all enabled sources."""
|
||||
await blocklist_service.create_source(db, "S1", "https://s1.test/")
|
||||
|
||||
Reference in New Issue
Block a user