mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(mcp): correct fetchFn fallback order in mcpAuthGuarded (#5423)
Spread order previously let an explicit fetchFn (including fetchFn: undefined) in options silently disable the SSRF-guarded default. Fallback is now applied after the spread so the guard always wins unless a real override is passed. fix(tools): handle non-numeric Drive file size in early size check Guard the pre-download size check against a malformed metadata.size string so it's skipped explicitly instead of relying on an incidental NaN no-op; the streaming cap on the actual download still enforces the limit either way.
This commit is contained in:
@@ -133,6 +133,30 @@ describe('POST /api/tools/google_drive/download', () => {
|
||||
expect(data.success).toBe(false)
|
||||
})
|
||||
|
||||
it('proceeds to the streamed download when metadata size is malformed', async () => {
|
||||
mockSecureFetchWithPinnedIP
|
||||
.mockResolvedValueOnce(
|
||||
jsonResponse({
|
||||
id: 'file-abc',
|
||||
name: 'report.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
size: 'not-a-number',
|
||||
capabilities: { canReadRevisions: false },
|
||||
})
|
||||
)
|
||||
.mockResolvedValueOnce(fileResponse(1024))
|
||||
|
||||
const response = await POST(createMockRequest('POST', baseBody))
|
||||
expect(response.status).toBe(200)
|
||||
const data = (await response.json()) as { success: boolean; output: { file: { size: number } } }
|
||||
expect(data.success).toBe(true)
|
||||
expect(data.output.file.size).toBe(1024)
|
||||
|
||||
// The early size check should be skipped, but the streaming cap must still apply.
|
||||
const downloadCall = mockSecureFetchWithPinnedIP.mock.calls[1]
|
||||
expect(downloadCall[2]).toMatchObject({ maxResponseBytes: MAX_FILE_SIZE })
|
||||
})
|
||||
|
||||
it('does not require a metadata size for Google Workspace exports', async () => {
|
||||
mockSecureFetchWithPinnedIP
|
||||
.mockResolvedValueOnce(
|
||||
|
||||
@@ -188,11 +188,10 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
logger.info(`[${requestId}] Downloading regular file`, { fileId, mimeType: fileMimeType })
|
||||
|
||||
if (metadata.size) {
|
||||
assertKnownSizeWithinLimit(
|
||||
Number.parseInt(metadata.size, 10),
|
||||
MAX_FILE_SIZE,
|
||||
`Google Drive file ${fileId}`
|
||||
)
|
||||
const parsedSize = Number.parseInt(metadata.size, 10)
|
||||
if (Number.isFinite(parsedSize)) {
|
||||
assertKnownSizeWithinLimit(parsedSize, MAX_FILE_SIZE, `Google Drive file ${fileId}`)
|
||||
}
|
||||
}
|
||||
|
||||
const downloadUrl = `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media&supportsAllDrives=true`
|
||||
|
||||
@@ -51,4 +51,17 @@ describe('mcpAuthGuarded', () => {
|
||||
fetchFn: overrideFetch,
|
||||
})
|
||||
})
|
||||
|
||||
it('falls back to the SSRF-guarded fetch when fetchFn is explicitly undefined', async () => {
|
||||
await mcpAuthGuarded(provider, {
|
||||
serverUrl: 'https://mcp.example.com/mcp',
|
||||
fetchFn: undefined,
|
||||
})
|
||||
|
||||
expect(mockCreateSsrfGuardedMcpFetch).toHaveBeenCalledTimes(1)
|
||||
expect(mockAuth).toHaveBeenCalledWith(provider, {
|
||||
serverUrl: 'https://mcp.example.com/mcp',
|
||||
fetchFn: mockGuardedFetch,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -15,5 +15,5 @@ export function mcpAuthGuarded(
|
||||
provider: OAuthClientProvider,
|
||||
options: McpAuthOptions
|
||||
): ReturnType<typeof auth> {
|
||||
return auth(provider, { fetchFn: createSsrfGuardedMcpFetch(), ...options })
|
||||
return auth(provider, { ...options, fetchFn: options.fetchFn ?? createSsrfGuardedMcpFetch() })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user