From 693dc0c2657e09e09ada31f81085a0aec18275da Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 18 May 2026 18:06:17 -0700 Subject: [PATCH 1/2] fix(sandbox): Route forwarded provider egress Use Vercel Sandbox forwarded request headers as the credential egress boundary instead of routing provider traffic through a path-carried sandbox id. The proxy now verifies the sandbox OIDC token, uses its sandbox_id for the command-scoped egress session, and reconstructs upstream requests from forwarded host metadata plus the original path. Update the egress tests and specs to match the forwarded-request contract. Fixes #366 Co-authored-by: GPT-5 Codex --- packages/junior/src/app.ts | 21 ++-- .../junior/src/chat/sandbox/egress-oidc.ts | 2 - .../junior/src/chat/sandbox/egress-policy.ts | 16 +-- .../junior/src/chat/sandbox/egress-proxy.ts | 61 +++++----- .../src/handlers/sandbox-egress-proxy.ts | 17 ++- .../handlers/sandbox-egress-proxy.test.ts | 104 ++++++++++++------ specs/security-policy.md | 4 +- specs/skill-capabilities-spec.md | 4 +- 8 files changed, 138 insertions(+), 91 deletions(-) diff --git a/packages/junior/src/app.ts b/packages/junior/src/app.ts index 86899308..cac0c149 100644 --- a/packages/junior/src/app.ts +++ b/packages/junior/src/app.ts @@ -7,7 +7,10 @@ import { GET as dashboardGET } from "@/handlers/diagnostics-dashboard"; import { GET as healthGET } from "@/handlers/health"; import { GET as mcpOauthCallbackGET } from "@/handlers/mcp-oauth-callback"; import { GET as oauthCallbackGET } from "@/handlers/oauth-callback"; -import { ALL as sandboxEgressProxyALL } from "@/handlers/sandbox-egress-proxy"; +import { + ALL as sandboxEgressProxyALL, + isSandboxEgressRequest, +} from "@/handlers/sandbox-egress-proxy"; import { POST as turnResumePOST } from "@/handlers/turn-resume"; import { POST as webhooksPOST } from "@/handlers/webhooks"; import type { WaitUntilFn } from "@/handlers/types"; @@ -70,6 +73,15 @@ export async function createApp(options?: JuniorAppOptions): Promise { return c.text("Internal Server Error", 500); }); + app.use("*", async (c, next) => { + // Vercel Sandbox proxying preserves the original upstream path, so detect + // authenticated proxy traffic before ordinary application routes claim it. + if (isSandboxEgressRequest(c.req.raw)) { + return await sandboxEgressProxyALL(c.req.raw); + } + await next(); + }); + app.get("/", () => dashboardGET()); app.get("/health", () => healthGET()); @@ -91,13 +103,6 @@ export async function createApp(options?: JuniorAppOptions): Promise { return turnResumePOST(c.req.raw, waitUntil); }); - app.all("/api/internal/sandbox-egress/:egressId", (c) => { - return sandboxEgressProxyALL(c.req.raw, c.req.param("egressId")); - }); - app.all("/api/internal/sandbox-egress/:egressId/*", (c) => { - return sandboxEgressProxyALL(c.req.raw, c.req.param("egressId")); - }); - app.post("/api/webhooks/:platform", (c) => { return webhooksPOST(c.req.raw, c.req.param("platform"), waitUntil); }); diff --git a/packages/junior/src/chat/sandbox/egress-oidc.ts b/packages/junior/src/chat/sandbox/egress-oidc.ts index bd976c52..80167600 100644 --- a/packages/junior/src/chat/sandbox/egress-oidc.ts +++ b/packages/junior/src/chat/sandbox/egress-oidc.ts @@ -94,7 +94,6 @@ export function validateVercelSandboxOidcClaims( /** Verify Vercel signed this Sandbox firewall proxy request for the active VM session. */ export async function verifyVercelSandboxOidcToken( token: string, - egressId: string, ): Promise { const unverified = decodeJwt(token); if (typeof unverified.iss !== "string") { @@ -107,6 +106,5 @@ export async function verifyVercelSandboxOidcToken( // The Sandbox proxy token is request identity. Do not compare its audience, // team, or project claims with deployment OIDC; the egress session decides // whether this VM session may activate requester-bound credentials. - validateSandboxClaim(verified.payload, egressId); return verified.payload; } diff --git a/packages/junior/src/chat/sandbox/egress-policy.ts b/packages/junior/src/chat/sandbox/egress-policy.ts index 70d54e82..094b7d43 100644 --- a/packages/junior/src/chat/sandbox/egress-policy.ts +++ b/packages/junior/src/chat/sandbox/egress-policy.ts @@ -5,8 +5,6 @@ import { getPluginProviders } from "@/chat/plugins/registry"; import type { PluginManifest } from "@/chat/plugins/types"; import { resolveBaseUrl } from "@/chat/oauth-flow"; -const SANDBOX_EGRESS_PROXY_PATH = "/api/internal/sandbox-egress"; - /** Return whether an outbound host is covered by a sandbox egress domain rule. */ export function matchesSandboxEgressDomain( host: string, @@ -42,27 +40,21 @@ export function resolveSandboxEgressProviderForHost( )?.provider; } -function proxyUrl(egressId: string): string | undefined { +function proxyUrl(): string | undefined { const baseUrl = resolveBaseUrl(); if (!baseUrl) { return undefined; } - const url = new URL( - `${SANDBOX_EGRESS_PROXY_PATH}/${encodeURIComponent(egressId)}`, - baseUrl, - ); - return url.toString(); + return new URL("/", baseUrl).toString(); } /** Build the forwarding policy that keeps provider credentials outside the sandbox. */ -export function buildSandboxEgressNetworkPolicy( - egressId: string, -): NetworkPolicy | undefined { +export function buildSandboxEgressNetworkPolicy(): NetworkPolicy | undefined { const entries = providerEntries(); if (entries.length === 0) { return undefined; } - const forwardURL = proxyUrl(egressId); + const forwardURL = proxyUrl(); if (!forwardURL) { // Credential placeholders must not reach real provider domains. If Junior // cannot receive forwarded requests, fail setup before running commands. diff --git a/packages/junior/src/chat/sandbox/egress-proxy.ts b/packages/junior/src/chat/sandbox/egress-proxy.ts index fd8b457e..2f505774 100644 --- a/packages/junior/src/chat/sandbox/egress-proxy.ts +++ b/packages/junior/src/chat/sandbox/egress-proxy.ts @@ -14,12 +14,12 @@ import { type SandboxEgressCredentialLease, type SandboxEgressSession, } from "@/chat/sandbox/egress-session"; +import type { JWTPayload } from "jose"; const OIDC_TOKEN_HEADER = "vercel-sandbox-oidc-token"; const FORWARDED_HOST_HEADER = "vercel-forwarded-host"; const FORWARDED_SCHEME_HEADER = "vercel-forwarded-scheme"; const FORWARDED_PORT_HEADER = "vercel-forwarded-port"; -const ROUTE_PREFIX = "/api/internal/sandbox-egress"; const HOP_BY_HOP_HEADERS = new Set([ "connection", "host", @@ -44,7 +44,7 @@ const DECODED_RESPONSE_HEADERS = new Set([ const AUTH_REJECTION_STATUS = new Set([401, 403]); interface ProxyDeps { fetch?: typeof fetch; - verifyOidc?: (token: string, egressId: string) => Promise; + verifyOidc?: (token: string) => Promise; } type UpstreamUrlResult = { ok: true; url: URL } | { ok: false; error: string }; @@ -82,22 +82,18 @@ function normalizePort(value: string | null): string | undefined { return port >= 1 && port <= 65_535 ? trimmed : undefined; } -function upstreamPath(request: Request, egressId: string): string | undefined { +function sandboxIdFromPayload(payload: JWTPayload): string | undefined { + return typeof payload.sandbox_id === "string" + ? payload.sandbox_id + : undefined; +} + +function upstreamPath(request: Request): string { const url = new URL(request.url); - const prefix = `${ROUTE_PREFIX}/${encodeURIComponent(egressId)}`; - if (url.pathname === prefix) { - return `/${url.search}`; - } - if (url.pathname.startsWith(`${prefix}/`)) { - return `${url.pathname.slice(prefix.length)}${url.search}`; - } - return undefined; + return `${url.pathname}${url.search}`; } -function buildUpstreamUrl( - request: Request, - egressId: string, -): UpstreamUrlResult { +function buildUpstreamUrl(request: Request): UpstreamUrlResult { const forwardedHost = request.headers.get(FORWARDED_HOST_HEADER); if (!forwardedHost?.trim()) { return { ok: false, error: "Missing forwarded host" }; @@ -119,10 +115,7 @@ function buildUpstreamUrl( if (forwardedPort && !port) { return { ok: false, error: "Invalid forwarded port" }; } - const path = upstreamPath(request, egressId); - if (!path) { - return { ok: false, error: "Invalid egress route" }; - } + const path = upstreamPath(request); try { const url = new URL(`${scheme}://${host}${port ? `:${port}` : ""}${path}`); return { ok: true, url }; @@ -230,10 +223,18 @@ function hasTransformForHost( ); } +/** Return whether a request appears to be from the Vercel Sandbox egress proxy. */ +export function isSandboxEgressForwardedRequest(request: Request): boolean { + return Boolean( + request.headers.get(OIDC_TOKEN_HEADER)?.trim() && + request.headers.get(FORWARDED_HOST_HEADER)?.trim() && + request.headers.get(FORWARDED_SCHEME_HEADER)?.trim(), + ); +} + /** Proxy one Vercel Sandbox firewall egress request through Junior credential activation. */ export async function proxySandboxEgressRequest( request: Request, - egressId: string, deps: ProxyDeps = {}, ): Promise { const oidcToken = request.headers.get(OIDC_TOKEN_HEADER)?.trim(); @@ -241,10 +242,10 @@ export async function proxySandboxEgressRequest( return jsonError("Missing Vercel Sandbox OIDC token", 401); } + let oidcPayload: JWTPayload; try { - await (deps.verifyOidc ?? verifyVercelSandboxOidcToken)( + oidcPayload = await (deps.verifyOidc ?? verifyVercelSandboxOidcToken)( oidcToken, - egressId, ); } catch (error) { logWarn( @@ -259,7 +260,15 @@ export async function proxySandboxEgressRequest( return jsonError("Invalid Vercel Sandbox OIDC token", 401); } - const upstreamResult = buildUpstreamUrl(request, egressId); + const activeEgressId = sandboxIdFromPayload(oidcPayload); + if (!activeEgressId) { + return jsonError( + "Vercel Sandbox OIDC token did not include sandbox_id", + 401, + ); + } + + const upstreamResult = buildUpstreamUrl(request); if (!upstreamResult.ok) { return jsonError(upstreamResult.error, 400); } @@ -272,14 +281,14 @@ export async function proxySandboxEgressRequest( // Vercel OIDC authenticates the forwarded VM session; Junior's egress // session authorizes credential activation for the current requester. - const session = await getSandboxEgressSession(egressId); + const session = await getSandboxEgressSession(activeEgressId); if (!session) { return jsonError("Sandbox egress session is not authorized", 403); } let lease: SandboxEgressCredentialLease; try { - lease = await credentialLease(egressId, provider, session); + lease = await credentialLease(activeEgressId, provider, session); } catch (error) { if (error instanceof CredentialUnavailableError) { return new Response( @@ -316,7 +325,7 @@ export async function proxySandboxEgressRequest( }, "Sandbox egress upstream auth rejected", ); - await clearSandboxEgressCredentialLease(egressId, provider, session); + await clearSandboxEgressCredentialLease(activeEgressId, provider, session); } return new Response(upstream.body, { diff --git a/packages/junior/src/handlers/sandbox-egress-proxy.ts b/packages/junior/src/handlers/sandbox-egress-proxy.ts index 90302a8b..186eaf85 100644 --- a/packages/junior/src/handlers/sandbox-egress-proxy.ts +++ b/packages/junior/src/handlers/sandbox-egress-proxy.ts @@ -1,9 +1,14 @@ -import { proxySandboxEgressRequest } from "@/chat/sandbox/egress-proxy"; +import { + isSandboxEgressForwardedRequest, + proxySandboxEgressRequest, +} from "@/chat/sandbox/egress-proxy"; /** Handles Vercel Sandbox firewall egress proxy requests. */ -export async function ALL( - request: Request, - egressId: string, -): Promise { - return await proxySandboxEgressRequest(request, egressId); +export async function ALL(request: Request): Promise { + return await proxySandboxEgressRequest(request); +} + +/** Return whether a request should be routed through sandbox egress proxying. */ +export function isSandboxEgressRequest(request: Request): boolean { + return isSandboxEgressForwardedRequest(request); } diff --git a/packages/junior/tests/unit/handlers/sandbox-egress-proxy.test.ts b/packages/junior/tests/unit/handlers/sandbox-egress-proxy.test.ts index 99767701..2e37a14f 100644 --- a/packages/junior/tests/unit/handlers/sandbox-egress-proxy.test.ts +++ b/packages/junior/tests/unit/handlers/sandbox-egress-proxy.test.ts @@ -70,7 +70,10 @@ import { validateVercelSandboxOidcClaims, verifyVercelSandboxOidcToken, } from "@/chat/sandbox/egress-oidc"; -import { proxySandboxEgressRequest } from "@/chat/sandbox/egress-proxy"; +import { + isSandboxEgressForwardedRequest, + proxySandboxEgressRequest, +} from "@/chat/sandbox/egress-proxy"; import { upsertSandboxEgressSession } from "@/chat/sandbox/egress-session"; import { disconnectStateAdapter } from "@/chat/state/adapter"; import { CredentialUnavailableError } from "@/chat/credentials/broker"; @@ -116,7 +119,7 @@ function egressRequest( } = {}, ): Request { return new Request( - `https://junior.example.com/api/internal/sandbox-egress/${EGRESS_ID}${input.path ?? "/api/0/issues/"}`, + `https://junior.example.com${input.path ?? "/api/0/issues/"}`, { method: input.method ?? "GET", headers: { @@ -139,9 +142,9 @@ function proxy( async () => new Response("ok"), ) as typeof fetch, ): Promise { - return proxySandboxEgressRequest(request, EGRESS_ID, { + return proxySandboxEgressRequest(request, { fetch: fetchMock, - verifyOidc: async () => ({ sub: "sandbox" }), + verifyOidc: async () => ({ sandbox_id: EGRESS_ID }), }); } @@ -168,17 +171,17 @@ describe("sandbox egress proxy", () => { it("builds provider forwarding policy for sandbox egress", () => { expect(matchesSandboxEgressDomain("SENTRY.IO", "sentry.io")).toBe(true); expect(matchesSandboxEgressDomain("eu.sentry.io", "sentry.io")).toBe(false); - expect(buildSandboxEgressNetworkPolicy(EGRESS_ID)).toEqual({ + expect(buildSandboxEgressNetworkPolicy()).toEqual({ allow: { "*": [], "sentry.io": [ { - forwardURL: `https://junior.example.com/api/internal/sandbox-egress/${EGRESS_ID}`, + forwardURL: "https://junior.example.com/", }, ], "us.sentry.io": [ { - forwardURL: `https://junior.example.com/api/internal/sandbox-egress/${EGRESS_ID}`, + forwardURL: "https://junior.example.com/", }, ], }, @@ -188,7 +191,7 @@ describe("sandbox egress proxy", () => { it("fails sandbox egress policy setup without a public callback URL", () => { delete process.env.JUNIOR_BASE_URL; - expect(() => buildSandboxEgressNetworkPolicy(EGRESS_ID)).toThrow( + expect(() => buildSandboxEgressNetworkPolicy()).toThrow( "Cannot determine base URL for sandbox credential egress", ); }); @@ -210,14 +213,11 @@ describe("sandbox egress proxy", () => { }); }); - it("requires OIDC before route configuration details", async () => { + it("requires OIDC before proxy configuration details", async () => { delete process.env.JUNIOR_BASE_URL; const response = await ALL( - new Request( - `https://junior.example.com/api/internal/sandbox-egress/${EGRESS_ID}`, - ), - EGRESS_ID, + new Request("https://junior.example.com/api/0/issues/"), ); expect(response.status).toBe(401); @@ -284,6 +284,51 @@ describe("sandbox egress proxy", () => { expect(issueProviderCredentialLeaseMock).toHaveBeenCalledTimes(1); }); + it("forwards root-path sandbox proxy requests using the verified OIDC session", async () => { + await authorizeSandboxEgress(); + mockSentryLease(); + + const fetchMock = vi.fn(async (url: URL | string, init?: RequestInit) => { + expect(String(url)).toBe( + "https://sentry.io/api/0/issues/?query=forwarded", + ); + expect(new Headers(init?.headers).get("authorization")).toBe( + "Bearer sentry-token", + ); + return new Response("ok", { status: 200 }); + }); + + const response = await proxySandboxEgressRequest( + egressRequest({ path: "/api/0/issues/?query=forwarded" }), + { + fetch: fetchMock as typeof fetch, + verifyOidc: async () => ({ sandbox_id: EGRESS_ID }), + }, + ); + + expect(response.status).toBe(200); + await expect(response.text()).resolves.toBe("ok"); + expect(issueProviderCredentialLeaseMock).toHaveBeenCalledWith({ + provider: "sentry", + requesterId: REQUESTER_ID, + reason: "sandbox-egress:sentry", + }); + }); + + it("recognizes root-path forwarded sandbox proxy requests", () => { + expect(isSandboxEgressForwardedRequest(egressRequest())).toBe(true); + expect( + isSandboxEgressForwardedRequest( + new Request("https://junior.example.com/api/0/issues/", { + headers: { + "vercel-forwarded-host": "sentry.io", + "vercel-forwarded-scheme": "https", + }, + }), + ), + ).toBe(false); + }); + it("does not synthesize an empty body for bodyless methods", async () => { await authorizeSandboxEgress(); mockSentryLease(); @@ -576,26 +621,19 @@ describe("sandbox egress proxy", () => { expect(fetchMock).not.toHaveBeenCalled(); }); - it("rejects requests outside the sandbox egress route", async () => { + it("requires the verified OIDC token to identify the sandbox session", async () => { const fetchMock = vi.fn(); - const response = await proxy( - new Request(`https://junior.example.com/not-egress/${EGRESS_ID}`, { - headers: { - "vercel-forwarded-host": "sentry.io", - "vercel-forwarded-scheme": "https", - "vercel-sandbox-oidc-token": "signed-token", - }, - }), - fetchMock as typeof fetch, - ); + const response = await proxySandboxEgressRequest(egressRequest(), { + fetch: fetchMock as typeof fetch, + verifyOidc: async () => ({ sub: "sandbox" }), + }); - expect(response.status).toBe(400); + expect(response.status).toBe(401); await expect(response.json()).resolves.toEqual({ - error: "Invalid egress route", + error: "Vercel Sandbox OIDC token did not include sandbox_id", }); expect(fetchMock).not.toHaveBeenCalled(); - expect(issueProviderCredentialLeaseMock).not.toHaveBeenCalled(); }); it("rejects plaintext forwarded schemes before credential injection", async () => { @@ -692,8 +730,8 @@ describe("sandbox egress proxy", () => { ); vi.stubGlobal("fetch", fetchMock); - await verifyVercelSandboxOidcToken("signed-token-1", EGRESS_ID); - await verifyVercelSandboxOidcToken("signed-token-2", EGRESS_ID); + await verifyVercelSandboxOidcToken("signed-token-1"); + await verifyVercelSandboxOidcToken("signed-token-2"); expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock.mock.calls[0]?.[1]).toEqual({ redirect: "error" }); @@ -721,7 +759,7 @@ describe("sandbox egress proxy", () => { ), ); - await verifyVercelSandboxOidcToken("signed-token", EGRESS_ID); + await verifyVercelSandboxOidcToken("signed-token"); expect(jwtVerifyMock).toHaveBeenCalledWith( "signed-token", @@ -743,9 +781,9 @@ describe("sandbox egress proxy", () => { ); vi.stubGlobal("fetch", fetchMock); - await expect( - verifyVercelSandboxOidcToken("signed-token", EGRESS_ID), - ).rejects.toThrow("jwks_uri"); + await expect(verifyVercelSandboxOidcToken("signed-token")).rejects.toThrow( + "jwks_uri", + ); expect(createRemoteJWKSetMock).not.toHaveBeenCalled(); expect(jwtVerifyMock).not.toHaveBeenCalled(); diff --git a/specs/security-policy.md b/specs/security-policy.md index 56a78261..f7bb8df2 100644 --- a/specs/security-policy.md +++ b/specs/security-policy.md @@ -32,8 +32,8 @@ This policy applies to: - Production should use explicit network policy and minimal allowlists. - Credential-capable provider domains should route through the Junior sandbox egress proxy instead of receiving long-lived sandbox secrets. -- Proxied sandbox egress requests must verify the Vercel-signed Sandbox OIDC token, validate its sandbox claim against the active VM session used in the forwarding route, resolve the provider from the forwarded host, and require a requester-bound sandbox egress session. Egress sessions are command-scoped, and cached provider leases must be scoped to one session activation. -- The public egress route must verify Vercel Sandbox OIDC before returning configuration, provider, or session-specific responses. +- Proxied sandbox egress requests must verify the Vercel-signed Sandbox OIDC token, use its sandbox claim as the active VM session id, resolve the provider from the forwarded host, and require a requester-bound sandbox egress session. Egress sessions are command-scoped, and cached provider leases must be scoped to one session activation. +- The egress handler must verify Vercel Sandbox OIDC before returning configuration, provider, or session-specific responses. - The egress proxy must not reject duplicate method/URL/body requests as replay; duplicate request shapes can be legitimate retries. Requester-bound credential issuance is the security boundary. ### Harness-owned tool targeting diff --git a/specs/skill-capabilities-spec.md b/specs/skill-capabilities-spec.md index a6279496..20c4ff91 100644 --- a/specs/skill-capabilities-spec.md +++ b/specs/skill-capabilities-spec.md @@ -90,8 +90,8 @@ Rules: ### Sandbox egress proxy -- New sandbox sessions use a Vercel Sandbox network policy that forwards declared credential provider domains to Junior's internal egress route. -- The internal egress route must verify the Vercel Sandbox OIDC token before proxying. +- New sandbox sessions use a Vercel Sandbox network policy that forwards declared credential provider domains to Junior's internal egress handler. +- The internal egress handler must verify the Vercel Sandbox OIDC token before proxying. - The egress route must reconstruct the upstream URL only from Vercel forwarded host/scheme/port headers and the request path. - The egress route must reject forwarded hosts that do not match a registered provider domain. - The proxy must not use method/URL/body-only replay fingerprints as an authorization boundary because duplicate request shapes can be legitimate client retries. From fff978790ce57596fabbdfb55e7d9e28caf074ae Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 18 May 2026 18:14:35 -0700 Subject: [PATCH 2/2] ref(sandbox): Remove stale egress route validator Drop the unused helper that described the old route-bound egress check. The proxy now uses the verified Vercel Sandbox OIDC sandbox_id as the active egress session key, so keeping the old helper made the security model look split across two paths. Refs GH-366 Co-Authored-By: GPT-5 Codex --- .../junior/src/chat/sandbox/egress-oidc.ts | 14 ----------- .../handlers/sandbox-egress-proxy.test.ts | 25 +------------------ 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/packages/junior/src/chat/sandbox/egress-oidc.ts b/packages/junior/src/chat/sandbox/egress-oidc.ts index 80167600..9d81134e 100644 --- a/packages/junior/src/chat/sandbox/egress-oidc.ts +++ b/packages/junior/src/chat/sandbox/egress-oidc.ts @@ -77,20 +77,6 @@ async function getJwks( return jwks; } -function validateSandboxClaim(payload: JWTPayload, egressId: string): void { - if (payload.sandbox_id !== egressId) { - throw new Error("Vercel OIDC token belongs to a different sandbox"); - } -} - -/** Validate that a verified Vercel Sandbox proxy token is bound to this route. */ -export function validateVercelSandboxOidcClaims( - payload: JWTPayload, - egressId: string, -): void { - validateSandboxClaim(payload, egressId); -} - /** Verify Vercel signed this Sandbox firewall proxy request for the active VM session. */ export async function verifyVercelSandboxOidcToken( token: string, diff --git a/packages/junior/tests/unit/handlers/sandbox-egress-proxy.test.ts b/packages/junior/tests/unit/handlers/sandbox-egress-proxy.test.ts index 2e37a14f..5d6869f5 100644 --- a/packages/junior/tests/unit/handlers/sandbox-egress-proxy.test.ts +++ b/packages/junior/tests/unit/handlers/sandbox-egress-proxy.test.ts @@ -66,10 +66,7 @@ import { matchesSandboxEgressDomain, resolveSandboxCommandEnvironment, } from "@/chat/sandbox/egress-policy"; -import { - validateVercelSandboxOidcClaims, - verifyVercelSandboxOidcToken, -} from "@/chat/sandbox/egress-oidc"; +import { verifyVercelSandboxOidcToken } from "@/chat/sandbox/egress-oidc"; import { isSandboxEgressForwardedRequest, proxySandboxEgressRequest, @@ -694,26 +691,6 @@ describe("sandbox egress proxy", () => { expect(issueProviderCredentialLeaseMock).not.toHaveBeenCalled(); }); - it("requires OIDC sandbox claims to match the egress route", () => { - expect(() => - validateVercelSandboxOidcClaims( - { - sandbox_id: EGRESS_ID, - }, - EGRESS_ID, - ), - ).not.toThrow(); - - expect(() => - validateVercelSandboxOidcClaims( - { - sandbox_id: "other-sandbox", - }, - EGRESS_ID, - ), - ).toThrow("different sandbox"); - }); - it("caches Vercel OIDC discovery metadata by issuer", async () => { decodeJwtMock.mockReturnValue({ iss: "https://oidc.vercel.com/cache-test",