From eb9d587e9ccf1d37caf609a479963f1313a294a5 Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 07:11:15 +0000 Subject: [PATCH] fix(orb-broker): improve error handling for token exchange failures --- src/api/routes.ts | 11 +++++++++-- src/orb/broker.ts | 13 ++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/api/routes.ts b/src/api/routes.ts index eb9cb34214..ec0ff71ceb 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -2981,8 +2981,15 @@ export function createApp() { const auth = c.req.header("authorization") ?? ""; const secret = auth.startsWith("Bearer ") ? auth.slice(7).trim() : ""; if (!secret) return c.json({ error: "missing_enrollment_secret" }, 401); - const result = await brokerOrbToken(c.env, secret); - if ("error" in result) return c.json(result, result.error === "invalid_enrollment" ? 401 : 403); + let result: Awaited>; + try { + result = await brokerOrbToken(c.env, secret); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(JSON.stringify({ level: "error", event: "orb_broker_mint_failed", message: message.slice(0, 200) })); + return c.json({ error: "broker_error" }, 503); + } + if ("error" in result) return c.json(result, result.error === "invalid_enrollment" ? 401 : result.error === "broker_misconfigured" ? 503 : 403); return c.json(result); }); diff --git a/src/orb/broker.ts b/src/orb/broker.ts index a3af7cd129..8d07d1e3af 100644 --- a/src/orb/broker.ts +++ b/src/orb/broker.ts @@ -48,12 +48,23 @@ export async function issueOrbEnrollment( return { enrollId, secret }; } -export type BrokerResult = { token: string; installationId: number; expiresAt: string } | { error: "invalid_enrollment" | "installation_not_eligible" }; +export type BrokerResult = { token: string; installationId: number; expiresAt: string } | { error: "invalid_enrollment" | "installation_not_eligible" | "broker_misconfigured" }; /** The container's token-exchange: a valid enrollment secret → a short-lived installation token for the BOUND * install. installation_id is read from the enrollment row, never the caller; the install must still be * registered=1 and neither suspended nor removed at mint time (the gate is re-checked, not trusted from issue). */ export async function brokerOrbToken(env: Env, secret: string): Promise { + // Validate Orb App credentials up front so a misconfiguration returns a structured error (503) rather than an + // unhandled exception that manifests as a generic 500 to the self-hosted engine. + if (!env.ORB_GITHUB_APP_ID || !env.ORB_GITHUB_APP_PRIVATE_KEY) { + console.error(JSON.stringify({ level: "error", event: "orb_broker_misconfigured", message: "ORB_GITHUB_APP_ID or ORB_GITHUB_APP_PRIVATE_KEY is not set; broker cannot mint tokens." })); + return { error: "broker_misconfigured" }; + } + // Warn when TOKEN_ENCRYPTION_SECRET is absent — without it, the broker cache is bypassed and every exchange hits + // GitHub's token endpoint, dramatically increasing exposure to throttle-induced failures. + if (!env.TOKEN_ENCRYPTION_SECRET) { + console.warn(JSON.stringify({ level: "warn", event: "orb_broker_no_encryption_key", message: "TOKEN_ENCRYPTION_SECRET is not set; broker token cache is disabled. Set this variable to enable caching and reduce GitHub throttle risk." })); + } const row = await env.DB .prepare("SELECT enroll_id, installation_id, state, revoked_at, cached_token_json FROM orb_enrollments WHERE secret_hash = ?") .bind(await hashToken(secret))