Skip to content

Commit 509ce37

Browse files
committed
test(tools): cover the stripAuthOnRedirect plumbing end to end
Asserting the flag on the tool config alone would not catch a regression in formatRequestParams or in the executor's call into secureFetchWithPinnedIP, so pin what the fetch layer actually receives, in both the opted-in and the default case.
1 parent 0899406 commit 509ce37

1 file changed

Lines changed: 63 additions & 0 deletions

File tree

apps/sim/tools/index.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,69 @@ describe('Automatic Internal Route Detection', () => {
988988
Object.assign(tools, originalTools)
989989
})
990990

991+
it("should forward a tool's stripAuthOnRedirect to secureFetchWithPinnedIP", async () => {
992+
// Tools whose endpoint redirects to a signed third-party URL (GitHub's
993+
// Actions log download) must not have their API credential replayed to the
994+
// redirect target. This fetch path follows redirects itself rather than
995+
// through the fetch spec, so nothing strips the header without the flag.
996+
const mockTool = {
997+
id: 'test_redirecting_download',
998+
name: 'Test Redirecting Download Tool',
999+
description: 'A test tool whose endpoint redirects to another origin',
1000+
version: '1.0.0',
1001+
params: {},
1002+
request: {
1003+
url: 'https://api.example.com/download',
1004+
method: 'GET',
1005+
headers: () => ({ Authorization: 'Bearer secret-token' }),
1006+
stripAuthOnRedirect: true,
1007+
},
1008+
transformResponse: vi.fn().mockResolvedValue({ success: true, output: {} }),
1009+
}
1010+
1011+
const originalTools = { ...tools }
1012+
;(tools as any).test_redirecting_download = mockTool
1013+
1014+
await executeTool('test_redirecting_download', {})
1015+
1016+
expect(mockSecureFetchWithPinnedIP).toHaveBeenCalledWith(
1017+
'https://api.example.com/download',
1018+
'93.184.216.34',
1019+
expect.objectContaining({ stripAuthOnRedirect: true })
1020+
)
1021+
1022+
Object.assign(tools, originalTools)
1023+
})
1024+
1025+
it('should leave stripAuthOnRedirect unset for tools that do not opt in', async () => {
1026+
const mockTool = {
1027+
id: 'test_plain_external',
1028+
name: 'Test Plain External Tool',
1029+
description: 'A test tool with no redirect handling',
1030+
version: '1.0.0',
1031+
params: {},
1032+
request: {
1033+
url: 'https://api.example.com/plain',
1034+
method: 'GET',
1035+
headers: () => ({ Authorization: 'Bearer secret-token' }),
1036+
},
1037+
transformResponse: vi.fn().mockResolvedValue({ success: true, output: {} }),
1038+
}
1039+
1040+
const originalTools = { ...tools }
1041+
;(tools as any).test_plain_external = mockTool
1042+
1043+
await executeTool('test_plain_external', {})
1044+
1045+
expect(mockSecureFetchWithPinnedIP).toHaveBeenCalledWith(
1046+
'https://api.example.com/plain',
1047+
'93.184.216.34',
1048+
expect.objectContaining({ stripAuthOnRedirect: undefined })
1049+
)
1050+
1051+
Object.assign(tools, originalTools)
1052+
})
1053+
9911054
it('should throw when the proxyUrl param fails validation', async () => {
9921055
inputValidationMockFns.mockValidateAndPinProxyUrl.mockResolvedValue({
9931056
isValid: false,

0 commit comments

Comments
 (0)