Skip to content

Commit 61c92a4

Browse files
fix(workflows): hide route workspace existence
1 parent fda5640 commit 61c92a4

File tree

2 files changed

+63
-4
lines changed

2 files changed

+63
-4
lines changed

apps/sim/app/api/workflows/[id]/route.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,36 @@ describe('Workflow By ID API Route', () => {
144144
expect(data.error).toBe('Workflow not found')
145145
})
146146

147+
it('should return 404 for workspace api key targeting a workflow in another workspace', async () => {
148+
const mockWorkflow = {
149+
id: 'workflow-123',
150+
userId: 'other-user',
151+
name: 'Foreign Workflow',
152+
workspaceId: 'workspace-b',
153+
}
154+
155+
mockCheckHybridAuth.mockResolvedValue({
156+
success: true,
157+
userId: 'api-user',
158+
authType: 'api_key',
159+
apiKeyType: 'workspace',
160+
workspaceId: 'workspace-a',
161+
})
162+
mockGetWorkflowById.mockResolvedValue(mockWorkflow)
163+
164+
const req = new NextRequest('http://localhost:3000/api/workflows/workflow-123', {
165+
headers: { 'x-api-key': 'test-key' },
166+
})
167+
const params = Promise.resolve({ id: 'workflow-123' })
168+
169+
const response = await GET(req, { params })
170+
171+
expect(response.status).toBe(404)
172+
const data = await response.json()
173+
expect(data.error).toBe('Workflow not found')
174+
expect(mockAuthorizeWorkflowByWorkspacePermission).not.toHaveBeenCalled()
175+
})
176+
147177
it('should allow access when user has admin workspace permission', async () => {
148178
const mockWorkflow = {
149179
id: 'workflow-123',
@@ -220,6 +250,38 @@ describe('Workflow By ID API Route', () => {
220250
expect(data.data.id).toBe('workflow-123')
221251
})
222252

253+
it('should keep session access semantics unchanged for readable workflows', async () => {
254+
const mockWorkflow = {
255+
id: 'workflow-123',
256+
userId: 'other-user',
257+
name: 'Test Workflow',
258+
workspaceId: 'workspace-456',
259+
}
260+
261+
mockGetSession({ user: { id: 'user-123' } })
262+
mockGetWorkflowById.mockResolvedValue(mockWorkflow)
263+
mockAuthorizeWorkflowByWorkspacePermission.mockResolvedValue({
264+
allowed: true,
265+
status: 200,
266+
workflow: mockWorkflow,
267+
workspacePermission: 'read',
268+
})
269+
270+
const req = new NextRequest('http://localhost:3000/api/workflows/workflow-123')
271+
const params = Promise.resolve({ id: 'workflow-123' })
272+
273+
const response = await GET(req, { params })
274+
275+
expect(response.status).toBe(200)
276+
const data = await response.json()
277+
expect(data.data.id).toBe('workflow-123')
278+
expect(mockAuthorizeWorkflowByWorkspacePermission).toHaveBeenCalledWith({
279+
workflowId: 'workflow-123',
280+
userId: 'user-123',
281+
action: 'read',
282+
})
283+
})
284+
223285
it('should deny access when user has no workspace permissions', async () => {
224286
const mockWorkflow = {
225287
id: 'workflow-123',

apps/sim/app/api/workflows/[id]/route.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
5151
}
5252

5353
if (auth.apiKeyType === 'workspace' && auth.workspaceId !== workflowData.workspaceId) {
54-
return NextResponse.json(
55-
{ error: 'API key is not authorized for this workspace' },
56-
{ status: 403 }
57-
)
54+
return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
5855
}
5956

6057
if (isInternalCall && !userId) {

0 commit comments

Comments
 (0)