You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(selfhost): record installation app_id and filter foreign-app webhooks
Self-host migration prereq (blocker 2a). When the cloud App and a self-host App
are installed on the same account during the parallel-run phase, a backend
should only act on ITS OWN App's installations.
- Add a nullable installations.app_id column (Drizzle + migration 0071),
captured in upsertInstallation from installation events / the App-installation
API refresh; a payload without it never clears the stored value.
- upsertInstallation returns the resolved app_id so the webhook entry can filter
without a second read.
- New pure isForeignAppInstallation(ownAppId, installationAppId): true ONLY on a
positive numeric mismatch with GITHUB_APP_ID; fail-open on any unknown.
- Wire it at the webhook entry: a foreign-app delivery is acked
(webhook_events 'foreign_app') without processing.
Defense-in-depth: the per-App webhook secret (GITHUB_WEBHOOK_SECRET) is the
PRIMARY isolation; this is the belt-and-suspenders for a shared-endpoint/secret
misconfig. FAIL-OPEN — an unknown/own-matching app_id always processes, so the
live single-app path is byte-identical until the column is populated.
// The delivery was acked as foreign, and the PR was never upserted (the handler returned before the PR block).
7545
+
constevt=awaitenv.DB.prepare("select payload_hash from webhook_events where delivery_id = ?").bind("foreign-app-pr").first<{payload_hash: string}>();
7546
+
expect(evt?.payload_hash).toBe("foreign_app");
7547
+
constpr=awaitenv.DB.prepare("select count(*) as n from pull_requests where repo_full_name = ? and number = ?").bind("JSONbored/gittensory",88).first<{n: number}>();
7548
+
expect(pr?.n).toBe(0);
7549
+
});
7550
+
7551
+
it("processes a webhook whose installation app_id matches this backend (no false filtering)",async()=>{
7552
+
constenv=createTestEnv();// own GITHUB_APP_ID "3824093"
// The matching-app webhook was processed normally — the PR row exists and it was NOT acked as foreign.
7570
+
constpr=awaitenv.DB.prepare("select count(*) as n from pull_requests where repo_full_name = ? and number = ?").bind("JSONbored/gittensory",89).first<{n: number}>();
7571
+
expect(pr?.n).toBe(1);
7572
+
constevt=awaitenv.DB.prepare("select payload_hash from webhook_events where delivery_id = ?").bind("own-app-pr").first<{payload_hash: string}>();
0 commit comments