fix(ci): boot protocol E2E on the peer-stamped custom server with preserved open bootstrap (#11535) (#11549)

Validated in a combined 3-PR batch worktree off release/v3.8.51 tip.
- Focused test: protocol-e2e-server-stamping-11535.test.ts — part of batch's 165/165 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity, check:docs-counts-sync — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff

Thanks for the meticulous root-causing here — three distinct issues (server flavor, HOST pin, open-bootstrap env leak) traced to exact line numbers with live-boot before/after evidence.
This commit is contained in:
Webman
2026-08-25 16:54:09 -05:00
committed by GitHub
parent 7cfabfc5c9
commit 700819a29b
4 changed files with 133 additions and 1 deletions

View File

@@ -61,6 +61,25 @@ for (const [key, value] of Object.entries(mergedEnv)) {
}
}
// E2E open-mode bootstrap (#11535). Test harnesses that boot THIS server (protocol
// clients E2E) rely on an auth-disabled "open" bootstrap so management endpoints such
// as /api/mcp/audit are genuinely exercised unauthenticated (200), not short-circuited
// by a stray credential. bootstrap-env.mjs deliberately drops empty strings from
// process.env/.env/server.env, so an INITIAL_PASSWORD="" injected by a harness cannot
// survive the merge above and any INITIAL_PASSWORD persisted in .env or server.env
// would leak back in (401 → green-shallow suite).
// Cleared to EMPTY STRING (not deleted): Next's env loader re-reads the repo .env
// during app prepare(), AFTER this point — an absent var would be re-populated from
// the file and src/instrumentation-node.ts would bcrypt-persist it as a real login
// (401s everywhere). An existing empty var is falsy to every consumer AND wins over
// dotenv's no-override load, mirroring run-next-playwright.mjs's open-mode overrides.
// Gated on the test-only env var so production boots are untouched.
if (process.env.OMNIROUTE_E2E_BOOTSTRAP_MODE === "open") {
process.env.INITIAL_PASSWORD = "";
process.env.OMNIROUTE_E2E_PASSWORD = "";
process.env.OMNIROUTE_API_KEY = "";
}
// systemd sd_notify (Type=notify / WatchdogSec=): this process owns the
// watchdog pings — if its event loop blocks (freeze), the pings stop and
// systemd kills the service. No-op outside systemd (no NOTIFY_SOCKET).

View File

@@ -57,10 +57,24 @@ async function main() {
OMNIROUTE_BASE_URL: baseUrl,
}),
OMNIROUTE_E2E_BOOTSTRAP_MODE: process.env.OMNIROUTE_E2E_BOOTSTRAP_MODE || "open",
// Pin the custom server's bind address to loopback (#11535): under the
// programmatic next() entry the middleware's nextUrl.hostname mirrors the
// configured HOST (default "0.0.0.0"), and apiAuth.isLoopbackRequest() reads
// nextUrl.hostname FIRST — an unpinned boot makes every request look remote,
// so the anonymous open-bootstrap allow never fires (401 green-shallow).
HOST: process.env.HOST || "127.0.0.1",
};
if (!(await isServerReady())) {
serverProcess = spawn(process.execPath, ["scripts/dev/run-next-playwright.mjs", "dev"], {
// Boot the REAL custom server (run-next.mjs), not the bare `next dev` CLI.
// Only the custom Node server stamps the trusted PEER_IP_HEADER from the TCP
// socket; without that stamp the authz middleware fails closed on locality and
// every LOCAL_ONLY route (e.g. /api/mcp/audit) answers 403 even from loopback
// (#11535). run-next.mjs honors OMNIROUTE_E2E_BOOTSTRAP_MODE=open by clearing
// bootstrap credentials after its env merge, keeping the audit assertions live
// (200) instead of masking them behind a 401. The Playwright webServer runner is
// intentionally left untouched — it serves the whole blocking test-e2e suite.
serverProcess = spawn(process.execPath, ["scripts/dev/run-next.mjs", "dev"], {
stdio: "inherit",
env: testEnv,
});