mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-21 21:15:56 +08:00
fix(dataverse): strip the bearer token when a request redirects
The host allowlist added alongside the connector work only constrains the initial destination. `secureFetchWithPinnedIP` follows redirects and keeps the `Authorization` header unless a tool opts out, so a redirect away from an allowed Dataverse origin would forward the caller's OAuth token to whatever host answers. Dataverse redirects in normal operation — file downloads hand back a signed storage URL, and environment hosts move between regional origins — so this is reachable without a compromised environment URL. Sets `stripAuthOnRedirect` on all 18 Dataverse tools, matching the existing GitHub job-logs and Windchill precedent.
This commit is contained in:
@@ -80,6 +80,12 @@ export const dataverseAssociateTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}(${params.recordId.trim()})/${params.navigationProperty.trim()}/$ref`
|
||||
},
|
||||
method: (params) => (params.navigationType === 'single' ? 'PUT' : 'POST'),
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -62,6 +62,12 @@ export const dataverseCreateMultipleTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}/Microsoft.Dynamics.CRM.CreateMultiple`
|
||||
},
|
||||
method: 'POST',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -55,6 +55,12 @@ export const dataverseCreateRecordTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}`
|
||||
},
|
||||
method: 'POST',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -53,6 +53,12 @@ export const dataverseDeleteRecordTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}(${params.recordId.trim()})`
|
||||
},
|
||||
method: 'DELETE',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
|
||||
@@ -74,6 +74,12 @@ export const dataverseDisassociateTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${entitySetName}(${recordId})/${navigationProperty}/$ref`
|
||||
},
|
||||
method: 'DELETE',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
|
||||
@@ -60,6 +60,12 @@ export const dataverseDownloadFileTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}(${params.recordId.trim()})/${params.fileColumn.trim()}/$value`
|
||||
},
|
||||
method: 'GET',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
|
||||
@@ -78,6 +78,12 @@ export const dataverseExecuteActionTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${actionName}`
|
||||
},
|
||||
method: 'POST',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -83,6 +83,12 @@ export const dataverseExecuteFunctionTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${functionName}${paramStr}${querySuffix}`
|
||||
},
|
||||
method: 'GET',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
|
||||
@@ -57,6 +57,12 @@ export const dataverseFetchXmlQueryTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}?fetchXml=${encodedFetchXml}`
|
||||
},
|
||||
method: 'GET',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
|
||||
@@ -76,6 +76,12 @@ export const dataverseGetEntityMetadataTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/EntityDefinitions(LogicalName='${entityLogicalName}')${query}`
|
||||
},
|
||||
method: 'GET',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
|
||||
@@ -71,6 +71,12 @@ export const dataverseGetRecordTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}(${params.recordId.trim()})${query}`
|
||||
},
|
||||
method: 'GET',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
|
||||
@@ -93,6 +93,12 @@ export const dataverseListRecordsTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}${query}`
|
||||
},
|
||||
method: 'GET',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => {
|
||||
// Dataverse ignores $top entirely when Prefer: odata.maxpagesize is also sent, so the
|
||||
// page-size preference is only applied when the caller hasn't requested an explicit $top.
|
||||
|
||||
@@ -98,6 +98,12 @@ export const dataverseSearchTool: ToolConfig<DataverseSearchParams, DataverseSea
|
||||
return `${baseUrl}/api/data/v9.2/searchquery`
|
||||
},
|
||||
method: 'POST',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -62,6 +62,12 @@ export const dataverseUpdateMultipleTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}/Microsoft.Dynamics.CRM.UpdateMultiple`
|
||||
},
|
||||
method: 'POST',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -60,6 +60,12 @@ export const dataverseUpdateRecordTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}(${params.recordId.trim()})`
|
||||
},
|
||||
method: 'PATCH',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -70,6 +70,12 @@ export const dataverseUploadFileTool: ToolConfig<
|
||||
request: {
|
||||
url: '/api/tools/microsoft-dataverse/upload-file',
|
||||
method: 'POST',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
|
||||
@@ -61,6 +61,12 @@ export const dataverseUpsertRecordTool: ToolConfig<
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName.trim()}(${params.recordId.trim()})`
|
||||
},
|
||||
method: 'PATCH',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -39,6 +39,12 @@ export const dataverseWhoAmITool: ToolConfig<DataverseWhoAmIParams, DataverseWho
|
||||
return `${baseUrl}/api/data/v9.2/WhoAmI()`
|
||||
},
|
||||
method: 'GET',
|
||||
/**
|
||||
* Dataverse endpoints redirect (file downloads issue a signed storage URL,
|
||||
* and environment hosts redirect between regional origins), so drop the
|
||||
* bearer token rather than forward it to whatever origin answers.
|
||||
*/
|
||||
stripAuthOnRedirect: true,
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
|
||||
Reference in New Issue
Block a user