The `picture` and `banner` fields parsed from a kind 31990 NIP-89 event's
JSON content were passed directly to `<img src>` attributes without any
scheme validation. Non-https URLs could leak the user's IP to arbitrary
hosts, and data: URIs could be used for fingerprinting.
The same event's `website` URL was already sanitized; apply the same
treatment to the image URLs for consistency. The app's CSP `img-src`
already blocks most of these at the browser level, so this is
defense-in-depth.
Add a shared sanitizeUrl() utility that validates URLs are well-formed
https: before they reach href attributes, window.open(), or openUrl().
Apply sanitization across all components that render untrusted URLs:
- CalendarEventDetailPage: r-tag links
- ZapstoreAppContent: url and repository tags
- ZapstoreReleaseContent: asset url tags passed to openUrl()
- AppHandlerContent: web handler tags and metadata.website
- NsiteCard: source tag
- GitRepoCard: web tag URLs passed to openUrl()
- FileMetadataContent: url tag used in download href
- ProfilePage: metadata.website (tighten weak startsWith check)
- useUserStatus: r-tag URL
Document sanitizeUrl usage in AGENTS.md for future agent use.
NsitePreviewDialog now builds a path→sha256 manifest from the event's 'path'
tags and resolves files directly from Blossom servers (from the event's 'server'
tags, falling back to the user's configured app Blossom servers). Each fetch
request from the iframe is intercepted, the sha256 is looked up in the manifest,
and the blob is fetched from the first Blossom server that responds successfully.
Unknown paths fall back to /index.html to support SPA client-side routing.
- NsitePreviewDialog: remove nsiteUrl proxy, accept NostrEvent instead
- NsiteCard: pass event directly to dialog
- AppHandlerContent: use useAddrEvent to fetch the kind 35128 event by
pubkey+d-tag from the 'a' tag, then pass the event to the dialog; disable
Run button until the nsite event is loaded; remove unused hexToBase36
Separate the proxy target (nsite.lol gateway URL) from the display URL.
Pass nsiteName through to the dialog so the address bar shows a clean
nsite:// scheme with no gateway hostname.
When a kind 31990 app event includes an 'a' tag pointing to a kind 35128
nsite, display a 'Run' button that opens an in-app preview dialog. The
dialog embeds the nsite in a sandboxed iframe via the Shakespeare
iframe-fetch-client protocol (local-shakespeare.dev), proxying fetch
requests from the iframe to the live nsite URL so the SPA renders
without needing CORS headers on the origin server.
- Rename Zapstore kind labels to include 'Zapstore' prefix across all
label registries (NoteCard, PostDetailPage, CommentContext,
ExternalContentHeader, NotificationsPage, extraKinds)
- Wrap Zapstore (32267, 30063, 3063) compact and detail content in
rounded bordered cards with hover effects; remove redundant mt-2/mt-3
margins from component roots
- Replace useLinkPreview thumbnail with metadata banner/picture in kind
31990 app handler cards (compact and full views)
- Add pt-4 to Zapstore detail card wrappers in PostDetailPage
- Fix sticky tab bar (SubHeaderBar z-10) being painted over by card
content: remove z-10 from AppHandlerContent inner div and add z-0 to
the main content column in MainLayout