SkateSpots/src/components/RouteErrorBoundary.tsx
shakespeare.diy 40e4e53400 Hardening: https-only URLs, route-level crash resilience, cleaner errors (1.4.3)
Security & reliability audit follow-up. The app was already well-hardened
(strict CSP synced across index.html/_headers/netlify.toml, careful
textContent/escapeHtml in the Leaflet DOM, broad sanitizeUrl usage,
consistent author-filtering on trust-sensitive queries). This closes the
remaining gaps:

- sanitizeUrl is now HTTPS-only: bare http:// links are upgraded to
  https:// (not silently dropped); every other scheme (javascript:,
  data:, file:, relative) is rejected. Removes a downgrade/mixed-content/
  IP-leak vector and matches the native HTTPS-only WebView. Added a
  sanitizeCssString() allowlist helper for future CSS interpolation.

- Route-level ErrorBoundary (RouteErrorBoundary) wraps the routed pages,
  keyed by pathname: a crash in one page now shows a recoverable
  "this page slammed → go back" message while the header/nav/rest of the
  app keep working, instead of the single top-level boundary blanking the
  ENTIRE app. Resets automatically on navigation.

- ErrorBoundary fallback no longer leaks stack traces to users (full
  detail still logged to console for us); redesigned on-brand ("Slammed",
  neon, reassures that account + data are safe). Updated its test.

Author-filtering spot-check across admin/newsletter/featured/crew/stash
paths found no gaps — all trust-sensitive queries correctly constrain
authors.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-09-09 12:40:43 +07:00

52 lines
1.9 KiB
TypeScript

import { useLocation } from 'react-router-dom';
import { ErrorBoundary } from '@/components/ErrorBoundary';
/**
* Per-route error boundary.
*
* The app has a single top-level ErrorBoundary (in main.tsx) as a last resort,
* but on its own that means ANY component throwing blanks the ENTIRE app. This
* wrapper adds a second, inner boundary around the routed page content so a
* crash in one page shows a recoverable message while the app keeps running.
*
* It is keyed by the current pathname: navigating to a different route remounts
* the boundary and clears the error automatically, so a broken page never
* "sticks" once the user moves away.
*/
export function RouteErrorBoundary({ children }: { children: React.ReactNode }) {
const location = useLocation();
return (
<ErrorBoundary
key={location.pathname}
fallback={
<div className="min-h-[60vh] flex items-center justify-center p-6">
<div className="max-w-sm w-full text-center space-y-5">
<div className="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-[#ff0040]/15 border border-[#ff0040]/30 text-3xl">
🛹
</div>
<div className="space-y-1.5">
<h2 className="text-xl font-black uppercase text-[#ff0040]">
This page slammed
</h2>
<p className="text-sm text-gray-400">
Something on this screen tripped up. Your account and data are safe.
Head back and try again.
</p>
</div>
<button
onClick={() => window.history.back()}
className="w-full px-4 py-3 rounded-full font-black uppercase bg-[#ff0040] text-white hover:bg-[#ff0040]/90 transition-colors"
>
Go back
</button>
</div>
</div>
}
>
{children}
</ErrorBoundary>
);
}
export default RouteErrorBoundary;