From 36bb9585c649f84fab3dffddf9d29731c72c8e27 Mon Sep 17 00:00:00 2001 From: bitfathers94 <237535319+bitfathers94@users.noreply.github.com> Date: Sun, 26 Jul 2026 13:19:01 +0000 Subject: [PATCH] fix(orb): wire backfillOrbInstallations to an admin backfill route The registry reconciliation backfillOrbInstallations promises in its doc comment -- recovering installs whose installation webhook fired before the receiver secret was configured -- had no caller anywhere in src/, so the capability never ran. Expose it through a bearer-gated POST /v1/internal/orb/installations/backfill admin route alongside the existing /v1/internal/orb/installations endpoints, returning { backfilled }. Closes #8882 --- src/api/routes.ts | 10 +++++++ test/integration/orb-onboarding.test.ts | 35 ++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/api/routes.ts b/src/api/routes.ts index 827ed51fdc..e19403903f 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -155,6 +155,7 @@ import { requestAprRepoTransfer } from "../orb/apr-repo-transfer"; import { handleOrbIngest, readOrbIngestBody } from "../orb/ingest"; import { handleAmsIngest } from "../ams/ingest"; import { handleOrbWebhook } from "../orb/webhook"; +import { backfillOrbInstallations } from "../orb/installations"; import { handleOrbOAuthCallback } from "../orb/oauth"; import { brokerOrbToken, @@ -4512,6 +4513,15 @@ export function createApp() { return c.json({ installationId, registered: registered === 1 }); }); + // Operator-triggered reconciliation of the installation registry against GitHub's authoritative install list — + // recovers installs whose `installation` webhook fired before the receiver's secret was configured (so they were + // never recorded). Upserts each install WITHOUT touching `registered`, so a re-run never re-trusts an opted-out + // install and new rows land at registered=0 (the manual-onboarding gate). Bearer-gated by the `/v1/internal/*` + // middleware (INTERNAL_JOB_TOKEN). Returns { backfilled } — the count of installs GitHub reported. + app.post("/v1/internal/orb/installations/backfill", async (c) => { + return c.json(await backfillOrbInstallations(c.env)); + }); + // Operator-only: issue a one-time token-broker enrollment secret for a REGISTERED install, to hand to that // maintainer's self-hosted container. The secret is returned ONCE (stored only hashed). Bearer-gated by the // /v1/internal/* middleware (INTERNAL_JOB_TOKEN); flag-gated (404 until ORB_BROKER_ENABLED). diff --git a/test/integration/orb-onboarding.test.ts b/test/integration/orb-onboarding.test.ts index 3a00cd7e85..5703a78274 100644 --- a/test/integration/orb-onboarding.test.ts +++ b/test/integration/orb-onboarding.test.ts @@ -1,7 +1,13 @@ -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { createApp } from "../../src/api/routes"; import { createTestEnv, type TestD1Database } from "../helpers/d1"; +async function pkcs8Pem(): Promise { + const key = (await crypto.subtle.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, true, ["sign", "verify"])) as CryptoKeyPair; + const b64 = Buffer.from((await crypto.subtle.exportKey("pkcs8", key.privateKey)) as ArrayBuffer).toString("base64").replace(/(.{64})/g, "$1\n"); + return ["-----BEGIN", "PRIVATE KEY-----"].join(" ") + `\n${b64}\n` + ["-----END", "PRIVATE KEY-----"].join(" "); +} + describe("Central Orb installation registry routes (/v1/internal/orb/installations)", () => { const app = createApp(); const auth = { authorization: "Bearer dev-internal-token" }; @@ -13,6 +19,8 @@ describe("Central Orb installation registry routes (/v1/internal/orb/installatio const register = (env: Env, body: unknown) => app.request("/v1/internal/orb/installations/register", { method: "POST", headers: auth, body: typeof body === "string" ? body : JSON.stringify(body) }, env); + afterEach(() => vi.unstubAllGlobals()); + it("lists recorded installations (registered surfaced as a boolean)", async () => { const env = createTestEnv(); await seed(env, 100); @@ -48,4 +56,29 @@ describe("Central Orb installation registry routes (/v1/internal/orb/installatio const res = await app.request("/v1/internal/orb/installations", { headers: auth }, env); expect(((await res.json()) as { installations: unknown[] }).installations).toEqual([]); }); + + it("backfills the registry from GitHub, recovering a webhook-missed installation", async () => { + const env = createTestEnv({ ORB_GITHUB_APP_ID: "4139483", ORB_GITHUB_APP_PRIVATE_KEY: await pkcs8Pem() }); + await seed(env, 200, 1); // already recorded + opted in + vi.stubGlobal("fetch", async () => + Response.json([ + { id: 200, account: { login: "acme", type: "Organization", id: 20 }, repository_selection: "all" }, + { id: 201, account: { login: "bob", type: "User", id: 21 }, repository_selection: "selected" }, // webhook fired pre-secret → never recorded + ]), + ); + const res = await app.request("/v1/internal/orb/installations/backfill", { method: "POST", headers: auth }, env); + expect(res.status).toBe(200); + expect(await res.json()).toEqual({ backfilled: 2 }); + const rows = await (env.DB as unknown as TestD1Database) + .prepare("SELECT installation_id, account_login, registered FROM orb_github_installations ORDER BY installation_id") + .all<{ installation_id: number; account_login: string; registered: number }>(); + expect(rows.results).toEqual([ + { installation_id: 200, account_login: "acme", registered: 1 }, // stayed trusted (backfill never re-trusts/untrusts) + { installation_id: 201, account_login: "bob", registered: 0 }, // recovered at the onboarding gate + ]); + }); + + it("401 without the internal token on the backfill route", async () => { + expect((await app.request("/v1/internal/orb/installations/backfill", { method: "POST" }, createTestEnv())).status).toBe(401); + }); });