diff --git a/Docs/Refactoring.md b/Docs/Refactoring.md index 7e1d9a0..58df3f9 100644 --- a/Docs/Refactoring.md +++ b/Docs/Refactoring.md @@ -4,6 +4,12 @@ This document catalogues architecture violations, code smells, and structural is --- +## 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`. diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 8512f47..09178f2 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -81,7 +81,8 @@ export function LoginPage(): React.JSX.Element { const [error, setError] = useState(null); const [submitting, setSubmitting] = useState(false); - const nextPath = searchParams.get("next") ?? "/"; + const next = searchParams.get("next") ?? ""; + const safePath = /^\/(?!\/)/.test(next) ? next : "/"; function handlePasswordChange(ev: ChangeEvent): void { setPassword(ev.target.value); @@ -100,7 +101,7 @@ export function LoginPage(): React.JSX.Element { try { await login(password); - navigate(nextPath, { replace: true }); + navigate(safePath, { replace: true }); } catch (err) { if (err instanceof ApiError && err.status === 401) { setError("Incorrect password. Please try again.");