* fix(cors): use ACAO: * for bootstrap to fix CF cache origin pinning
CF ignores Vary: Origin and pins the first request's ACAO header on the
cached response. Preview deployments from *.vercel.app got ACAO: worldmonitor.app
from CF's cache, blocking CORS. Bootstrap data is fully public (world events,
market prices, seismic data) so ACAO: * is safe and allows CF to cache one
entry valid for all origins. isDisallowedOrigin() still gates non-cache paths.
* chore: finish security triage
* fix(aviation): update isArray callback signature for fast-xml-parser 5.5.x
fast-xml-parser bumped from 5.4.2 to 5.5.7 changed the isArray callback's
second parameter type from string to unknown. Guard with typeof check before
calling .test() to satisfy the new type contract.
* docs: fix MD032 blank lines around lists in tradingview-screener-integration
* fix(security): address code review findings from PR #1903
- api/_json-response.js: add recursion depth limit (20) to sanitizeJsonValue
and strip Error.cause chain alongside stack/stackTrace
- scripts/ais-relay.cjs: extract WORLD_BANK_COUNTRY_ALLOWLIST to module level
to eliminate duplicate; clamp years param to [1,30] to prevent unbounded
World Bank date ranges
- src-tauri/sidecar/local-api-server.mjs: use JSON.stringify for vq value
in inline JS, consistent with safeVideoId/safeOrigin handling
- src/services/story-share.ts: simplify sanitizeStoryType to use typed array
instead of repeated as-casts
* fix(desktop): use parent window origin for YouTube embed postMessage
Sidecar youtube-embed route was targeting the iframe's own localhost origin
for all window.parent.postMessage calls, so browsers dropped yt-ready/
yt-state/yt-error on Tauri builds where the parent is tauri://localhost or
asset://localhost. LiveNewsPanel and LiveWebcamsPanel already pass
parentOrigin=window.location.origin in the embed URL; the sidecar now reads,
validates, and uses it as the postMessage target for all player event
messages. The YT API playerVars origin/widget_referrer continue to use the
sidecar's own localhost origin which YouTube requires.
Also restore World Bank relay to a generic proxy: replace TECH_INDICATORS
membership check with a format-only regex so any valid indicator code
(NY.GDP.MKTP.CD etc.) is accepted, not just the 16 tech-sector codes.
* refactor: dedupe edge api json response assembly
* refactor: expand jsonResponse helper to all edge functions
Roll out jsonResponse() from _json-response.js to 16 files (14 handlers
+ 2 shared helpers), eliminating 55 instances of the
new Response(JSON.stringify(...)) boilerplate.
Only exception: health.js uses JSON.stringify(body, null, indent) for
pretty-print mode, which is incompatible with the helper signature.
Replaced local jsonResponse/json() definitions in contact.js,
register-interest.js, and cache-purge.js with the shared import.