Pin that no provider error carries the provider's body

F1's second half, tested where it is decided: the Discord token exchange
(a 2xx body that is not JSON, and a 401), the Discord guild lookup and the
YouTube channel lookup each fail with a ProviderResponseError whose code
and status name the failure, and no printed form of the error (message,
stack, JSON) holds a byte of the canary body. Until now only the callers'
fixed answers were pinned, so a body in the message went unseen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-30 17:16:39 +02:00
commit 804f41f5f7

View file

@ -1655,3 +1655,67 @@ describe("a backend handoff the app refuses", () => {
expect(JSON.stringify(failure) + String(failure)).not.toContain("CANARY");
});
});
describe("a provider answer Heimdall cannot use", () => {
const runtimes = createOAuthRuntimeRegistry();
const notJson = '{"access_token":"CANARYaccess","refresh_token":CANARYrefresh}';
const refusal = '{"error":"invalid_grant","echo":"CANARYrefused"}';
/** What the call threw, as every form a catcher might record or print. */
async function failureOf(call: () => Promise<unknown>) {
const error = await call().then(
() => undefined,
(caught: unknown) => caught
);
expect(error).toBeInstanceOf(Error);
return {
error: error as Error & { code?: unknown; status?: unknown },
printed: `${String(error)} ${(error as Error).stack ?? ""} ${JSON.stringify(error)}`,
};
}
it.each([
["a 2xx body that is not JSON", () => new Response(notJson, { status: 200 }), "provider_body_not_json", 200],
["a refusal", () => new Response(refusal, { status: 401 }), "provider_status", 401],
])("fails the Discord token exchange on %s by status and code alone", async (_name, answer, code, status) => {
globalThis.fetch = async () => answer();
const { error, printed } = await failureOf(() =>
runtimes.discord.exchangeAuthorizationCode({ config: createTestConfig(), code: "c", redirectUri: "https://heimdall.gamecult.org/cb" })
);
expect(error.name).toBe("ProviderResponseError");
expect(error.code).toBe(code);
expect(error.status).toBe(status);
expect(printed).not.toContain("CANARY");
});
it("fails the Discord guild lookup on a 2xx body that is not JSON by status and code alone", async () => {
globalThis.fetch = async () => new Response(notJson, { status: 200 });
const { error, printed } = await failureOf(() =>
runtimes.discord.evaluateEntitlements({
config: createTestConfig(),
callback: {
appSlug: "ghostlight",
accountId: "account-1",
connection: null,
entitlementPolicy: { kind: "discord_role_access", guildId: "g", allowedRoleIds: ["r"] },
},
identity: { provider: "discord", providerUserId: "u1", profile: {} },
tokenSet: { accessToken: "a", tokenType: "Bearer", scope: [], raw: {} },
})
);
expect(error.code).toBe("provider_body_not_json");
expect(printed).not.toContain("CANARY");
});
it("fails the YouTube channel lookup on a 2xx body that is not JSON by status and code alone", async () => {
globalThis.fetch = async () => new Response(notJson, { status: 200 });
const { error, printed } = await failureOf(() =>
runtimes.youtube.resolveIdentity({ config: createTestConfig(), accessToken: "a" })
);
expect(error.code).toBe("provider_body_not_json");
expect(printed).not.toContain("CANARY");
});
});