Skip to content

Commit 20c5a90

Browse files
authored
Fixes cross org connection registry (#2083)
* Fixes cross org connection registry Signed-off-by: Marcos Candeia <marrcooos@gmail.com> * Add include tools Signed-off-by: Marcos Candeia <marrcooos@gmail.com> --------- Signed-off-by: Marcos Candeia <marrcooos@gmail.com>
1 parent b7b512a commit 20c5a90

6 files changed

Lines changed: 25 additions & 12 deletions

File tree

apps/mesh/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@decocms/mesh",
3-
"version": "1.0.0-alpha.22",
3+
"version": "1.0.0-alpha.23",
44
"description": "MCP Mesh - Self-hostable MCP Gateway for managing AI connections and tools",
55
"license": "MIT",
66
"author": "Deco team",

apps/mesh/src/storage/connection.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export class ConnectionStorage implements ConnectionStoragePort {
6363
const existing = await this.findById(id);
6464

6565
if (existing) {
66+
// Only allow update if same organization - prevent cross-org hijacking
67+
if (existing.organization_id !== data.organization_id) {
68+
throw new Error("Connection ID already exists");
69+
}
6670
return this.update(id, data);
6771
}
6872

apps/mesh/src/tools/connection/get.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
createCollectionGetOutputSchema,
1010
} from "@decocms/bindings/collections";
1111
import { defineTool } from "../../core/define-tool";
12+
import { requireOrganization } from "../../core/mesh-context";
1213
import { ConnectionEntitySchema } from "./schema";
1314

1415
/**
@@ -26,13 +27,17 @@ export const COLLECTION_CONNECTIONS_GET = defineTool({
2627
outputSchema: ConnectionGetOutputSchema,
2728

2829
handler: async (input, ctx) => {
30+
// Require organization context
31+
const organization = requireOrganization(ctx);
32+
2933
// Check authorization
3034
await ctx.access.check();
3135

3236
// Get connection
3337
const connection = await ctx.storage.connections.findById(input.id);
3438

35-
if (!connection) {
39+
// Verify connection exists and belongs to the current organization
40+
if (!connection || connection.organization_id !== organization.id) {
3641
return { item: null };
3742
}
3843

apps/mesh/src/tools/connection/test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { z } from "zod";
88
import { defineTool } from "../../core/define-tool";
9+
import { requireOrganization } from "../../core/mesh-context";
910

1011
export const CONNECTION_TEST = defineTool({
1112
name: "CONNECTION_TEST",
@@ -22,9 +23,18 @@ export const CONNECTION_TEST = defineTool({
2223
}),
2324

2425
handler: async (input, ctx) => {
26+
// Require organization context
27+
const organization = requireOrganization(ctx);
28+
2529
// Check authorization
2630
await ctx.access.check();
2731

32+
// Fetch connection to verify org ownership before testing
33+
const connection = await ctx.storage.connections.findById(input.id);
34+
if (!connection || connection.organization_id !== organization.id) {
35+
throw new Error("Connection not found");
36+
}
37+
2838
// Test connection
2939
const result = await ctx.storage.connections.testConnection(input.id);
3040

apps/mesh/src/tools/connection/update.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,14 @@ async function validateConfiguration(
6969
if (refConnectionId === WellKnownMCPId.SELF) {
7070
continue;
7171
}
72-
// Verify connection exists
72+
// Verify connection exists and belongs to same organization
73+
// Use consistent error message to prevent cross-org information disclosure
7374
const refConnection =
7475
await ctx.storage.connections.findById(refConnectionId);
75-
if (!refConnection) {
76+
if (!refConnection || refConnection.organization_id !== organizationId) {
7677
throw new Error(`Referenced connection not found: ${refConnectionId}`);
7778
}
7879

79-
// Verify connection belongs to same organization
80-
if (refConnection.organization_id !== organizationId) {
81-
throw new Error(
82-
`Referenced connection ${refConnectionId} does not belong to organization ${organizationId}`,
83-
);
84-
}
85-
8680
// Verify user has access to the referenced connection
8781
try {
8882
await ctx.access.check(refConnectionId);

apps/mesh/src/web/hooks/use-binding-schema-from-registry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export function useBindingSchemaFromRegistry(
121121

122122
// Build query input params using proper WhereExpression format
123123
const toolInputParams = parsedAppName
124-
? { where: { appName: parsedAppName } }
124+
? { where: { appName: parsedAppName }, includeTools: true }
125125
: {};
126126

127127
// Create queries for all registries in parallel

0 commit comments

Comments
 (0)