Skip to content

Commit d60fdd7

Browse files
committed
fix(files): await delivery observers and track private CLI downloads
1 parent 65b23a2 commit d60fdd7

7 files changed

Lines changed: 78 additions & 5 deletions

File tree

‎apps/sim/app/api/files/serve/[...path]/route.test.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,10 @@ describe('File Serve API Route', () => {
338338
},
339339
})
340340

341-
async function serveVersionedDoc(dependsOnReferencedFiles: boolean) {
341+
async function serveVersionedDoc(
342+
dependsOnReferencedFiles: boolean,
343+
method: 'GET' | 'HEAD' = 'GET'
344+
) {
342345
mockResolveStoredFileContext.mockResolvedValue('workspace')
343346
mockParseWorkspaceFileKey.mockReturnValue('test-workspace-id')
344347
mockAuthenticateWorkspaceFile.mockResolvedValue(principal)
@@ -349,6 +352,7 @@ describe('File Serve API Route', () => {
349352
})
350353

351354
const req = createMockRequest({
355+
method,
352356
url: 'http://localhost:3000/api/files/serve/workspace/test-workspace-id/report.pdf?v=1756684800000',
353357
})
354358
const response = await GET(
@@ -359,6 +363,15 @@ describe('File Serve API Route', () => {
359363
return response
360364
}
361365

366+
it('authorizes HEAD without compiling a representation or inventing its length', async () => {
367+
const response = await serveVersionedDoc(true, 'HEAD')
368+
expect(await response.text()).toBe('')
369+
expect(response.headers.get('Content-Length')).toBeNull()
370+
expect(response.headers.get('Cache-Control')).toBe('private, no-store')
371+
expect(mockReadWorkspaceFileRecordByKey).toHaveBeenCalled()
372+
expect(fileUtilsServerMockFns.mockDownloadServableFileFromStorage).not.toHaveBeenCalled()
373+
})
374+
362375
it('caches a versioned document immutably when its bytes derive from the stored source alone', async () => {
363376
const response = await serveVersionedDoc(false)
364377
expect(response.headers.get('Cache-Control')).toBe('private, max-age=31536000, immutable')

‎apps/sim/lib/mothership/agent-cli/file-read-transport.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export function createFileReadTransport(context: {
105105
workspaceId: parsed.data.query.workspaceId,
106106
actorUserId: context.invocation.userId,
107107
unknown: 'taint',
108+
destination: 'workbench',
108109
}),
109110
() =>
110111
downloadWorkspaceFileStream.execute({

‎apps/sim/lib/mothership/content-delivery/registry-gate.test.ts‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,23 @@ describe('createRegistryContentGate', () => {
135135
expect(await gate.admit([item('config', exact(SECRET))])).toEqual([false])
136136
})
137137

138+
it('tracks opaque workbench downloads without relaxing model or excerpt admission', async () => {
139+
const registry = newRegistry()
140+
const gate = createRegistryContentGate({
141+
registry,
142+
actorUserId: 'user-1',
143+
destination: 'workbench',
144+
})
145+
expect(await gate.admit([item('download', exact(SECRET), 'opaque')])).toEqual([true])
146+
expect(modelView(SECRET, registry)).not.toContain(SECRET)
147+
expect(await gate.admit([item('snippet', exact(SECRET), 'excerpt')])).toEqual([false])
148+
expect(
149+
await createRegistryContentGate({ actorUserId: 'user-1', destination: 'workbench' }).admit([
150+
item('download', exact(SECRET), 'opaque'),
151+
])
152+
).toEqual([false])
153+
})
154+
138155
it('refuses an unknown item without latching the registry', async () => {
139156
const registry = newRegistry()
140157
const gate = createRegistryContentGate({ registry, actorUserId: 'user-1' })

‎apps/sim/lib/mothership/content-delivery/registry-gate.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export interface RegistryContentGateScope {
2323
* anything persisted from it stays unknown. Only a direct single-file download takes it (D7).
2424
*/
2525
unknown?: 'refuse' | 'taint'
26+
/** Private workbench downloads persist imported provenance; opaque bytes never go directly to the model. */
27+
destination?: 'model' | 'workbench'
2628
}
2729

2830
/**
@@ -56,7 +58,11 @@ export function createRegistryContentGate(scope: RegistryContentGateScope): Cont
5658
return true
5759
}
5860
if (provenance.entries.length === 0) return true
59-
if (source.view === 'opaque' || source.view === 'excerpt') return refuse(source, 'opaque')
61+
if (
62+
source.view === 'excerpt' ||
63+
(source.view === 'opaque' && scope.destination !== 'workbench')
64+
)
65+
return refuse(source, 'opaque')
6066
const registry = scope.registry
6167
if (!registry) return refuse(source, 'registry')
6268
/** A derived view that shows nothing needs no entry; `undefined` would mean the whole item. */

‎apps/sim/lib/projects/files/__integration__/content-rendered.integration.ts‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import * as storageCleanup from '@/lib/uploads/contexts/workspace/file-storage-c
4848
import * as storage from '@/lib/uploads/core/storage-service'
4949
import { storeCompiledDoc } from '@/lib/uploads/documents/compiled-store'
5050
import * as heic from '@/lib/uploads/server/heic'
51+
import { observeWorkspaceFileDelivery } from '@/lib/workspace-files/application/file-delivery-observer'
5152
import * as searchDelivery from '@/lib/workspace-files/search/delivery'
5253
import * as searchRepository from '@/lib/workspace-files/search/repository'
5354

@@ -233,6 +234,41 @@ describe('Project delivery admission', () => {
233234
})
234235
}
235236
}
237+
for (const kind of ['inline', 'csv', 'artifact'] as const) {
238+
check(`${kind} awaits an asynchronous transport observer refusal`, async () => {
239+
const f = await fixture()
240+
const created = await createProjectFile.execute({
241+
principal: f.principal,
242+
input: {
243+
...createInput(f.projectId, 'name,value\nrow,secret'),
244+
name: 'rows.csv',
245+
contentType: 'text/csv',
246+
},
247+
})
248+
const error = new Error('Transport observer refused delivery')
249+
const input = { projectId: f.projectId, fileId: created.file.id, key: created.file.key }
250+
await expect(
251+
observeWorkspaceFileDelivery(
252+
async () => {
253+
throw error
254+
},
255+
() =>
256+
kind === 'inline'
257+
? readProjectInlineFile.execute({
258+
principal: f.principal,
259+
input: { projectId: f.projectId, referenceFileId: created.file.id },
260+
})
261+
: kind === 'csv'
262+
? readProjectFileCsvPreview.execute({ principal: f.principal, input })
263+
: readProjectFileArtifact.execute({
264+
principal: f.principal,
265+
input: { ...input, maxBytes: 1024 },
266+
})
267+
)
268+
).rejects.toBe(error)
269+
})
270+
}
271+
236272
check('Project search withholds stale previews and removes moved-out results', async () => {
237273
const f = await fixture()
238274
const created = await createProjectFile.execute({

‎apps/sim/lib/projects/files/application/artifacts.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,6 @@ export const readProjectFileArtifact = defineAuthorizedProjectFileUseCase<
356356
provenance: result.secretProvenance,
357357
view: 'opaque',
358358
})
359-
reportWorkspaceFileDelivery(result.secretProvenance, result.file.id)
359+
await reportWorkspaceFileDelivery(result.secretProvenance, result.file.id)
360360
},
361361
})

‎apps/sim/lib/projects/files/application/previews.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ const readInlineFile = defineAuthorizedProjectFileUseCase({
123123
provenance: result.secretProvenance ?? { status: 'unknown' },
124124
view: 'opaque',
125125
})
126-
reportWorkspaceFileDelivery(result.secretProvenance, result.file.id)
126+
await reportWorkspaceFileDelivery(result.secretProvenance, result.file.id)
127127
},
128128
})
129129

@@ -184,6 +184,6 @@ export const readProjectFileCsvPreview = defineAuthorizedProjectFileUseCase({
184184
provenance: result.secretProvenance ?? { status: 'unknown' },
185185
view: 'excerpt',
186186
})
187-
reportWorkspaceFileDelivery(result.secretProvenance, input.fileId)
187+
await reportWorkspaceFileDelivery(result.secretProvenance, input.fileId)
188188
},
189189
})

0 commit comments

Comments
 (0)