Commit Graph

11 Commits

Author SHA1 Message Date
e08a16c7dd Refactor: Split blocklist import flow into focused components
Extracted the monolithic import_source() function (776 lines) into focused,
testable components with clear single responsibilities:

- BlocklistDownloader: HTTP download with exponential backoff retry logic
  * Handles transient failures (429, 5xx errors, timeouts)
  * Configurable retry attempts and backoff strategy
  * 93% test coverage

- BlocklistParser: Parse and validate IP addresses
  * Extract valid IPv4/IPv6 addresses from text
  * Skip CIDRs and malformed entries gracefully
  * Separate parsing from validation concerns
  * 100% test coverage

- BanExecutor: Ban execution with error handling
  * Ban IPs via fail2ban socket
  * Stop on JailNotFoundError (jail doesn't exist)
  * Continue on JailOperationError (individual ban failures)
  * 100% test coverage

- BlocklistImportWorkflow: Thin orchestrator
  * Coordinates the download → parse → ban → log flow
  * Pre-warms geo cache with newly banned IPs
  * 96% test coverage

- blocklist_service.py: Maintains public API
  * Source CRUD (create, read, update, delete)
  * URL validation and preview functionality
  * Scheduling configuration and import triggers
  * 92% test coverage

Benefits:
* Each component is independently testable with mock dependencies
* Error handling is explicit and localized
* Components can evolve independently
* Logging is contextual and clear
* Retry and transient error handling are isolated

Testing:
* All 36 existing blocklist_service tests pass
* All 13 blocklist import task tests pass
* Added 17 comprehensive component unit tests
* Combined 96%+ coverage on new modules
* Zero type errors in new code

Documentation:
* Updated Refactoring.md with detailed architecture notes
* Added component architecture diagram to Architekture.md
* Documented ownership and responsibilities of each component

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-27 18:34:11 +02:00
b44b72053a T-11: Validate repository Protocol structural compatibility — minimal approach (Option B)
Problem: Repository modules use structural typing to satisfy Protocol interfaces via
cast(). A function rename, parameter change, or signature mismatch would silently pass
mypy but fail at runtime.

Solution (Option B — minimal):
1. Aligned Protocol signatures in protocols.py with actual implementations:
   - BlocklistRepository: dict[str, object] → dict[str, Any] (matches implementation)
   - ImportLogRepository: dict[str, object] → ImportLogRow (typed model)
   - GeoCacheRepository: dict[str, object] → GeoCacheRow; Iterable → Sequence
   - HistoryArchiveRepository: dict[str, object] → dict[str, Any]
   - ImportLogRepository: async compute_total_pages → sync (matches implementation)

2. Created CI validation script (backend/scripts/validate_repository_protocols.py)
   that runs at build time to ensure all repository modules satisfy their Protocol
   interfaces. Exit 0 if valid, 1 if any mismatch. Detects:
   - Missing functions
   - Parameter count mismatches
   - Type annotation mismatches
   - Return type mismatches

3. Updated backend/app/dependencies.py with explicit docstrings linking each
   get_*_repo() provider to Backend-Development.md § 13.7.1, explaining the
   module-as-Protocol pattern and that it is intentional and validated.

4. Documented the pattern in Backend-Development.md § 13.7.1:
   'Repository Module Pattern — Module-as-Protocol Structural Compatibility'
   explaining why the pattern works, risks (silent breakage), and how the
   validation mitigates it.

5. Fixed type annotation in history_archive_repo.py:
   - get_all_archived_history returns list[dict] → list[dict[str, Any]]
   - Imported Any type

Benefits:
- Prevents silent breakage of repository interfaces
- Formalizes the module-as-Protocol pattern as intentional
- CI validation prevents regressions without refactoring cost
- All repository tests pass (53/53)
- mypy --strict passes on modified files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-25 18:59:49 +02:00
0f261e31c2 Fix infinite re-fetch loop in useJailConfigs
The hook was passing an inline onSuccess callback to useListData, which
included onSuccess in its internal refresh function's dependency array.
This caused refresh to be recreated on each render, which triggered the
useEffect, which fired the fetch, which completed and caused a re-render,
creating an infinite loop.

Wrap onSuccess in useCallback with empty dependencies so it maintains a
stable reference across renders. This allows refresh to be stable when
its dependencies don't change, breaking the cycle.

Add documentation to Refactoring.md explaining the onSuccess stability
requirement for useListData callers.

Also add tests for useJailConfigs to verify it doesn't trigger infinite
refetches with stable onSuccess callback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-22 21:16:42 +02:00
0481810226 Fix open redirect vulnerability in LoginPage
Validate the ?next= query parameter to prevent open redirects to
external URLs. The parameter is validated to ensure it is a relative
path (starts with / but not //) before using it for navigation.
Invalid paths fall back to '/'.

This prevents attackers from crafting login links like /login?next=https://evil.com
that would transparently redirect authenticated users to malicious sites.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-22 21:04:17 +02:00
16687b0520 Mark Task 20 complete and document global exception handlers 2026-04-17 17:07:48 +02:00
13b3fde274 Fix stale activation record on failed jail activation
Record activation only after a successful jail activate request and add regression coverage to prevent stale last_activation state.
2026-04-17 14:53:57 +02:00
0e22d1c425 Move config file exceptions into app.exceptions
Move ConfigDirError, ConfigFileNotFoundError, ConfigFileExistsError, ConfigFileWriteError, and ConfigFileNameError from raw_config_io_service into the shared domain exception module. Update router and tests to import the exceptions from app.exceptions.
2026-04-15 10:28:27 +02:00
328f3575e2 Move Fail2Ban exceptions into central app.exceptions module 2026-04-15 10:22:48 +02:00
ee73373111 backup 2026-03-22 14:24:32 +01:00
a442836c5c refactor: complete Task 2/3 geo decouple + exceptions centralization; mark as done 2026-03-22 14:24:25 +01:00
8f515893ea refactoring tasks 2026-03-16 20:51:07 +01:00