Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/tools/mongodb/update/updateMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export class UpdateManyTool extends MongoDBToolBase {
const provider = await this.ensureConnected();

this.assertMqlIsAllowed(filter);
// The update document is also MQL and, in its aggregation-pipeline form, can carry
// server-side JS operators ($function/$where/$accumulator) and write stages ($out/$merge).
// It must go through the same guard as the filter so `disableServerSideJs` / readOnly /
// disabled-tools restrictions can't be bypassed via the update argument.
this.assertMqlIsAllowed(update);

// Check if update operation uses an index if enabled
if (this.config.indexCheck) {
Expand Down
29 changes: 29 additions & 0 deletions tests/integration/tools/mongodb/update/updateMany.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,33 @@ describeWithMongoDB("updateMany tool with server-side JavaScript operators", (in
}
});
}

// The `update` document is MQL too, so it must go through the same guard as the `filter`
// (the regression this protects against). Unlike a filter, a classic update document does not
// execute the operator server-side, so the enabled case only asserts the guard lets it through.
for (const jsDisabled of [true, false]) {
it(`${jsDisabled ? "rejects" : "allows"} an update document carrying $function when disableServerSideJs is ${jsDisabled}`, async () => {
integration.mcpServer().userConfig.disableServerSideJs = jsDisabled;
await integration.connectMcpClient();
const response = await integration.mcpClient().callTool({
name: "update-many",
arguments: {
database: integration.randomDbName(),
collection: "people",
filter: {},
update: {
$set: {
flagged: { $function: { body: "function () { return true; }", args: [], lang: "js" } },
},
},
},
});
const content = getResponseContent(response.content);
if (jsDisabled) {
expect(content).toContain(`The "$function" operator is not allowed.`);
} else {
expect(content).not.toContain("server-side JavaScript operators");
}
});
}
});
Loading