@@ -3,14 +3,10 @@ import { createLogger } from '@sim/logger'
33import { getErrorMessage } from '@sim/utils/errors'
44import { sleep } from '@sim/utils/helpers'
55import OpenAI , { toFile } from 'openai'
6- import {
7- secureFetchWithPinnedIP ,
8- validateUrlWithDNS ,
9- } from '@/lib/core/security/input-validation.server'
10- import { readResponseToBufferWithLimit } from '@/lib/core/utils/stream-limits'
116import type { StorageContext } from '@/lib/uploads'
127import { StorageService } from '@/lib/uploads'
138import { inferContextFromKey } from '@/lib/uploads/utils/file-utils'
9+ import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
1410import { verifyFileAccess } from '@/app/api/files/authorization'
1511import type { UserFile } from '@/executor/types'
1612import {
@@ -148,36 +144,12 @@ function groupUploadableFiles(messages: Message[] | undefined): UserFile[][] {
148144}
149145
150146/**
151- * Downloads the file from its signed URL with DNS validation and IP pinning so a URL that
152- * somehow resolves to an internal address can never be fetched ( SSRF defense for every
153- * caller, not just the agent path) . Bounded by the provider's attachment ceiling.
147+ * Reads the file bytes straight from storage via the storage SDK (not by HTTP-fetching the
148+ * signed URL), so there is no server-side URL fetch to be an SSRF vector and internal
149+ * object storage works . Bounded by the provider's attachment ceiling.
154150 */
155- async function fetchRemoteFileBlob (
156- file : UserFile ,
157- maxBytes : number ,
158- signal ?: AbortSignal
159- ) : Promise < Blob > {
160- const url = file . remoteUrl as string
161- const validation = await validateUrlWithDNS ( url , 'fileUrl' )
162- if ( ! validation . isValid || ! validation . resolvedIP ) {
163- throw new Error (
164- `Cannot download "${ file . name } " for upload: ${ validation . error || 'invalid URL' } `
165- )
166- }
167-
168- const response = await secureFetchWithPinnedIP ( url , validation . resolvedIP , {
169- maxResponseBytes : maxBytes ,
170- signal,
171- } )
172- if ( ! response . ok ) {
173- throw new Error ( `Failed to download "${ file . name } " for upload (status ${ response . status } )` )
174- }
175-
176- const buffer = await readResponseToBufferWithLimit ( response , {
177- maxBytes,
178- label : 'provider file upload' ,
179- signal,
180- } )
151+ async function downloadFileForUpload ( file : UserFile , maxBytes : number ) : Promise < Blob > {
152+ const buffer = await downloadFileFromStorage ( file , 'provider-file-upload' , logger , { maxBytes } )
181153 return new Blob ( [ buffer ] , { type : file . type || inferAttachmentMimeType ( file ) } )
182154}
183155
@@ -188,7 +160,7 @@ async function uploadOpenAIFile(
188160 signal ?: AbortSignal
189161) : Promise < void > {
190162 const mimeType = inferAttachmentMimeType ( file )
191- const blob = await fetchRemoteFileBlob ( file , maxBytes , signal )
163+ const blob = await downloadFileForUpload ( file , maxBytes )
192164
193165 const uploaded = await client . files . create (
194166 {
@@ -210,7 +182,7 @@ async function uploadGeminiFile(
210182 signal ?: AbortSignal
211183) : Promise < void > {
212184 const mimeType = inferAttachmentMimeType ( file )
213- const blob = await fetchRemoteFileBlob ( file , maxBytes , signal )
185+ const blob = await downloadFileForUpload ( file , maxBytes )
214186
215187 let uploaded = await ai . files . upload ( { file : blob , config : { mimeType, abortSignal : signal } } )
216188
0 commit comments