fix(security): extract & reuse getSafeRedirectPath, plug TopUpAmount returnTo

TelegramRedirect already had a local getSafeRedirectUrl helper that
collapsed protocol-relative URLs, absolute URLs, exotic schemes, and
URL-encoded forms down to '/'. TopUpAmount.handleSuccess was navigating
straight to a user-supplied returnTo query param without that filter —
not externally exploitable through react-router's navigate() (it doesn't
trigger an external nav), but a crafted link could produce ugly path
artefacts ('?returnTo=https://evil.com' would land the user at
/balance/top-up/<method>/https://evil.com).

Hoist the helper to src/utils/safeRedirect.ts, rename to
getSafeRedirectPath, reuse it in TelegramRedirect, and wrap TopUpAmount's
returnTo through it before navigate().
This commit is contained in:
c0mrade
2026-05-26 16:13:33 +03:00
parent b6613ae4c9
commit 424a19344d
3 changed files with 40 additions and 22 deletions

View File

@@ -13,6 +13,7 @@ import { staggerContainer, staggerItem } from '@/components/motion/transitions';
import type { PaymentMethod, PaymentMethodOption } from '../types';
import BentoCard from '../components/ui/BentoCard';
import { saveTopUpPendingInfo } from '../utils/topUpStorage';
import { getSafeRedirectPath } from '../utils/safeRedirect';
import { copyToClipboard } from '@/utils/clipboard';
// Icons
@@ -145,7 +146,12 @@ export default function TopUpAmount() {
}, [navigate]);
const handleSuccess = useCallback(() => {
navigate(returnTo || '/balance', { replace: true });
// returnTo arrives via query string — validate as an in-app path before
// navigate(), otherwise an absolute or encoded URL produces ugly
// path artefacts in the URL bar. The validator returns '/' for invalid
// input; treat that case as "no returnTo" and use the /balance default.
const safe = getSafeRedirectPath(returnTo);
navigate(returnTo && safe !== '/' ? safe : '/balance', { replace: true });
}, [navigate, returnTo]);
// Keyboard: Escape to go back