mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
feat: enhance security measures for MCP server interactions
- Restricted CORS to localhost origins to prevent remote code execution (RCE) attacks. - Updated MCP server configuration handling to enforce security warnings when adding or importing servers. - Introduced a SecurityWarningDialog to inform users about potential risks associated with server commands and configurations. - Ensured that only serverId is accepted for testing server connections, preventing arbitrary command execution. These changes improve the overall security posture of the MCP server management and usage.
This commit is contained in:
@@ -105,9 +105,13 @@ if (ENABLE_REQUEST_LOGGING) {
|
||||
})
|
||||
);
|
||||
}
|
||||
// SECURITY: Restrict CORS to localhost UI origins to prevent drive-by attacks
|
||||
// from malicious websites. MCP server endpoints can execute arbitrary commands,
|
||||
// so allowing any origin would enable RCE from any website visited while Automaker runs.
|
||||
const DEFAULT_CORS_ORIGINS = ['http://localhost:3007', 'http://127.0.0.1:3007'];
|
||||
app.use(
|
||||
cors({
|
||||
origin: process.env.CORS_ORIGIN || '*',
|
||||
origin: process.env.CORS_ORIGIN || DEFAULT_CORS_ORIGINS,
|
||||
credentials: true,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -158,8 +158,8 @@ interface McpPermissionOptions {
|
||||
*/
|
||||
function buildMcpOptions(config: CreateSdkOptionsConfig): McpPermissionOptions {
|
||||
const hasMcpServers = config.mcpServers && Object.keys(config.mcpServers).length > 0;
|
||||
// Default to true - this is a deliberate design choice for ease of use with MCP servers.
|
||||
// Users can disable these in settings for stricter security.
|
||||
// Default to true for autonomous workflow. Security is enforced when adding servers
|
||||
// via the security warning dialog that explains the risks.
|
||||
const mcpAutoApprove = config.mcpAutoApproveTools ?? true;
|
||||
const mcpUnrestricted = config.mcpUnrestrictedTools ?? true;
|
||||
|
||||
|
||||
@@ -193,7 +193,8 @@ export async function getMCPPermissionSettings(
|
||||
settingsService?: SettingsService | null,
|
||||
logPrefix = '[SettingsHelper]'
|
||||
): Promise<{ mcpAutoApproveTools: boolean; mcpUnrestrictedTools: boolean }> {
|
||||
// Default values (both enabled for backwards compatibility)
|
||||
// Default to true for autonomous workflow. Security is enforced when adding servers
|
||||
// via the security warning dialog that explains the risks.
|
||||
const defaults = { mcpAutoApproveTools: true, mcpUnrestrictedTools: true };
|
||||
|
||||
if (!settingsService) {
|
||||
|
||||
@@ -40,7 +40,8 @@ export class ClaudeProvider extends BaseProvider {
|
||||
// This logic mirrors buildMcpOptions() in sdk-options.ts but is applied here since
|
||||
// the provider is the final point where SDK options are constructed.
|
||||
const hasMcpServers = options.mcpServers && Object.keys(options.mcpServers).length > 0;
|
||||
// Default to true - deliberate design choice for ease of use. Users can disable in settings.
|
||||
// Default to true for autonomous workflow. Security is enforced when adding servers
|
||||
// via the security warning dialog that explains the risks.
|
||||
const mcpAutoApprove = options.mcpAutoApproveTools ?? true;
|
||||
const mcpUnrestricted = options.mcpUnrestrictedTools ?? true;
|
||||
const defaultTools = ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch'];
|
||||
|
||||
@@ -4,21 +4,22 @@
|
||||
* Lists available tools for an MCP server.
|
||||
* Similar to test but focused on tool discovery.
|
||||
*
|
||||
* SECURITY: Only accepts serverId to look up saved configs. Does NOT accept
|
||||
* arbitrary serverConfig to prevent drive-by command execution attacks.
|
||||
* Users must explicitly save a server config through the UI before testing.
|
||||
*
|
||||
* Request body:
|
||||
* { serverId: string } - Get tools by server ID from settings
|
||||
* OR { serverConfig: MCPServerConfig } - Get tools with provided config
|
||||
*
|
||||
* Response: { success: boolean, tools?: MCPToolInfo[], error?: string }
|
||||
*/
|
||||
|
||||
import type { Request, Response } from 'express';
|
||||
import type { MCPTestService } from '../../../services/mcp-test-service.js';
|
||||
import type { MCPServerConfig } from '@automaker/types';
|
||||
import { getErrorMessage, logError } from '../common.js';
|
||||
|
||||
interface ListToolsRequest {
|
||||
serverId?: string;
|
||||
serverConfig?: MCPServerConfig;
|
||||
serverId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,18 +30,15 @@ export function createListToolsHandler(mcpTestService: MCPTestService) {
|
||||
try {
|
||||
const body = req.body as ListToolsRequest;
|
||||
|
||||
if (!body.serverId && !body.serverConfig) {
|
||||
if (!body.serverId || typeof body.serverId !== 'string') {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Either serverId or serverConfig is required',
|
||||
error: 'serverId is required',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point, we know at least one of serverId or serverConfig is truthy
|
||||
const result = body.serverId
|
||||
? await mcpTestService.testServerById(body.serverId)
|
||||
: await mcpTestService.testServer(body.serverConfig!);
|
||||
const result = await mcpTestService.testServerById(body.serverId);
|
||||
|
||||
// Return only tool-related information
|
||||
res.json({
|
||||
|
||||
@@ -2,23 +2,23 @@
|
||||
* POST /api/mcp/test - Test MCP server connection and list tools
|
||||
*
|
||||
* Tests connection to an MCP server and returns available tools.
|
||||
* Accepts either a serverId to look up config, or a full server config.
|
||||
*
|
||||
* SECURITY: Only accepts serverId to look up saved configs. Does NOT accept
|
||||
* arbitrary serverConfig to prevent drive-by command execution attacks.
|
||||
* Users must explicitly save a server config through the UI before testing.
|
||||
*
|
||||
* Request body:
|
||||
* { serverId: string } - Test server by ID from settings
|
||||
* OR { serverConfig: MCPServerConfig } - Test with provided config
|
||||
*
|
||||
* Response: { success: boolean, tools?: MCPToolInfo[], error?: string, connectionTime?: number }
|
||||
*/
|
||||
|
||||
import type { Request, Response } from 'express';
|
||||
import type { MCPTestService } from '../../../services/mcp-test-service.js';
|
||||
import type { MCPServerConfig } from '@automaker/types';
|
||||
import { getErrorMessage, logError } from '../common.js';
|
||||
|
||||
interface TestServerRequest {
|
||||
serverId?: string;
|
||||
serverConfig?: MCPServerConfig;
|
||||
serverId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,19 +29,15 @@ export function createTestServerHandler(mcpTestService: MCPTestService) {
|
||||
try {
|
||||
const body = req.body as TestServerRequest;
|
||||
|
||||
if (!body.serverId && !body.serverConfig) {
|
||||
if (!body.serverId || typeof body.serverId !== 'string') {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Either serverId or serverConfig is required',
|
||||
error: 'serverId is required',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point, we know at least one of serverId or serverConfig is truthy
|
||||
const result = body.serverId
|
||||
? await mcpTestService.testServerById(body.serverId)
|
||||
: await mcpTestService.testServer(body.serverConfig!);
|
||||
|
||||
const result = await mcpTestService.testServerById(body.serverId);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
logError(error, 'Test server failed');
|
||||
|
||||
Reference in New Issue
Block a user