Critical: Leaflet marker popups built untrusted Nostr event fields (and BTC Map API fields) into innerHTML, bypassing React's output encoding. Any user could publish a spot/tour/crew/event with an <img onerror> payload in a text field; it executed on /map render with no interaction, letting injected script read the plaintext nsec from localStorage (nostr:login) and the Cashu seed (cashu-mnemonic) — permanent, unrotatable account + wallet takeover. Fixes in src/components/SpotMap.tsx: - Rebuild all popup DOM with createElement + textContent instead of innerHTML for every untrusted field: spot.location; tour.title/location/meetingPoint; evt.title/location; crew.name/location; BTC Map name/address/opening_hours/ phone/description. - Validate every event/API URL with sanitizeUrl() before it reaches an href/src: crew.image (popup + marker icon) and BTC Map website. Drop anything that isn't http(s). tel: links reduced to safe characters. - Replace inline onerror handler on the crew marker image (would be blocked by CSP anyway) with a scripted img.onerror fallback. - Add popupRow() helper so untrusted detail rows can never be innerHTML'd. Defense in depth (web only, native Capacitor build unaffected): - Add public/_headers with a strict Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval' (no unsafe-inline/eval); object-src 'none'; base-uri 'none'; frame-ancestors 'none'; plus the https:/wss: allowances a Nostr client genuinely needs for relays, media, and mints. Also X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy. - Move the inline boot-crash-guard script to public/boot-guard.js so script-src can stay 'self' with no 'unsafe-inline'; index.html now loads it externally. The guard also now paints errors via textContent, not innerHTML. Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
58 lines
2.9 KiB
HTML
58 lines
2.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
<!-- CSP intentionally omitted: app is sandboxed by Android/iOS at the OS level.
|
|
A restrictive CSP here breaks Capacitor's internal asset loading scheme. -->
|
|
|
|
<!-- PWA Manifest -->
|
|
<link rel="manifest" href="/manifest.webmanifest">
|
|
|
|
<!-- App Icons -->
|
|
<link rel="icon" type="image/png" href="/images/icon-1024.png">
|
|
<link rel="apple-touch-icon" href="/images/icon-1024.png">
|
|
<link rel="apple-touch-icon" sizes="152x152" href="/images/icon-1024.png">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/images/icon-1024.png">
|
|
<link rel="apple-touch-startup-image" href="/images/icon-1024.png">
|
|
|
|
<!-- Theme Color -->
|
|
<meta name="theme-color" content="#ff0040">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
<meta name="apple-mobile-web-app-title" content="SkateSpots">
|
|
|
|
<!-- SEO Meta Tags -->
|
|
<meta name="description" content="Discover skate spots, find coaches, join events, and connect with the skateboarding community on Nostr. Free, decentralized, censorship-resistant.">
|
|
<meta name="keywords" content="skateboarding, skate spots, skate parks, nostr, decentralized, bitcoin, lightning, zaps">
|
|
|
|
<!-- Open Graph -->
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:title" content="SkateSpots - Decentralized Skate Map & Community">
|
|
<meta property="og:description" content="Discover skate spots, find coaches, join events, and connect with the skateboarding community on Nostr.">
|
|
<meta property="og:image" content="https://skate-spots.org/images/icon-1024.png">
|
|
<meta property="og:url" content="https://skate-spots.org">
|
|
|
|
<!-- Twitter Card -->
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="SkateSpots - Decentralized Skate Map">
|
|
<meta name="twitter:description" content="Discover skate spots, find coaches, join events on Nostr.">
|
|
<meta name="twitter:image" content="https://skate-spots.org/images/icon-1024.png">
|
|
|
|
<!-- Leaflet CSS -->
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css">
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css">
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
|
|
<!--
|
|
BOOT CRASH GUARD (native-safe) — external so the web build can ship a
|
|
strict CSP (script-src 'self') with no 'unsafe-inline'. See boot-guard.js.
|
|
-->
|
|
<script src="/boot-guard.js"></script>
|
|
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
</body>
|
|
</html>
|