Delete hard-coded Repixelizer callback trust (deletion_line 2)
acceptsBackendCallback is now a pure per-app env allow-list lookup; Repixelizer's callback must be configured via GC_ACCESS_APP_REPIXELIZER_BACKEND_CALLBACK_URLS like every other app (R8: operator sets this on the running legacy body before the new build starts). Test config adds the allow-list entry so existing Repixelizer flows keep passing, and a new negative test pins that the slug name alone buys nothing.
This commit is contained in:
parent
a999d48b6d
commit
c6d0ecb68b
2 changed files with 30 additions and 9 deletions
10
src/app.ts
10
src/app.ts
|
|
@ -508,15 +508,7 @@ function acceptsBackendCallback(config: HeimdallConfig, appSlug: AppSlug, handof
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((config.appBackendCallbacks[appSlug] ?? []).includes(handoff.callbackUrl)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (appSlug === "repixelizer") {
|
||||
return handoff.callbackUrl === "https://repixelizer.gamecult.org/api/auth/heimdall/callback";
|
||||
}
|
||||
|
||||
return false;
|
||||
return (config.appBackendCallbacks[appSlug] ?? []).includes(handoff.callbackUrl);
|
||||
}
|
||||
|
||||
async function maybeDeliverBackendHandoff(handoff: OAuthHandoff, payload: BackendHandoffPayload): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ function createTestConfig(): HeimdallConfig {
|
|||
},
|
||||
appBackendCallbacks: {
|
||||
bifrost: ["https://bifrost.gamecult.org/auth/heimdall/callback"],
|
||||
repixelizer: ["https://repixelizer.gamecult.org/api/auth/heimdall/callback"],
|
||||
},
|
||||
storage: {
|
||||
backend: "memory",
|
||||
|
|
@ -898,6 +899,34 @@ describe("Heimdall service", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("no longer trusts Repixelizer's callback URL by slug name alone", async () => {
|
||||
const baseConfig = createTestConfig();
|
||||
const config = {
|
||||
...baseConfig,
|
||||
appBackendCallbacks: {},
|
||||
};
|
||||
const app = await buildApp({ config });
|
||||
apps.push(app);
|
||||
|
||||
const response = await app.inject({
|
||||
method: "POST",
|
||||
url: "/v1/oauth/discord/start",
|
||||
payload: {
|
||||
appSlug: "repixelizer",
|
||||
mode: "sign_in",
|
||||
returnTo: "https://repixelizer.gamecult.org/app/",
|
||||
handoff: {
|
||||
kind: "backend_callback",
|
||||
attemptId: "attempt-123",
|
||||
callbackUrl: "https://repixelizer.gamecult.org/api/auth/heimdall/callback",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
expect(response.json()).toEqual(expect.objectContaining({ error: "untrusted_backend_callback" }));
|
||||
});
|
||||
|
||||
it("resolves app-managed provider credentials without exposing refresh custody", async () => {
|
||||
const store = new InMemoryStore();
|
||||
const app = await buildApp({
|
||||
|
|
|
|||
Loading…
Reference in a new issue