Skip to content

Commit 9531045

Browse files
committed
test(agent): preserve rejection of unprefixed attachment keys
1 parent fd01018 commit 9531045

1 file changed

Lines changed: 97 additions & 0 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
import type { ExecutionContext, UserFile } from '@/executor/types'
6+
7+
const { presign, download, metadata, permission } = vi.hoisted(() => ({
8+
presign: vi.fn(),
9+
download: vi.fn(),
10+
metadata: vi.fn(),
11+
permission: vi.fn(),
12+
}))
13+
14+
vi.mock('@/lib/uploads', () => ({
15+
StorageService: { hasCloudStorage: () => true, generatePresignedDownloadUrl: presign },
16+
getFileMetadata: metadata,
17+
}))
18+
19+
vi.mock('@/lib/uploads/utils/file-utils.server', () => ({
20+
downloadServableFileFromStorage: download,
21+
}))
22+
23+
vi.mock('@/lib/uploads/server/metadata', () => ({
24+
getFileMetadataByKey: metadata,
25+
}))
26+
27+
vi.mock('@/lib/workspaces/permissions/utils', () => ({
28+
getUserEntityPermissions: permission,
29+
}))
30+
31+
import { resolveTrustedFileContext } from '@/lib/uploads/utils/file-utils'
32+
import {
33+
attachLargeFileRemoteUrls,
34+
uploadLargeFilesToProvider,
35+
} from '@/providers/file-attachments.server'
36+
import type { ProviderRequest } from '@/providers/types'
37+
38+
/** Authorization and key inference are real: mocking either hid this pre-existing refusal. */
39+
describe('provider attachment storage-key authorization', () => {
40+
beforeEach(() => {
41+
vi.clearAllMocks()
42+
})
43+
44+
it.each(
45+
(['workspace', 'execution', 'chat', 'copilot', 'knowledge-base'] as const).flatMap((context) =>
46+
(['standalone', 'session', 'system'] as const).map((caller) => ({ context, caller }))
47+
)
48+
)(
49+
'rejects unprefixed $context keys for $caller before reading or signing bytes',
50+
async ({ context, caller }) => {
51+
const file: UserFile = {
52+
id: 'file-1',
53+
name: 'document.pdf',
54+
key: 'legacy-file-id/document.pdf',
55+
url: '',
56+
size: 10 * 1024 * 1024,
57+
type: 'application/pdf',
58+
context,
59+
}
60+
const request: ProviderRequest = {
61+
model: 'gpt-4.1',
62+
userId: 'billing-owner',
63+
messages: [{ role: 'user', content: 'Read this file', files: [file] }],
64+
}
65+
const executionContext =
66+
caller === 'standalone'
67+
? undefined
68+
: ({
69+
workflowId: 'workflow-1',
70+
workspaceId: 'workspace-1',
71+
executionId: 'execution-1',
72+
userId: 'billing-owner',
73+
principal:
74+
caller === 'session'
75+
? { kind: 'session', userId: 'acting-user', sessionId: 'session-1' }
76+
: {
77+
kind: 'system',
78+
serviceId: 'chat',
79+
workspaceId: 'workspace-1',
80+
workflowId: 'workflow-1',
81+
},
82+
} as ExecutionContext)
83+
84+
expect(resolveTrustedFileContext(file.key, file.context)).toBe(context)
85+
await expect(attachLargeFileRemoteUrls(request, 'openai', executionContext)).rejects.toThrow()
86+
expect(presign).not.toHaveBeenCalled()
87+
88+
file.remoteUrl = 'https://storage.example.com/forged'
89+
await expect(
90+
uploadLargeFilesToProvider(request, 'openai', executionContext)
91+
).rejects.toThrow()
92+
expect(download).not.toHaveBeenCalled()
93+
expect(metadata).not.toHaveBeenCalled()
94+
expect(permission).not.toHaveBeenCalled()
95+
}
96+
)
97+
})

0 commit comments

Comments
 (0)