Files
BanGUI/Docs/Refactoring.md
Lukas 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

2.2 KiB

BanGUI — Architecture Issues & Refactoring Plan

This document catalogues architecture violations, code smells, and structural issues found during a full project review. Issues are grouped by category and prioritised.


Security Fixes

  • Fixed open redirect vulnerability in frontend/src/pages/LoginPage.tsx by validating the ?next= parameter to ensure it is a relative path (starts with / but not //). The validation regex /^\/(?!\/)/.test(next) prevents protocol-relative URLs and external redirects. Invalid paths fall back to "/".

Completed Refactors

  • Moved Fail2BanConnectionError and Fail2BanProtocolError from backend/app/utils/fail2ban_client.py into backend/app/exceptions.py. Updated all router, service, and test call sites to import these domain exceptions from app.exceptions and retained backward compatibility through re-exporting in app.utils.fail2ban_client.
  • Moved config file exceptions (ConfigDirError, ConfigFileNotFoundError, ConfigFileExistsError, ConfigFileWriteError, ConfigFileNameError) from backend/app/services/raw_config_io_service.py into backend/app/exceptions.py. Updated router and tests to import the shared domain exceptions from app.exceptions.
  • Added global domain exception handlers to backend/app/main.py so domain exceptions like JailNotFoundError, ConfigValidationError, and ConfigWriteError map consistently to 404, 400, and 500 responses.
  • Fixed stale activation tracking in backend/app/routers/jail_config.py by recording last_activation only after a successful jail activation and preventing a failed activation attempt from leaving a stale runtime state record.
  • Fixed infinite re-fetch loop in frontend/src/hooks/useJailConfigs.ts by wrapping the onSuccess callback in useCallback with empty dependencies. The bug occurred because useListData includes onSuccess in its internal refresh function's dependency array; an inline callback created a new reference on each render, causing refresh to be recreated, which triggered the useEffect again, leading to an unbounded fetch loop. Callers of useListData must always wrap onSuccess callbacks in useCallback to maintain reference stability.