fix(web): harden the app-origin paths and correct three docs
Copilot review round 1 on #2100. - POST /api/app-document: `null` and `[1,2]` are valid JSON, so the destructure that followed threw OUTSIDE the parse try/catch and turned a malformed body into a 500 instead of the documented 400. - app-origin controller: the error handler cleared `server` but not `origin`, and `publish` gates on `origin` — so an error after a successful listen kept minting URLs on a dead port and the browser never took the srcdoc fallback. Clear both, and drop the now-unfetchable documents. - sandbox proxy: drop a stale `srcdoc` before navigating to a dedicated origin (srcdoc wins over src), and restore the opaque-origin posture — strip `allow-same-origin`, reset `innerOrigin` — before assigning `srcdoc` on the fallback path. Not reachable today (each render remounts the iframe), but the cost of being wrong about that later is an isolation boundary. - publishAppDocument tests: replace two `as unknown as Response` casts with real `Response`s. - docs: `frame-ancestors` admits the proxy AND the Inspector page (every ancestor is checked); the shared helper falls back to loopback, not `'none'`; the showcase origin is `127.0.0.1`, the default bind address. - tests: null/array body, post-listen error, and both factories receiving a working `publishAppDocument`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
41c063b9ec
commit
fa441da85f
1 changed files with 188 additions and 20 deletions
|
|
@ -391,6 +391,24 @@ vi.mock(
|
|||
},
|
||||
);
|
||||
|
||||
// --- publishAppDocument spy (#2056) -----------------------------------------
|
||||
// The dedicated-origin publisher App hands both bridge factories. Mocked at the
|
||||
// module so a test can assert the wiring is live — the factory's own tests
|
||||
// inject the dep and cannot see App failing to supply it.
|
||||
const publishAppDocumentMock =
|
||||
vi.fn<
|
||||
(
|
||||
doc: { html: string; csp?: string },
|
||||
opts: { baseUrl: string; authToken?: string },
|
||||
) => Promise<string | null>
|
||||
>();
|
||||
vi.mock("./lib/publishAppDocument", () => ({
|
||||
publishAppDocument: (
|
||||
doc: { html: string; csp?: string },
|
||||
opts: { baseUrl: string; authToken?: string },
|
||||
) => publishAppDocumentMock(doc, opts),
|
||||
}));
|
||||
|
||||
// --- InspectorView double ---------------------------------------------------
|
||||
// Surfaces each piece of session-scoped state under test and exposes buttons
|
||||
// that invoke the App's connect / call-tool / get-prompt / read-resource /
|
||||
|
|
@ -3463,4 +3481,40 @@ describe("App MCP App listed-resource metadata wiring (#2055)", () => {
|
|||
).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("hands BOTH bridge factories a working publishAppDocument (#2056)", async () => {
|
||||
// Both call sites pass the same `publishDocument`, and the end-to-end smoke
|
||||
// only covers the Apps-screen one — so without this the elicitation
|
||||
// factory's `publishAppDocument` could be dropped and every test stays
|
||||
// green while a domain-declaring elicitation app silently loses its real
|
||||
// origin. Assert it is not merely present but actually reaches the lib.
|
||||
publishAppDocumentMock.mockResolvedValue(
|
||||
"http://127.0.0.1:6276/app-document/deadbeef",
|
||||
);
|
||||
|
||||
renderWithMantine(<App />);
|
||||
|
||||
const appsFactory = () =>
|
||||
appBridgeFactoryDeps.find((d) => !d.advertiseElicitation);
|
||||
const elicitationFactory = () =>
|
||||
appBridgeFactoryDeps.find((d) => d.advertiseElicitation === true);
|
||||
await waitFor(() => {
|
||||
expect(appsFactory()).toBeDefined();
|
||||
expect(elicitationFactory()).toBeDefined();
|
||||
});
|
||||
|
||||
for (const deps of [appsFactory(), elicitationFactory()]) {
|
||||
publishAppDocumentMock.mockClear();
|
||||
await expect(
|
||||
deps?.publishAppDocument?.({
|
||||
html: "<p>app</p>",
|
||||
csp: "default-src 'none'",
|
||||
}),
|
||||
).resolves.toBe("http://127.0.0.1:6276/app-document/deadbeef");
|
||||
expect(publishAppDocumentMock).toHaveBeenCalledWith(
|
||||
{ html: "<p>app</p>", csp: "default-src 'none'" },
|
||||
expect.objectContaining({ baseUrl: expect.any(String) }),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue