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>
6 lines
644 B
Text
6 lines
644 B
Text
/*
|
|
Content-Security-Policy: default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline' https://unpkg.com; img-src 'self' data: blob: https:; font-src 'self' https://fonts.gstatic.com data:; connect-src 'self' https: wss:; media-src 'self' blob: https:; frame-src 'self' https:; worker-src 'self' blob:; manifest-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests
|
|
X-Content-Type-Options: nosniff
|
|
X-Frame-Options: DENY
|
|
Referrer-Policy: strict-origin-when-cross-origin
|
|
Permissions-Policy: geolocation=(self), camera=(self), microphone=()
|