From 04fbded1cfa061cbb1ff3f8f0041cfd82709007a Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 31 Oct 2026 11:42:34 -0700 Subject: [PATCH 1/2] fix(auth): check a workspace's personal-key switch after membership, so a non-member never learns what it named exists --- .../workspace-authorization.test.ts | 11 +++++------ .../application/workspace-authorization.ts | 19 +++++++++---------- .../table/application/authorization.test.ts | 7 ++++--- .../application/authorization.test.ts | 7 ++++--- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/sim/lib/core/application/workspace-authorization.test.ts b/apps/sim/lib/core/application/workspace-authorization.test.ts index 0a906477d2e..b5e8fef8035 100644 --- a/apps/sim/lib/core/application/workspace-authorization.test.ts +++ b/apps/sim/lib/core/application/workspace-authorization.test.ts @@ -488,11 +488,11 @@ describe('authorizeWorkspaceOperation personal API key policy', () => { }) /** - * The workspace column keeps its fail-fast: it is a property of the workspace - * rather than of any group, so it is not the organization-configuration - * oracle the group key would be, and three call-site suites pin the ordering. + * The workspace column follows membership too: the context is loaded before + * authorization, so refusing the column first would tell a non-member that the + * workspace, and the resource it named in it, exist. */ - it('keeps refusing the workspace personal-key column before the role lookup', async () => { + it('conceals the workspace before refusing the workspace personal-key column', async () => { mocks.resolvePermission.mockResolvedValue(null) await expect( @@ -500,8 +500,7 @@ describe('authorizeWorkspaceOperation personal API key policy', () => { ...context, allowPersonalApiKeys: false, }) - ).rejects.toBeInstanceOf(PersonalApiKeysDisabledError) - expect(mocks.resolvePermission).not.toHaveBeenCalled() + ).rejects.toBeInstanceOf(NoWorkspaceAccessError) }) }) diff --git a/apps/sim/lib/core/application/workspace-authorization.ts b/apps/sim/lib/core/application/workspace-authorization.ts index 67de9320502..baad11a7c49 100644 --- a/apps/sim/lib/core/application/workspace-authorization.ts +++ b/apps/sim/lib/core/application/workspace-authorization.ts @@ -380,33 +380,32 @@ export async function authorizeWorkspaceOperation workspaceAuthzMock) +import { PersonalApiKeysDisabledError } from '@/lib/core/application/workspace-authorization' import type { OrchestrationError } from '@/lib/core/orchestration/types' import { authorizeTableOperation } from '@/lib/table/application/authorization' import { tableOperations } from '@/lib/table/application/operations' @@ -65,14 +66,14 @@ describe('table operation authorization', () => { await expectForbidden(createSessionPrincipal()) }) - it('rejects disabled personal keys before permission lookup', async () => { + it("refuses a member's personal key when the workspace disables them", async () => { + resolvePermission.mockResolvedValue('admin') await expect( authorizeTableOperation(createPersonalApiKeyPrincipal(), tableOperations.updateRow, { ...authorizationContext, allowPersonalApiKeys: false, }) - ).rejects.toMatchObject>({ code: 'forbidden' }) - expect(resolvePermission).not.toHaveBeenCalled() + ).rejects.toBeInstanceOf(PersonalApiKeysDisabledError) }) it('allows workspace keys only in their credential workspace', async () => { diff --git a/apps/sim/lib/workspace-files/application/authorization.test.ts b/apps/sim/lib/workspace-files/application/authorization.test.ts index 6e23be54992..cfbb5c5fdf0 100644 --- a/apps/sim/lib/workspace-files/application/authorization.test.ts +++ b/apps/sim/lib/workspace-files/application/authorization.test.ts @@ -9,6 +9,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@sim/platform-authz/workspace', () => workspaceAuthzMock) +import { PersonalApiKeysDisabledError } from '@/lib/core/application/workspace-authorization' import type { OrchestrationError } from '@/lib/core/orchestration/types' import { authorizeWorkspaceFileAccess } from '@/lib/workspace-files/application/authorization' import { fileOperations } from '@/lib/workspace-files/application/operations' @@ -53,14 +54,14 @@ describe('file operation authorization', () => { }) }) - it('fails a personal key immediately when the workspace disables it', async () => { + it("refuses a member's personal key when the workspace disables them", async () => { + resolvePermission.mockResolvedValue('admin') await expect( authorizeWorkspaceFileAccess(createPersonalApiKeyPrincipal(), fileOperations.rename, { ...authorizationContext, allowPersonalApiKeys: false, }) - ).rejects.toMatchObject>({ code: 'forbidden' }) - expect(resolvePermission).not.toHaveBeenCalled() + ).rejects.toBeInstanceOf(PersonalApiKeysDisabledError) }) it('rejects a disallowed principal kind before principal-specific authorization', async () => { From 27acfa3a24728c77542f4a2afacbfd4d7fa8b3de Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Sat, 31 Oct 2026 11:59:16 -0700 Subject: [PATCH 2/2] test(auth): import the key-switch error from the application barrel --- apps/sim/lib/table/application/authorization.test.ts | 2 +- apps/sim/lib/workspace-files/application/authorization.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sim/lib/table/application/authorization.test.ts b/apps/sim/lib/table/application/authorization.test.ts index 81e96769b6a..494dbd96984 100644 --- a/apps/sim/lib/table/application/authorization.test.ts +++ b/apps/sim/lib/table/application/authorization.test.ts @@ -11,7 +11,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@sim/platform-authz/workspace', () => workspaceAuthzMock) -import { PersonalApiKeysDisabledError } from '@/lib/core/application/workspace-authorization' +import { PersonalApiKeysDisabledError } from '@/lib/core/application' import type { OrchestrationError } from '@/lib/core/orchestration/types' import { authorizeTableOperation } from '@/lib/table/application/authorization' import { tableOperations } from '@/lib/table/application/operations' diff --git a/apps/sim/lib/workspace-files/application/authorization.test.ts b/apps/sim/lib/workspace-files/application/authorization.test.ts index cfbb5c5fdf0..a16565bbde2 100644 --- a/apps/sim/lib/workspace-files/application/authorization.test.ts +++ b/apps/sim/lib/workspace-files/application/authorization.test.ts @@ -9,7 +9,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@sim/platform-authz/workspace', () => workspaceAuthzMock) -import { PersonalApiKeysDisabledError } from '@/lib/core/application/workspace-authorization' +import { PersonalApiKeysDisabledError } from '@/lib/core/application' import type { OrchestrationError } from '@/lib/core/orchestration/types' import { authorizeWorkspaceFileAccess } from '@/lib/workspace-files/application/authorization' import { fileOperations } from '@/lib/workspace-files/application/operations'