fix(ui): https app launches and the nostr bridge follow the frame's real origin
Three launcher/bridge defects combined to make HTTPS dashboards look
broken while HTTP ones worked:
1. portAuth() looked the launch port up under the name the user clicks
('mempool-web', 'lnd', 'bitcoin-knots'…), but the signed catalog
declares those ports under the manifest id that owns them
(archy-mempool-web, lnd-ui, bitcoin-ui). The lookup missed,
portIsGateFronted answered false, and an HTTPS dashboard handed app
frames http:// URLs — blocked as mixed content: mempool and IndeeHub
'did not connect', bitcoin knots/core opened http:// in a new tab.
Resolution now follows launch aliases, then a port-wide catalog scan
that only answers when every declarer of that port agrees (a port
any app publishes as plain HTTP is never upgraded to https).
2. The signed-catalog cache was only warmed by the Store/Discover
views, so a user who went straight to My Apps launched apps with an
empty cache. Warmed at dashboard mount now — fetchAppCatalog()
already memoizes with a 1h TTL.
3. The NIP-07 bridge compared event.origin for strict equality with the
recorded (http) app URL and replied to the recorded URL as the
postMessage targetOrigin — both break the moment a frame is scheme-
upgraded (cached HSTS did exactly that): every nostr request was
silently dropped and replies to the stale origin threw. The bridge
now matches host+port (scheme deliberately ignored) and always
replies to event.origin — the frame's real origin.
Unit tests cover alias resolution (incl. bitcoin-knots→8334→https),
the conservative port-scan, and scheme-agnostic sender matching.
This commit is contained in:
@@ -29,7 +29,7 @@ vi.mock('@/router', () => ({
|
||||
|
||||
vi.stubGlobal('open', mockWindowOpen)
|
||||
|
||||
import { useAppLauncherStore } from '../appLauncher'
|
||||
import { useAppLauncherStore, senderMatchesApp } from '../appLauncher'
|
||||
|
||||
describe('useAppLauncherStore', () => {
|
||||
beforeEach(() => {
|
||||
@@ -448,4 +448,21 @@ describe('useAppLauncherStore', () => {
|
||||
vi.runAllTimers()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
describe('NIP-07 sender origin matching', () => {
|
||||
it('accepts a scheme-upgraded frame (HSTS) as the opened app', () => {
|
||||
// Regression (2026-09-01): the stored app URL was http:// but the
|
||||
// browser loaded the frame as https:// — strict origin equality
|
||||
// dropped every nostr sign-in from the upgraded frame.
|
||||
expect(senderMatchesApp('http://framework-pt.local:7778', 'https://framework-pt.local:7778')).toBe(true)
|
||||
expect(senderMatchesApp('https://framework-pt.local:7778', 'http://framework-pt.local:7778')).toBe(true)
|
||||
})
|
||||
|
||||
it('still rejects a different host or port', () => {
|
||||
expect(senderMatchesApp('http://framework-pt.local:7778', 'https://evil.example:7778')).toBe(false)
|
||||
expect(senderMatchesApp('http://framework-pt.local:7778', 'https://framework-pt.local:7777')).toBe(false)
|
||||
expect(senderMatchesApp('http://framework-pt.local:7778', 'null')).toBe(false)
|
||||
expect(senderMatchesApp('', 'https://framework-pt.local:7778')).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user