Ctrl K
ComunidadGuías…

proxy-machine-protocol.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { afterEach, describe, expect, it, vi } from "vitest";
import { NextRequest } from "next/server";

const TEMPLATE = "{id}.mso.example.com";

async function loadProxy(template: string) {
  vi.resetModules();
  vi.stubEnv("NEXT_PUBLIC_MANAGED_APP_HOST_TEMPLATE", template);
  return (await import("./proxy")).proxy;
}

function req(
  host: string,
  path: string,
  init: { method?: string; headers?: HeadersInit; body?: string } = {},
): NextRequest {
  const headers = new Headers(init.headers);
  headers.set("host", host);
  return new NextRequest(`https://${host}${path}`, { ...init, headers });
}

const rewriteOf = (res: Response) => res.headers.get("x-middleware-rewrite");

afterEach(() => {
  vi.unstubAllEnvs();
});

// The session cookie is widened to Domain=mso.example.com, so ANY host under that
// name receives it. A host in the namespace that is not one of the apps must
// therefore 404 rather than serve the cockpit — otherwise adding a DNS record is
// enough to hand a page a fully authenticated cockpit, with no code change at all.
describe("an unclaimed host inside the app namespace serves nothing", () => {
  it.each([
    "staging.mso.example.com",
    "deep.sub.mso.example.com",
    "xhermes.mso.example.com",
  ])("404s %s", async (host) => {
    const proxy = await loadProxy(TEMPLATE);
    for (const path of [
      "/",
      "/api/v1/exec/run",
      "/api/auth/login",
      "/_next/static/chunks/main.js",
    ]) {
      const res = await proxy(req(host, path));
      expect(res.status).toBe(404);
      expect(rewriteOf(res)).toBeNull();
    }
  });

  it("refuses only the namespace: the cockpit, its siblings and single-origin mode all still serve", async () => {
    const split = await loadProxy(TEMPLATE);
    for (const host of [
      "mso.example.com",
      "api-mso.example.com",
      "rahmanef.com",
    ]) {
      expect((await split(req(host, "/files"))).status).toBe(200);
    }
    const single = await loadProxy("");
    expect(
      (await single(req("staging.mso.example.com", "/files"))).status,
    ).toBe(200);
  });
});

describe("single-origin mode (template unset) is unchanged", () => {
  it("stamps a nonce + document policy on a page", async () => {
    const proxy = await loadProxy("");
    const res = await proxy(req("mso.example.com", "/files"));
    const csp = res.headers.get("content-security-policy")!;
    expect(csp).toContain("frame-ancestors 'none'");
    expect(csp).toMatch(/script-src 'self' 'nonce-[^']+' 'strict-dynamic'/);
    expect(res.headers.get("x-middleware-request-x-nonce")).toBeTruthy();
  });

  it("waves through the paths the matcher used to exclude, with no policy", async () => {
    const proxy = await loadProxy("");
    for (const path of [
      "/_next/static/chunks/main.js",
      "/_next/image?url=%2Fx.png",
      "/favicon.ico",
    ]) {
      const res = await proxy(req("mso.example.com", path));
      expect(res.status).toBe(200);
      expect(res.headers.get("content-security-policy")).toBeNull();
      expect(res.headers.get("x-middleware-request-x-nonce")).toBeNull();
    }
  });

  it("never treats an app-shaped host as an app host", async () => {
    const proxy = await loadProxy("");
    const res = await proxy(req("hermes.mso.example.com", "/"));
    expect(rewriteOf(res)).toBeNull();
  });

  it("still blocks a cross-site mutating /api and leaves reads alone", async () => {
    const proxy = await loadProxy("");
    const blocked = await proxy(
      req("mso.example.com", "/api/v1/exec/run", {
        method: "POST",
        headers: { "sec-fetch-site": "cross-site" },
      }),
    );
    expect(blocked.status).toBe(403);
    const read = await proxy(req("mso.example.com", "/api/v1/sys/status"));
    expect(read.status).toBe(200);
    expect(read.headers.get("content-security-policy")).toBeNull();
  });

  it("treats A2A discovery and protocol routes as machine surfaces, not documents", async () => {
    const proxy = await loadProxy("");
    for (const path of ["/.well-known/agent-card.json", "/a2a/v1"]) {
      const res = await proxy(req("mso.example.com", path));
      expect(res.status).toBe(200);
      expect(res.headers.get("content-security-policy")).toBeNull();
      expect(res.headers.get("x-middleware-request-x-nonce")).toBeNull();
    }
  });

  it("lets remote bearer-authenticated A2A POST reach the route while keeping owner A2A management CSRF-protected", async () => {
    const proxy = await loadProxy("");
    const protocol = await proxy(
      req("mso.example.com", "/a2a/v1", {
        method: "POST",
        headers: { "sec-fetch-site": "cross-site" },
        body: JSON.stringify({
          jsonrpc: "2.0",
          id: 1,
          method: "ListTasks",
          params: {},
        }),
      }),
    );
    expect(protocol.status).toBe(200);
    expect(protocol.headers.get("content-security-policy")).toBeNull();

    const management = await proxy(
      req("mso.example.com", "/api/v1/a2a", {
        method: "POST",
        headers: { "sec-fetch-site": "cross-site" },
        body: JSON.stringify({ action: "state" }),
      }),
    );
    expect(management.status).toBe(403);
    expect(await management.json()).toEqual({ error: "cross_origin_blocked" });
  });

  // Full coverage of this bridge (forged cookie, revoked device, upgrade, off-box
  // destination) lives in proxy-websocket.test.ts, which has the session harness.
  it("does not let an unverified cookie near the camoufox VNC bridge", async () => {
    const proxy = await loadProxy("");
    const res = await proxy(
      req("mso.example.com", "/camoufox-vnc/", {
        headers: { cookie: "session=anything" },
      }),
    );
    expect(res.status).toBe(404);
    expect(res.headers.get("x-middleware-rewrite")).toBeNull();
  });

  it("allows cross-origin workflow webhook triggers while keeping workflow management CSRF-protected", async () => {
    const proxy = await loadProxy("");
    const webhook = await proxy(
      req("mso.example.com", "/api/v1/workflows/webhook/graph-1/hook", {
        method: "POST",
        headers: { "sec-fetch-site": "cross-site", "content-type": "application/json" },
        body: JSON.stringify({ event: "smoke" }),
      }),
    );
    expect(webhook.status).toBe(200);

    const management = await proxy(
      req("mso.example.com", "/api/v1/workflows", {
        method: "POST",
        headers: { "sec-fetch-site": "cross-site", "content-type": "application/json" },
        body: JSON.stringify({ action: "create" }),
      }),
    );
    expect(management.status).toBe(403);
    expect(await management.json()).toEqual({ error: "cross_origin_blocked" });
  });

});