Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/server/host-routing-runtime.test.ts
T

681 lines
32 KiB
TypeScript

// @vitest-environment node
import { describe, expect, test } from "vitest";
import {
finalizePortalHostRoutingDecision,
finalizePortalTenantRoutingDecision,
getHostRoutingRuntimeDecision,
} from "./host-routing-runtime";
function expectDispatchDecision(request: Request) {
const decision = getHostRoutingRuntimeDecision(request);
expect(decision.type).toBe("dispatch");
if (decision.type !== "dispatch") {
throw new Error(`Expected dispatch decision, received ${decision.type}`);
}
return decision;
}
const adminOptions = { runtimeSurface: "admin" as const };
function expectAdminDispatchDecision(request: Request) {
const decision = getHostRoutingRuntimeDecision(request, adminOptions);
expect(decision.type).toBe("dispatch");
if (decision.type !== "dispatch") {
throw new Error(`Expected admin dispatch decision, received ${decision.type}`);
}
return decision;
}
describe("getHostRoutingRuntimeDecision", () => {
test("rejects unknown hosts instead of serving authenticated web routes on sibling or attacker domains", () => {
expect(getHostRoutingRuntimeDecision(new Request("https://untrusted.example/acme/dashboard"))).toEqual({
type: "reject",
status: 404,
});
expect(
getHostRoutingRuntimeDecision(
new Request("http://untrusted.example/acme/dashboard", {
headers: { host: "untrusted.example", "x-forwarded-host": "app.localhost" },
})
)
).toEqual({ type: "reject", status: 404 });
});
test("dispatches the canonical app host root to the tenant picker route", () => {
const decision = expectDispatchDecision(new Request("http://app.localhost/?from=login#tenants"));
expect(decision.target).toBe("web");
expect(new URL(decision.request.url).pathname).toBe("/");
expect(new URL(decision.request.url).search).toBe("?from=login");
expect(new URL(decision.request.url).hash).toBe("#tenants");
});
test("isolates public sales bookings on the dedicated meeting host", () => {
const page = expectDispatchDecision(
new Request(
"http://meet.localhost/erstgespraech?date=2026-08-03#slots"
)
);
expect(page.target).toBe("web");
expect(new URL(page.request.url).pathname).toBe(
"/meet/erstgespraech"
);
expect(new URL(page.request.url).search).toBe("?date=2026-08-03");
expect(new URL(page.request.url).hash).toBe("#slots");
const asset = expectDispatchDecision(
new Request("http://meet.localhost/assets/entry.client-build.js")
);
expect(new URL(asset.request.url).pathname).toBe(
"/assets/entry.client-build.js"
);
});
test("keeps app host requests on promoted tenant paths for web dispatch", () => {
const decision = expectDispatchDecision(new Request("http://app.localhost/acme/dashboard?tab=activity#events"));
expect(decision.target).toBe("web");
expect(new URL(decision.request.url).pathname).toBe("/acme/dashboard");
expect(new URL(decision.request.url).search).toBe("?tab=activity");
expect(new URL(decision.request.url).hash).toBe("#events");
});
test("dispatches public auth routes on the app host without tenant rewriting", () => {
const decision = expectDispatchDecision(new Request("http://app.localhost/login?redirectTo=%2Facme%2Fdashboard"));
expect(decision.target).toBe("web");
expect(new URL(decision.request.url).pathname).toBe("/login");
expect(new URL(decision.request.url).search).toBe("?redirectTo=%2Facme%2Fdashboard");
});
test("dispatches registration plan advice through the allowed auth namespace", () => {
const decision = expectDispatchDecision(new Request("http://app.localhost/auth/registration-plan-advice?office=5&field=15"));
expect(decision.target).toBe("web");
expect(new URL(decision.request.url).pathname).toBe("/auth/registration-plan-advice");
expect(new URL(decision.request.url).search).toBe("?office=5&field=15");
});
test("dispatches the password-policy remind endpoint on the app host", () => {
// Regression guard: the banner's snooze endpoint moved from /api/* to
// /auth/* because removed-API rejection below 404s /api/* on this host.
const decision = expectDispatchDecision(new Request("http://app.localhost/auth/password-policy-remind", { method: "POST" }));
expect(decision.target).toBe("web");
expect(new URL(decision.request.url).pathname).toBe("/auth/password-policy-remind");
expect(decision.request.method).toBe("POST");
});
test("rejects removed public API paths instead of redirecting compatibility routes", () => {
const decision = getHostRoutingRuntimeDecision(new Request("http://app.localhost/api/workflows/run/workflow_1?tenantId=tenant_1"));
expect(decision).toEqual({
type: "reject",
status: 404,
});
});
test("redirects legacy api v1 paths before removed API path rejection", () => {
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/api/v1/health?check=deep#status"))).toEqual({
type: "redirect",
status: 308,
location: "http://api.localhost/v1/health?check=deep#status",
});
});
test("lets internal API proxies reach private web API handlers without public redirects", () => {
const decision = expectDispatchDecision(
new Request("http://app.localhost/internal/api/workflows/run/workflow_1?tenantId=tenant_1", {
method: "POST",
headers: {
"x-hivegrid-internal-web-api-proxy": "1",
"x-hivegrid-internal-web-api-proxy-signature": "signed",
"x-api-key": "tenant-api-key",
},
body: JSON.stringify({ name: "Ada" }),
})
);
expect(decision.target).toBe("web");
expect(new URL(decision.request.url).pathname).toBe("/internal/api/workflows/run/workflow_1");
expect(new URL(decision.request.url).search).toBe("?tenantId=tenant_1");
expect(decision.request.headers.get("x-hivegrid-internal-web-api-proxy")).toBe("1");
expect(decision.request.headers.get("x-hivegrid-internal-web-api-proxy-signature")).toBe("signed");
expect(decision.request.headers.get("x-api-key")).toBe("tenant-api-key");
});
test("dispatches canonical admin host requests for web dispatch", () => {
const decision = expectAdminDispatchDecision(new Request("http://admin.localhost/accounts/users?role=owner#active"));
expect(decision.target).toBe("web");
expect(new URL(decision.request.url).pathname).toBe("/accounts/users");
expect(new URL(decision.request.url).search).toBe("?role=owner");
expect(new URL(decision.request.url).hash).toBe("#active");
});
test.each([
"/assets/entry.client-build.js",
"/assets/root-build.css",
"/fonts/instrument-sans-latin.woff2",
"/images/placeholder.svg",
"/locales/de/translations.json",
"/android-chrome-192x192.png",
"/android-chrome-512x512.png",
"/android-icon-36x36.png",
"/android-icon-48x48.png",
"/android-icon-72x72.png",
"/android-icon-96x96.png",
"/android-icon-192x192.png",
"/apple-touch-icon.png",
"/browserconfig.xml",
"/favicon.ico",
"/favicon-16x16.png",
"/favicon-32x32.png",
"/favicon-96x96.png",
"/manifest.webmanifest",
"/ms-icon-70x70.png",
"/ms-icon-150x150.png",
"/ms-icon-310x310.png",
"/site.webmanifest",
"/sw.js?v=build-sha",
])("keeps the core admin static asset %s on the admin host", (pathname) => {
for (const method of ["GET", "HEAD"]) {
const decision = expectAdminDispatchDecision(new Request(`http://admin.localhost${pathname}`, { method }));
expect(decision.target).toBe("web");
expect(decision.request.url).toBe(`http://admin.localhost${pathname}`);
expect(decision.request.method).toBe(method);
expect(decision.serveStaticAssets).toBe(true);
expect(decision.reactRouterFallback).toBe(false);
}
});
test("fails closed on admin static asset misses and mutations instead of redirecting them to the app host", () => {
const missingAsset = expectAdminDispatchDecision(new Request("http://admin.localhost/assets/missing-build.js"));
const mutation = getHostRoutingRuntimeDecision(
new Request("http://admin.localhost/assets/entry.client-build.js", { method: "POST", body: "not-a-static-read" }),
adminOptions
);
expect(missingAsset).toEqual(expect.objectContaining({ serveStaticAssets: true, reactRouterFallback: false }));
expect(mutation).toEqual({ type: "reject", status: 404 });
});
test("keeps canonical admin host routes stable for web dispatch", () => {
const settingsDecision = expectAdminDispatchDecision(new Request("http://admin.localhost/settings/profile?tab=mfa#codes"));
const metricsDataDecision = expectAdminDispatchDecision(
new Request("http://admin.localhost/metrics/summary.data?_routes=routes%2Fadmin%2Fmetrics%2Fsummary.__host")
);
expect(settingsDecision.target).toBe("web");
expect(new URL(settingsDecision.request.url).pathname).toBe("/settings/profile");
expect(new URL(settingsDecision.request.url).search).toBe("?tab=mfa");
expect(new URL(settingsDecision.request.url).hash).toBe("#codes");
expect(new URL(metricsDataDecision.request.url).pathname).toBe("/metrics/summary.data");
});
test("redirects the legacy admin login URL on the canonical admin host", () => {
expect(getHostRoutingRuntimeDecision(new Request("http://admin.localhost/admin/login?error=sso_state_invalid#retry"), adminOptions)).toEqual({
type: "redirect",
status: 308,
location: "http://admin.localhost/login?error=sso_state_invalid#retry",
});
});
test.each([
["/api?tab=overview", "/api"],
["/api/keys?filter=active#keys", "/api/keys"],
["/api/keys/new", "/api/keys/new"],
["/api/keys/api_key_1", "/api/keys/api_key_1"],
["/api/logs", "/api/logs"],
["/api/docs", "/api/docs"],
["/api/credits", "/api/credits"],
["/api/keys.data?_routes=routes%2Fadmin%2Fapi%2Fkeys.__host", "/api/keys.data"],
])("dispatches canonical admin API section route %s to the web runtime", (route, expectedPathname) => {
const decision = expectAdminDispatchDecision(new Request(`http://admin.localhost${route}`));
expect(decision.target).toBe("web");
expect(new URL(decision.request.url).pathname).toBe(expectedPathname);
});
test("redirects API host unversioned aliases to v1 paths before dispatch", () => {
expect(getHostRoutingRuntimeDecision(new Request("http://api.localhost/health?check=deep#status"))).toEqual({
type: "redirect",
status: 308,
location: "http://api.localhost/v1/health?check=deep#status",
});
expect(getHostRoutingRuntimeDecision(new Request("http://api.localhost/accounts?cursor=next"))).toEqual({
type: "redirect",
status: 308,
location: "http://api.localhost/v1/accounts?cursor=next",
});
});
test("redirects public web paths away from the API host", () => {
const decision = getHostRoutingRuntimeDecision(new Request("http://api.localhost/login?redirectTo=%2Facme%2Fdashboard"));
expect(decision).toEqual({
type: "redirect",
status: 308,
location: "http://app.localhost/login?redirectTo=%2Facme%2Fdashboard",
});
});
test("does not double-prefix API internal paths", () => {
const apiDecision = expectDispatchDecision(new Request("http://api.localhost/v1/health?check=deep#status"));
expect(new URL(apiDecision.request.url).pathname).toBe("/v1/health");
});
test("rejects removed root app paths instead of redirecting compatibility routes", () => {
const decision = getHostRoutingRuntimeDecision(new Request("http://localhost/app/acme/settings?tab=profile#billing"));
expect(decision).toEqual({
type: "reject",
status: 404,
});
});
test("rejects removed admin paths on root and app hosts", () => {
expect(getHostRoutingRuntimeDecision(new Request("http://localhost/admin/settings?tab=users#active"))).toEqual({
type: "reject",
status: 404,
});
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/admin/settings?tab=users#active"))).toEqual({
type: "reject",
status: 404,
});
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/admin/login?error=sso_state_invalid"))).toEqual({
type: "reject",
status: 404,
});
});
test("redirects canonical paths away from the wrong host before dispatch", () => {
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/dashboard?tab=activity#events"))).toEqual({
type: "redirect",
status: 308,
location: "http://admin.localhost/dashboard?tab=activity#events",
});
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/accounts/users?filter=active"))).toEqual({
type: "redirect",
status: 308,
location: "http://admin.localhost/accounts/users?filter=active",
});
expect(getHostRoutingRuntimeDecision(new Request("http://admin.localhost/acme/dashboard?tab=activity#events"))).toEqual({
type: "reject",
status: 404,
});
expect(getHostRoutingRuntimeDecision(new Request("http://admin.localhost/acme"))).toEqual({
type: "reject",
status: 404,
});
expect(getHostRoutingRuntimeDecision(new Request("http://api.localhost/acme"))).toEqual({
type: "redirect",
status: 308,
location: "http://app.localhost/acme",
});
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/v1/health"))).toEqual({
type: "redirect",
status: 308,
location: "http://api.localhost/v1/health",
});
});
test("dispatches API operational routes to the API runtime", () => {
const healthDecision = expectDispatchDecision(new Request("http://api.localhost/healthz"));
const readyDecision = expectDispatchDecision(new Request("http://api.localhost/readyz"));
expect(healthDecision.target).toBe("api");
expect(new URL(healthDecision.request.url).pathname).toBe("/healthz");
expect(readyDecision.target).toBe("api");
expect(new URL(readyDecision.request.url).pathname).toBe("/readyz");
});
test("dispatches the token-protected web metrics route to the Core runtime", () => {
const decision = expectDispatchDecision(new Request("http://app.localhost/api/metrics"));
expect(decision.target).toBe("web");
expect(decision.serveStaticAssets).toBe(true);
expect(decision.reactRouterFallback).toBe(true);
expect(new URL(decision.request.url).pathname).toBe("/api/metrics");
});
test("keeps Core and Admin hosts on separate fail-closed runtime surfaces", () => {
expect(getHostRoutingRuntimeDecision(new Request("http://admin.localhost/dashboard"))).toEqual({ type: "reject", status: 404 });
for (const host of ["app.localhost", "api.localhost", "localhost", "meet.localhost"]) {
expect(getHostRoutingRuntimeDecision(new Request(`http://${host}/healthz`), adminOptions)).toEqual({ type: "reject", status: 404 });
}
expect(getHostRoutingRuntimeDecision(new Request("http://admin.localhost/healthz"), adminOptions)).toEqual(
expect.objectContaining({ type: "dispatch", target: "web" })
);
});
});
describe("portal runtime surface boundary", () => {
const portalOptions = { runtimeSurface: "portal" as const };
const customPortalOptions = {
runtimeSurface: "portal" as const,
routing: {
appBaseDomain: "hivegrid.de",
webAppHost: "app.hivegrid.de",
adminAppHost: "admin.hivegrid.de",
apiHost: "api.hivegrid.de",
portalAppHost: "portal.hivegrid.de",
protocol: "https" as const,
},
};
function expectPortalDispatch(request: Request) {
const initialDecision = getHostRoutingRuntimeDecision(request, portalOptions);
const decision =
initialDecision.type === "resolve-portal-tenant"
? finalizePortalTenantRoutingDecision(initialDecision, initialDecision.tenantSlug)
: initialDecision;
expect(decision.type).toBe("dispatch");
if (decision.type !== "dispatch") throw new Error(`Expected portal dispatch, received ${decision.type}`);
return decision;
}
test("keeps the portal host and internal namespace off the core surface", () => {
expect(getHostRoutingRuntimeDecision(new Request("http://portal.localhost/acme"))).toEqual({ type: "reject", status: 404 });
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/__portal/acme"))).toEqual({ type: "reject", status: 404 });
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/portal/acme"))).toEqual({ type: "reject", status: 404 });
});
test.each(["app.localhost", "admin.localhost", "api.localhost", "localhost", "custom.localhost", "attacker.example"])(
"rejects foreign host %s on the portal surface",
(host) => {
expect(getHostRoutingRuntimeDecision(new Request(`http://${host}/acme`), portalOptions)).toEqual({ type: "reject", status: 404 });
}
);
test("does not trust a forwarded portal host behind a foreign direct host", () => {
const request = new Request("http://internal.local/acme", {
headers: { host: "internal.local", "x-forwarded-host": "portal.localhost" },
});
expect(getHostRoutingRuntimeDecision(request, portalOptions)).toEqual({ type: "reject", status: 404 });
});
test("treats an explicit default HTTPS port as the same portal host", () => {
const decision = getHostRoutingRuntimeDecision(new Request("https://portal.example.com/acme"), {
runtimeSurface: "portal",
routing: {
appBaseDomain: "example.com",
webAppHost: "app.example.com",
adminAppHost: "admin.example.com",
apiHost: "api.example.com",
portalAppHost: "portal.example.com:443",
protocol: "https",
},
});
expect(decision).toEqual(expect.objectContaining({ type: "resolve-portal-tenant", tenantSlug: "acme" }));
});
test("rejects malformed direct hosts without throwing", () => {
const request = new Request("http://portal.localhost/acme", { headers: { host: "[malformed-host" } });
expect(getHostRoutingRuntimeDecision(request, portalOptions)).toEqual({ type: "reject", status: 404 });
expect(getHostRoutingRuntimeDecision(request)).toEqual({ type: "reject", status: 404 });
});
test.each(["/", "/a", "/Admin", "/admin", "/api/projects", "/portal/acme", "/__portal/acme", "/acme.data/projects", "//acme"])(
"rejects missing, invalid, reserved, or internal portal route %s",
(pathname) => {
expect(getHostRoutingRuntimeDecision(new Request(`http://portal.localhost${pathname}`), portalOptions)).toEqual({ type: "reject", status: 404 });
}
);
test("rewrites public tenant paths and React Router data requests into the internal namespace", () => {
const page = expectPortalDispatch(new Request("http://portal.localhost/acme/projects?tab=open#list"));
const data = expectPortalDispatch(new Request("http://portal.localhost/acme.data?_routes=routes%2Fportal"));
expect(page.target).toBe("web");
expect(page.serveStaticAssets).toBe(false);
expect(page.request.url).toBe("http://portal.localhost/__portal/acme/projects?tab=open#list");
expect(data.request.url).toBe("http://portal.localhost/__portal/acme.data?_routes=routes%2Fportal");
expect(page.request.headers.get("x-hivegrid-portal-tenant")).toBe("acme");
});
test("dispatches only the exact React Router manifest endpoint on the standard portal host", () => {
const decision = expectPortalDispatch(new Request("http://portal.localhost/__manifest?paths=%2Facme"));
expect(decision).toEqual(
expect.objectContaining({
target: "web",
serveStaticAssets: false,
reactRouterFallback: true,
})
);
expect(new URL(decision.request.url).pathname).toBe("/__manifest");
expect(getHostRoutingRuntimeDecision(new Request("http://portal.localhost/__manifest/extra"), portalOptions)).toEqual({
type: "reject",
status: 404,
});
});
test("dispatches standard-host framework and asset requests without a product rollout gate", () => {
for (const pathname of ["/__manifest?paths=%2Facme", "/assets/app.js", "/site.webmanifest"]) {
expect(getHostRoutingRuntimeDecision(new Request(`http://portal.localhost${pathname}`), portalOptions)).toEqual(
expect.objectContaining({ type: "dispatch", target: "web" })
);
}
});
test("requires tenant resolution before adding trusted context or rewriting the public path", () => {
const initial = getHostRoutingRuntimeDecision(new Request("http://portal.localhost/acme/projects"), portalOptions);
expect(initial.type).toBe("resolve-portal-tenant");
if (initial.type !== "resolve-portal-tenant") throw new Error(`Expected tenant resolution, received ${initial.type}`);
expect(initial.tenantSlug).toBe("acme");
expect(new URL(initial.request.url).pathname).toBe("/acme/projects");
expect(initial.request.headers.has("x-hivegrid-portal-tenant")).toBe(false);
expect(finalizePortalTenantRoutingDecision(initial, null)).toEqual({ type: "reject", status: 404 });
expect(finalizePortalTenantRoutingDecision(initial, "other")).toEqual({ type: "reject", status: 404 });
});
test("resolves an active custom host before routes, auth, or static assets", () => {
const initial = getHostRoutingRuntimeDecision(new Request("https://kunden.geerkens.de/projects?tab=open"), customPortalOptions);
expect(initial.type).toBe("resolve-portal-host");
if (initial.type !== "resolve-portal-host") throw new Error(`Expected host resolution, received ${initial.type}`);
expect(initial.hostname).toBe("kunden.geerkens.de");
expect(initial.request.headers.has("x-hivegrid-portal-tenant")).toBe(false);
expect(finalizePortalHostRoutingDecision(initial, null)).toEqual({ type: "reject", status: 404 });
const decision = finalizePortalHostRoutingDecision(initial, {
hostname: "kunden.geerkens.de",
tenantSlug: "acme",
activeOrigin: "https://kunden.geerkens.de",
});
expect(decision.type).toBe("dispatch");
if (decision.type !== "dispatch") throw new Error(`Expected dispatch, received ${decision.type}`);
expect(decision.request.url).toBe("https://kunden.geerkens.de/__portal/acme/projects?tab=open");
expect(decision.request.headers.get("x-hivegrid-portal-tenant")).toBe("acme");
expect(decision.request.headers.get("x-hivegrid-portal-origin")).toBe("https://kunden.geerkens.de");
expect(decision.request.headers.get("x-hivegrid-portal-custom-host")).toBe("kunden.geerkens.de");
});
test("keeps custom hosts fail-closed when the production runtime gate is disabled", () => {
const disabledOptions = { ...customPortalOptions, customDomainRuntimeEnabled: false };
for (const pathname of ["/", "/projects", "/assets/app.js", "/__manifest?paths=%2F", "/healthz"]) {
expect(getHostRoutingRuntimeDecision(new Request(`https://kunden.geerkens.de${pathname}`), disabledOptions)).toEqual({
type: "reject",
status: 404,
});
}
expect(getHostRoutingRuntimeDecision(new Request("https://portal.hivegrid.de/acme/projects"), disabledOptions)).toEqual(
expect.objectContaining({ type: "resolve-portal-tenant", tenantSlug: "acme" })
);
});
test("fails custom-host resolution closed for mismatches, noncanonical origins, and privileged operational paths", () => {
const initial = getHostRoutingRuntimeDecision(new Request("https://kunden.geerkens.de/"), customPortalOptions);
if (initial.type !== "resolve-portal-host") throw new Error(`Expected host resolution, received ${initial.type}`);
for (const resolved of [
{ hostname: "other.geerkens.de", tenantSlug: "acme", activeOrigin: "https://other.geerkens.de" },
{ hostname: "kunden.geerkens.de", tenantSlug: "acme", activeOrigin: "http://kunden.geerkens.de" },
{ hostname: "kunden.geerkens.de", tenantSlug: "acme", activeOrigin: "https://kunden.geerkens.de/path" },
{ hostname: "kunden.geerkens.de", tenantSlug: "Admin", activeOrigin: "https://kunden.geerkens.de" },
]) {
expect(finalizePortalHostRoutingDecision(initial, resolved)).toEqual({ type: "reject", status: 404 });
}
for (const pathname of ["/readyz", "/healthcheck", "/api/metrics"]) {
expect(getHostRoutingRuntimeDecision(new Request(`https://kunden.geerkens.de${pathname}`), customPortalOptions)).toEqual({
type: "reject",
status: 404,
});
}
});
test("requires a DNS-verified provision command before dependency-free custom-host health", () => {
const initial = getHostRoutingRuntimeDecision(
new Request("https://kunden.geerkens.de/healthz", {
headers: { "x-hivegrid-portal-tenant": "spoofed" },
}),
customPortalOptions
);
expect(initial).toEqual(expect.objectContaining({ type: "resolve-portal-host", hostname: "kunden.geerkens.de" }));
if (initial.type !== "resolve-portal-host") throw new Error(`Expected host resolution, received ${initial.type}`);
expect(finalizePortalHostRoutingDecision(initial, null)).toEqual({ type: "reject", status: 404 });
const decision = finalizePortalHostRoutingDecision(initial, {
hostname: "kunden.geerkens.de",
tenantSlug: "acme",
activeOrigin: "https://kunden.geerkens.de",
});
if (decision.type !== "dispatch") throw new Error(`Expected dispatch, received ${decision.type}`);
expect(decision).toEqual(expect.objectContaining({ target: "web", serveStaticAssets: false, reactRouterFallback: true }));
expect(new URL(decision.request.url).pathname).toBe("/healthz");
expect(decision.request.headers.has("x-hivegrid-portal-tenant")).toBe(false);
expect(decision.request.headers.has("x-hivegrid-portal-origin")).toBe(false);
expect(decision.request.headers.has("x-hivegrid-portal-custom-host")).toBe(false);
expect(getHostRoutingRuntimeDecision(new Request("https://kunden.geerkens.de/assets/app.js"), customPortalOptions).type).toBe("resolve-portal-host");
});
test("maps custom-host root, data, and assets only after resolution", () => {
const resolution = { hostname: "kunden.geerkens.de", tenantSlug: "acme", activeOrigin: "https://kunden.geerkens.de" };
for (const [pathname, expected] of [
["/", "/__portal/acme"],
["/.data?_routes=root", "/__portal/acme.data"],
["/projects.data?_routes=projects", "/__portal/acme/projects.data"],
]) {
const initial = getHostRoutingRuntimeDecision(new Request(`https://kunden.geerkens.de${pathname}`), customPortalOptions);
if (initial.type !== "resolve-portal-host") throw new Error(`Expected host resolution, received ${initial.type}`);
const decision = finalizePortalHostRoutingDecision(initial, resolution);
expect(decision.type).toBe("dispatch");
if (decision.type !== "dispatch") throw new Error(`Expected dispatch, received ${decision.type}`);
expect(new URL(decision.request.url).pathname).toBe(expected);
}
const assetInitial = getHostRoutingRuntimeDecision(new Request("https://kunden.geerkens.de/assets/app.js"), customPortalOptions);
expect(assetInitial.type).toBe("resolve-portal-host");
if (assetInitial.type !== "resolve-portal-host") throw new Error("Expected host resolution");
const asset = finalizePortalHostRoutingDecision(assetInitial, resolution);
expect(asset).toEqual(expect.objectContaining({ type: "dispatch", serveStaticAssets: true, reactRouterFallback: false }));
const manifestInitial = getHostRoutingRuntimeDecision(
new Request("https://kunden.geerkens.de/__manifest?paths=%2F%2C%2Fprojects%2C%2Fprojects%2Fproject-1&version=router-v1"),
customPortalOptions
);
expect(manifestInitial.type).toBe("resolve-portal-host");
if (manifestInitial.type !== "resolve-portal-host") throw new Error("Expected host resolution");
const manifest = finalizePortalHostRoutingDecision(manifestInitial, resolution);
expect(manifest).toEqual(expect.objectContaining({ type: "dispatch", serveStaticAssets: false, reactRouterFallback: true }));
if (manifest.type !== "dispatch") throw new Error("Expected manifest dispatch");
const manifestUrl = new URL(manifest.request.url);
expect(manifestUrl.pathname).toBe("/__manifest");
expect(manifestUrl.searchParams.get("paths")).toBe("/,/projects,/projects/project-1");
expect(manifestUrl.searchParams.get("version")).toBe("router-v1");
expect(manifest.request.headers.get("x-hivegrid-portal-tenant")).toBe("acme");
});
test("keeps custom-host manifest paths slugless when the trusted tenant collides with an alias", () => {
const initial = getHostRoutingRuntimeDecision(
new Request("https://kunden.geerkens.de/__manifest?paths=%2F%2C%2Fprojects%2C%2Fprojects%2Fproject-1"),
customPortalOptions
);
if (initial.type !== "resolve-portal-host") throw new Error("Expected host resolution");
const decision = finalizePortalHostRoutingDecision(initial, {
hostname: "kunden.geerkens.de",
tenantSlug: "projects",
activeOrigin: "https://kunden.geerkens.de",
});
if (decision.type !== "dispatch") throw new Error("Expected manifest dispatch");
expect(new URL(decision.request.url).searchParams.get("paths")).toBe("/,/projects,/projects/project-1");
expect(decision.request.headers.get("x-hivegrid-portal-tenant")).toBe("projects");
});
test.each(["", "projects", "//attacker.example/projects", "/projects,//attacker.example/projects"])(
"rejects malformed custom-host manifest paths %j",
(paths) => {
const requestUrl = new URL("https://kunden.geerkens.de/__manifest");
if (paths) requestUrl.searchParams.set("paths", paths);
const initial = getHostRoutingRuntimeDecision(new Request(requestUrl), customPortalOptions);
expect(initial.type).toBe("resolve-portal-host");
if (initial.type !== "resolve-portal-host") throw new Error("Expected host resolution");
expect(
finalizePortalHostRoutingDecision(initial, {
hostname: "kunden.geerkens.de",
tenantSlug: "acme",
activeOrigin: "https://kunden.geerkens.de",
})
).toEqual({ type: "reject", status: 404 });
}
);
test("preserves method and body while replacing spoofed portal context", async () => {
const decision = expectPortalDispatch(
new Request("http://portal.localhost/acme/projects", {
method: "POST",
body: "action=create",
headers: {
"content-type": "application/x-www-form-urlencoded",
"x-hivegrid-portal-tenant": "victim",
"x-hivegrid-portal-role": "admin",
},
})
);
expect(decision.request.method).toBe("POST");
expect(await decision.request.text()).toBe("action=create");
expect(decision.request.headers.get("x-hivegrid-portal-tenant")).toBe("acme");
expect(decision.request.headers.has("x-hivegrid-portal-role")).toBe(false);
});
test("allows only the explicit portal static namespaces on the exact portal host", () => {
for (const pathname of [
"/assets/app.js",
"/fonts/inter.woff2",
"/images/logo.svg",
"/locales/de/translations.json",
"/favicon.ico",
"/favicon-32x32.png",
]) {
expect(expectPortalDispatch(new Request(`http://portal.localhost${pathname}`)).serveStaticAssets).toBe(true);
expect(expectPortalDispatch(new Request(`http://portal.localhost${pathname}`)).reactRouterFallback).toBe(false);
}
expect(getHostRoutingRuntimeDecision(new Request("http://portal.localhost/sw.js"), portalOptions)).toEqual({ type: "reject", status: 404 });
expect(getHostRoutingRuntimeDecision(new Request("http://portal.localhost/favicon-16x16.png"), portalOptions)).toEqual({ type: "reject", status: 404 });
expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/assets/app.js"), portalOptions)).toEqual({ type: "reject", status: 404 });
});
test("allows operational routes without exposing static assets", () => {
for (const pathname of ["/healthz", "/readyz", "/healthcheck", "/api/metrics"]) {
const decision = expectPortalDispatch(new Request(`http://portal.localhost${pathname}`));
expect(decision.serveStaticAssets).toBe(false);
expect(new URL(decision.request.url).pathname).toBe(pathname);
}
});
test("strips client portal context headers from core dispatches too", () => {
const decision = expectDispatchDecision(
new Request("http://app.localhost/acme", {
headers: { "x-hivegrid-portal-tenant": "spoofed", "x-hivegrid-portal-role": "admin" },
})
);
expect(decision.request.headers.has("x-hivegrid-portal-tenant")).toBe(false);
expect(decision.request.headers.has("x-hivegrid-portal-role")).toBe(false);
});
});