Commit Graph

6 Commits

Author SHA1 Message Date
Elie Habib
93c28cf4e6 fix(widgets): fix CSP violations in pro widget iframe (#2362)
* fix(widgets): fix CSP violations in pro widget iframe by using sandbox page

srcdoc iframes inherit the parent page's Content-Security-Policy response
headers. The parent's hash-based script-src blocks inline scripts and
cdn.jsdelivr.net (Chart.js), making pro widgets silently broken.

Fix: replace srcdoc with a dedicated /wm-widget-sandbox.html page that
has its own permissive CSP via vercel.json route headers. Widget HTML is
passed via postMessage after the sandbox page loads.

- Add public/wm-widget-sandbox.html: minimal relay page that receives
  HTML via postMessage and renders it with document.open/write/close.
  Validates message origin against known worldmonitor.app domains.
- vercel.json: add CSP override route for sandbox page (unsafe-inline +
  cdn.jsdelivr.net), exclude from SPA rewrite and no-cache rules.
- widget-sanitizer.ts: switch wrapProWidgetHtml to src + data-wm-id,
  store widget bodies in module-level Map, auto-mount via MutationObserver.
  Fix race condition (always use load event, not readyState check).
  Delete store entries after mount to prevent memory leak.
- tests: update 4 tests to reflect new postMessage architecture.

* test(deploy): update deploy-config test for wm-widget-sandbox.html exclusion
2026-03-27 14:27:55 +04:00
Elie Habib
0245ed53a9 fix(auth): consolidate premium access into hasPremiumAccess() — fix PRO panel gating (#2299)
* fix(auth): consolidate premium access checks into hasPremiumAccess()

Single source of truth for all premium access paths: desktop API key,
wm-pro-key / wm-widget-key tester keys, and Clerk Pro role.

- Export hasPremiumAccess() from panel-gating.ts (covers all 3 paths)
- Remove duplicate local hasPremiumAccess() from data-loader.ts
- Add market-implications to WEB_PREMIUM_PANELS so it gets auth gating
- Fix lazy panel race: call updatePanelGating() inside lazyPanel() so
  daily-market-brief and market-implications aren't stuck loading
- Remove stale getSecretState / isProUser imports from panel-layout.ts
- Update static-analysis tests to assert hasPremiumAccess instead of isProUser

* fix(auth): pass getAuthState() to initial applyProBlockGating call

Prevents a flash of incorrect state for Clerk Pro users on first paint
before the auth subscription fires.
2026-03-26 19:28:13 +04:00
Elie Habib
f326621abb feat(pro): consolidate pro tier to unlock with either wm-widget-key or wm-pro-key (#1972)
Add isProUser() helper that returns true if either key is present,
replacing scattered isWidgetFeatureEnabled/isProWidgetEnabled checks
across panel-layout. Desktop _lockPanels now also respects web pro keys.
2026-03-21 10:08:43 +04:00
Elie Habib
cf48144138 feat(widgets): add Exa web search + fix widget API endpoints (#1782)
* feat(widgets): add Exa web search + fix widget API endpoints

- Replace Tavily with Exa as primary stock-news search provider
  (Exa → Brave → SerpAPI → Google News RSS cascade)
- Add search_web tool to widget agent so AI can fetch live data
  about any topic beyond the pre-defined RPC catalog
- Exa primary (type:auto + content snippets), Brave fallback
- Fix all widget tool endpoints: /rpc/... paths were hitting
  Vercel catch-all and returning SPA HTML instead of JSON data
- Fix wm-widget-shell min-height causing fixed-size border that
  clipped AI widget content
- Add HTML response guard in tool handler
- Update env key: TAVILY_API_KEYS → EXA_API_KEYS throughout

* fix(stock-news): use type 'neural' for Exa search (type 'news' is invalid)
2026-03-17 19:25:08 +04:00
Elie Habib
6d8109a85b feat(widgets): PRO interactive widgets via iframe srcdoc (#1771)
* feat(widgets): add PRO interactive widgets via iframe srcdoc

Introduces a PRO tier for AI-generated widgets that supports full JS
execution (Chart.js, sortable tables, animated counters) via sandboxed
iframes — no Docker, no build step required.

Key design decisions:
- Server returns <body> + inline <script> only; client builds the full
  <!DOCTYPE html> skeleton with CSP guaranteed as the first <head> child
  so the AI can never inject or bypass the security policy
- sandbox="allow-scripts" only — no allow-same-origin, no allow-forms
- PRO HTML stored in separate wm-pro-html-{id} localStorage key to
  isolate 80KB quota pressure from the main widget metadata array
- Raw localStorage.setItem() for PRO writes with HTML-first write order
  and metadata rollback on failure (bypasses saveToStorage which swallows
  QuotaExceededError)
- Separate PRO_WIDGET_KEY env var + x-pro-key header gate on Railway
- Separate rate limit bucket (20/hr PRO vs 10/hr basic)
- Claude Sonnet 4.6 (8192 tokens, 10 turns, 120s) for PRO vs Haiku for
  basic; health endpoint exposes proKeyConfigured for modal preflight

* feat(pro): gate finance panels and widget buttons behind wm-pro-key

The PRO localStorage key now unlocks the three previously desktop-only
finance panels (stock-analysis, stock-backtest, daily-market-brief) on
the web variant, giving PRO users access without needing WORLDMONITOR_API_KEY.

Button visibility is now cleanly separated by key:
- wm-widget-key only → basic "Create with AI" button
- wm-pro-key only    → PRO "Create Interactive" button only
- both keys          → both buttons
- no key             → neither button

Widget boot loader also accepts either key so PRO-only users see their
saved interactive widgets on page load.

* fix(widgets): inject Chart.js CDN into PRO iframe shell so new Chart() is defined
2026-03-17 18:10:10 +04:00
Elie Habib
4353c20637 feat(widgets): AI widget builder with live WorldMonitor data (#1732) 2026-03-17 09:23:04 +04:00