From 590f72923d7d65dfadd69259e7c5af010609679c Mon Sep 17 00:00:00 2001 From: Valentin Fernandez Date: Thu, 16 Jul 2026 11:08:16 -0300 Subject: [PATCH] fix(truapi): treat Firefox's masked "null" ancestor origin as hidden Firefox implements location.ancestorOrigins but serializes cross-origin ancestors as the literal string "null", which resolveHostOrigin returned verbatim; the sandbox bootstrap then threw SyntaxError ("An invalid or illegal string was specified") from parent.postMessage and every client call inside an iframe host failed. Treat "null" as an unknown host origin and fall through to the source-checked wildcard ready ping so the handshake completes on Firefox. --- js/packages/truapi/src/sandbox.test.ts | 23 +++++++++++++++++++++++ js/packages/truapi/src/sandbox.ts | 6 ++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/js/packages/truapi/src/sandbox.test.ts b/js/packages/truapi/src/sandbox.test.ts index 523e66bc..288dfd13 100644 --- a/js/packages/truapi/src/sandbox.test.ts +++ b/js/packages/truapi/src/sandbox.test.ts @@ -135,6 +135,29 @@ describe("sandbox iframe MessagePort handshake", () => { expect(currentWindow.listeners.size).toBe(0); }); + it('treats a masked "null" ancestor origin as hidden and pings with the wildcard', async () => { + // Firefox implements location.ancestorOrigins but serializes cross-origin + // ancestors as "null", which is not a valid postMessage targetOrigin. + currentWindow = installFakeIframeWindow({ ancestorOrigins: ["null"] }); + const sandbox = await importSandbox(); + + expect(sandbox.getClientSync()).not.toBeNull(); + expect(currentWindow.parentPostMessage.mock.calls).toEqual([ + [{ type: "truapi-ready" }, "*"], + ]); + + const accepted = trackChannel(); + currentWindow.dispatch({ + source: currentWindow.parent, + origin: "https://host.example", + data: { type: "truapi-init" }, + ports: [accepted.port1], + }); + await Promise.resolve(); + expect(currentWindow.win.__HOST_API_PORT__).toBe(accepted.port1); + expect(currentWindow.listeners.size).toBe(0); + }); + it("uses a data-free wildcard ready ping only when the host origin is hidden", async () => { currentWindow = installFakeIframeWindow({}); const sandbox = await importSandbox(); diff --git a/js/packages/truapi/src/sandbox.ts b/js/packages/truapi/src/sandbox.ts index 99ebdef4..33a8b189 100644 --- a/js/packages/truapi/src/sandbox.ts +++ b/js/packages/truapi/src/sandbox.ts @@ -68,8 +68,10 @@ function resolveHostOrigin(): string | null { // Fall through to ancestorOrigins. } } - const ancestors = window.location?.ancestorOrigins; - if (ancestors && ancestors.length > 0) return ancestors[0] ?? null; + // Firefox serializes cross-origin ancestors as "null", which is not a + // valid postMessage targetOrigin; treat it as an unknown host origin. + const ancestor = window.location?.ancestorOrigins?.[0]; + if (ancestor && ancestor !== "null") return ancestor; return null; }