diff --git a/frontend/src/lib/components/copilot/chat/global/sessionAccess.test.ts b/frontend/src/lib/components/copilot/chat/global/sessionAccess.test.ts index 0be7ee4f40..527d337ad9 100644 --- a/frontend/src/lib/components/copilot/chat/global/sessionAccess.test.ts +++ b/frontend/src/lib/components/copilot/chat/global/sessionAccess.test.ts @@ -83,4 +83,10 @@ describe('resolveSessionAccess', () => { expect(access.capabilities.has('write_draft')).toBe(true) expect(access.capabilities.has('deploy')).toBe(true) }) + + it('fails open on a body that resolves without a role, rather than throwing', async () => { + whoami.mockResolvedValueOnce(undefined) + const access = await resolveSessionAccess('ws') + expect(access.capabilities.has('write_draft')).toBe(true) + }) }) diff --git a/frontend/src/lib/components/copilot/chat/global/sessionAccess.ts b/frontend/src/lib/components/copilot/chat/global/sessionAccess.ts index 1e01c68e14..8fed511f2d 100644 --- a/frontend/src/lib/components/copilot/chat/global/sessionAccess.ts +++ b/frontend/src/lib/components/copilot/chat/global/sessionAccess.ts @@ -42,10 +42,14 @@ export function hasCapabilities( } export async function resolveSessionAccess(workspace: string): Promise { - let me: User + let me: User | undefined try { me = await UserService.whoami({ workspace }) - } catch { + } catch {} + // Checked rather than trusted: a body that arrives malformed resolves without + // throwing, and reading a capability off it would surface as a TypeError thrown + // out of the send rather than as the fail-open this whole path promises. + if (!me) { return fullSessionAccess(workspace) }