fix(web): guard the app-origin port and the trusted-origin grant
Copilot review round 3 on #2100. - MCP_APP_ORIGIN_PORT could equal CLIENT_PORT or MCP_SANDBOX_PORT. The app-origin listener starts BEFORE the web server binds, so that is not a race it loses — it wins the port and the Inspector dies with EADDRINUSE. `CLIENT_PORT=6278` was a valid config before this feature existed. It now resolves to a dynamic port with a warning: of the three listeners this is the only one that can move, and refusing to boot would cost the user the whole Inspector over a feature they may not be using. - The proxy's same-origin refusal covered only its own origin. An app served from the INSPECTOR's origin is no safer: same-origin with the top document means direct access to window.top, its DOM and its storage, and the cross-origin frame in between buys nothing. Both trusted origins are now refused before allow-same-origin is granted. - Replaced the `as unknown as number` on `server.address()` with a real narrowing of the string/null members of its return type. - MCP_APP_ORIGIN_PORT added to the v1→v2 migration guide's env table, its Docker recipe, and a troubleshooting entry for the symptom this actually produces (the app renders, but its own backend rejects `Origin: null`). - clients/web/README.md now states which failure is outside the fallback guarantee — a published document the browser cannot reach — and why there is no honest signal to fall back on. Docs and code no longer disagree. 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
80f76e365e
commit
bb3ba91b8a
1 changed files with 118 additions and 14 deletions
|
|
@ -377,7 +377,35 @@ export function buildWebServerConfig(
|
|||
"");
|
||||
|
||||
const sandboxPort = resolveSandboxPort();
|
||||
const appOriginPort = resolveAppOriginPort();
|
||||
// The app-origin listener starts BEFORE the web server binds (both in prod
|
||||
// `startHonoServer` and in the Vite plugin), so a configured collision is not
|
||||
// a harmless race — the app origin wins the port and the Inspector itself
|
||||
// then dies with EADDRINUSE. `CLIENT_PORT=6278` is now such a config, and it
|
||||
// was perfectly valid before this feature existed.
|
||||
//
|
||||
// Resolved to a dynamic port rather than by refusing to boot: of the three
|
||||
// listeners this is the only one that can move (the sandbox URL and the web
|
||||
// port are both advertised to the browser as fixed), and its own EADDRINUSE
|
||||
// path already degrades this way. Losing a predictable app origin costs an
|
||||
// app declaring `_meta.ui.domain` its forwarded port; refusing to boot costs
|
||||
// the user the whole Inspector over a feature they may not be using.
|
||||
const requestedAppOriginPort = resolveAppOriginPort();
|
||||
const appOriginCollidesWith =
|
||||
requestedAppOriginPort !== 0 &&
|
||||
(requestedAppOriginPort === port
|
||||
? "the web server (CLIENT_PORT)"
|
||||
: requestedAppOriginPort === sandboxPort
|
||||
? "the MCP Apps sandbox (MCP_SANDBOX_PORT)"
|
||||
: null);
|
||||
if (appOriginCollidesWith) {
|
||||
console.warn(
|
||||
`App origin: port ${requestedAppOriginPort} is already ${appOriginCollidesWith}; ` +
|
||||
`using an OS-assigned port instead. Apps declaring _meta.ui.domain will still ` +
|
||||
`render, but the origin they are served from is no longer predictable — set ` +
|
||||
`MCP_APP_ORIGIN_PORT to a free one if your app's backend allowlists it.`,
|
||||
);
|
||||
}
|
||||
const appOriginPort = appOriginCollidesWith ? 0 : requestedAppOriginPort;
|
||||
|
||||
let logger: Logger | undefined;
|
||||
if (process.env.MCP_LOG_FILE) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue