Skip to content

Commit 8eb364c

Browse files
committed
fix(deps): patch sharp librsvg CVE and MCP SDK OAuth issuer binding
- sharp 0.35.4 -> 0.35.5 (librsvg 2.63.2, CVE-2026-96889) - @modelcontextprotocol/sdk 1.29.0 -> 1.31.0 (GHSA-6qxp-vccf-f47h) - bind preregistered MCP OAuth client secrets to an issuer - skip token revocation when the advertised AS differs from the token issuer
1 parent 9fb9a64 commit 8eb364c

8 files changed

Lines changed: 73 additions & 36 deletions

File tree

‎apps/sim/lib/credential-groups/managed-mcp-connectors.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ interface DatabricksManagedMcpConnector extends ManagedMcpConnectorMetadata {
3131
interface FixedPreregisteredManagedMcpConnector extends ManagedMcpConnectorMetadata {
3232
id: 'hubspot' | 'zoom'
3333
url: string
34+
/** The only authorization server the deployment client secret is ever presented to. */
35+
authorizationServer: string
3436
oauthClientRegistration: 'preregistered'
3537
}
3638

@@ -45,6 +47,7 @@ export const MANAGED_MCP_CONNECTORS = {
4547
name: 'Zoom',
4648
description: 'Search past meetings, transcripts and notes using your Zoom account',
4749
url: 'https://mcp.zoom.us/mcp/meeting/streamable',
50+
authorizationServer: 'https://zoom.us',
4851
oauthClientRegistration: 'preregistered',
4952
},
5053
lucid: {
@@ -60,6 +63,7 @@ export const MANAGED_MCP_CONNECTORS = {
6063
name: 'HubSpot',
6164
description: 'Search CRM records using each person’s HubSpot permissions',
6265
url: 'https://mcp.hubspot.com',
66+
authorizationServer: 'https://mcp.hubspot.com',
6367
oauthClientRegistration: 'preregistered',
6468
},
6569
coda: {

‎apps/sim/lib/mcp/oauth/managed-provider.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export class ManagedMcpOauthProvider implements OAuthClientProvider {
7878
token_endpoint_auth_method:
7979
this.preregistered.tokenEndpointAuthMethod ??
8080
(this.preregistered.clientSecret ? 'client_secret_post' : 'none'),
81+
issuer: this.preregistered.issuer ?? this.currentTokens?.issuer,
8182
}
8283
}
8384

‎apps/sim/lib/mcp/oauth/provider.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ export interface PreregisteredClient {
4343
configurationFingerprint?: string
4444
scope?: string
4545
tokenEndpointAuthMethod?: 'client_secret_basic' | 'client_secret_post'
46+
/**
47+
* Authorization server the credentials are registered with. When unset, they are bound to
48+
* the issuer of the current grant, so the SDK never presents the secret to another server.
49+
*/
50+
issuer?: string
4651
}
4752

4853
interface SimMcpOauthProviderInit {
@@ -107,6 +112,7 @@ export class SimMcpOauthProvider implements OAuthClientProvider {
107112
token_endpoint_auth_method:
108113
this.preregistered.tokenEndpointAuthMethod ??
109114
(this.preregistered.clientSecret ? 'client_secret_post' : 'none'),
115+
issuer: this.preregistered.issuer ?? this.row.tokens?.issuer,
110116
}
111117
}
112118
return undefined
@@ -226,6 +232,8 @@ export async function loadPreregisteredClient(
226232
if (!row.clientId || !row.clientSecret)
227233
throw new Error('HubSpot OAuth registration is incomplete')
228234
}
235+
const issuer =
236+
row.connectorId === 'hubspot' ? MANAGED_MCP_CONNECTORS.hubspot.authorizationServer : undefined
229237
if (!row.clientId) return undefined
230238
let clientSecret: string | undefined
231239
if (row.clientSecret) {
@@ -240,5 +248,5 @@ export async function loadPreregisteredClient(
240248
throw new Error('Failed to decrypt preregistered MCP OAuth client secret')
241249
}
242250
}
243-
return { clientId: row.clientId, clientSecret }
251+
return { clientId: row.clientId, clientSecret, issuer }
244252
}

‎apps/sim/lib/mcp/oauth/revoke.ts‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,14 @@ export async function revokeMcpOauthTokens(
4141
const info = await discoverOAuthServerInfo(server.url, { fetchFn: ssrfGuardedFetch }).catch(
4242
() => undefined
4343
)
44-
const metadata = info?.authorizationServerMetadata as
44+
if (
45+
!info ||
46+
(row.tokens.issuer &&
47+
!isSameAuthorizationServer(row.tokens.issuer, info.authorizationServerUrl))
48+
) {
49+
return
50+
}
51+
const metadata = info.authorizationServerMetadata as
4552
| (Record<string, unknown> & { revocation_endpoint?: string })
4653
| undefined
4754
const revocationEndpoint = metadata?.revocation_endpoint
@@ -79,6 +86,21 @@ export async function revokeMcpOauthTokens(
7986
}
8087
}
8188

89+
/**
90+
* Mirrors the SDK's issuer binding: tokens stamped for one authorization server are never
91+
* posted, with the client secret, to a different one the MCP server now advertises.
92+
*/
93+
function isSameAuthorizationServer(issuer: string, authorizationServerUrl: string): boolean {
94+
const normalize = (value: string) => {
95+
try {
96+
return new URL(value).href.replace(/\/$/, '')
97+
} catch {
98+
return value.replace(/\/$/, '')
99+
}
100+
}
101+
return normalize(issuer) === normalize(authorizationServerUrl)
102+
}
103+
82104
async function postRevoke(
83105
endpoint: string,
84106
token: string,

‎apps/sim/lib/mcp/oauth/shared-clients.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export function getSharedHubSpotMcpClient() {
1515
return {
1616
clientId,
1717
clientSecret,
18+
issuer: MANAGED_MCP_CONNECTORS.hubspot.authorizationServer,
1819
configurationFingerprint: sha256Hex(
1920
JSON.stringify([
2021
'shared-hubspot-mcp',
@@ -40,6 +41,7 @@ export function getSharedZoomMcpClient() {
4041
clientSecret,
4142
scope,
4243
tokenEndpointAuthMethod,
44+
issuer: MANAGED_MCP_CONNECTORS.zoom.authorizationServer,
4345
configurationFingerprint: sha256Hex(
4446
JSON.stringify([
4547
'shared-zoom-mcp',

‎apps/sim/package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"@hookform/resolvers": "5.2.2",
8989
"@linear/sdk": "91.0.0",
9090
"@marsidev/react-turnstile": "1.4.2",
91-
"@modelcontextprotocol/sdk": "1.29.0",
91+
"@modelcontextprotocol/sdk": "1.31.0",
9292
"@monaco-editor/react": "4.7.0",
9393
"@napi-rs/canvas": "0.1.100",
9494
"@opentelemetry/api": "^1.9.0",
@@ -247,7 +247,7 @@
247247
"remark-stringify": "11.0.0",
248248
"resend": "^4.1.2",
249249
"rss-parser": "3.13.0",
250-
"sharp": "0.35.4",
250+
"sharp": "0.35.5",
251251
"sim": "workspace:*",
252252
"socket.io-client": "4.8.1",
253253
"ssh2": "^1.17.0",

0 commit comments

Comments
 (0)