refactor: use fixed localhost-only CORS policy, make auth mandatory
This commit is contained in:
@@ -8,52 +8,51 @@ export function setupMiddleware(app: Express, settings: MCPServerSettings, creat
|
||||
// Parse JSON bodies
|
||||
app.use(express.json());
|
||||
|
||||
// CORS configuration
|
||||
if (settings.enableCORS) {
|
||||
const corsOptions = {
|
||||
origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
|
||||
// Allow requests with no origin (like mobile apps or curl requests)
|
||||
if (!origin) return callback(null, true);
|
||||
|
||||
if (settings.allowedOrigins.includes('*') ||
|
||||
settings.allowedOrigins.includes(origin)) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(new Error('Not allowed by CORS'));
|
||||
}
|
||||
},
|
||||
credentials: true
|
||||
};
|
||||
app.use(cors(corsOptions));
|
||||
}
|
||||
// CORS configuration - Always enabled with fixed localhost-only policy
|
||||
const corsOptions = {
|
||||
origin: (origin: string | undefined, callback: (err: Error | null, allow?: boolean) => void) => {
|
||||
// Allow requests with no origin (like CLI clients, curl, MCP SDKs)
|
||||
if (!origin) {
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
// Authentication middleware
|
||||
if (settings.enableAuth) {
|
||||
app.use((req: Request, res: Response, next: any) => {
|
||||
// Defensive check: if auth is enabled but no API key is set, reject all requests
|
||||
if (!settings.apiKey || settings.apiKey.trim() === '') {
|
||||
return res.status(500).json(createErrorResponse(null, ErrorCodes.InternalError, 'Server misconfigured: Authentication enabled but no API key set'));
|
||||
// Allow localhost and 127.0.0.1 on any port, both HTTP and HTTPS
|
||||
const localhostRegex = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/;
|
||||
if (localhostRegex.test(origin)) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(new Error('Not allowed by CORS'));
|
||||
}
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
const apiKey = authHeader?.replace('Bearer ', '');
|
||||
|
||||
if (apiKey !== settings.apiKey) {
|
||||
return res.status(401).json(createErrorResponse(null, ErrorCodes.InvalidRequest, 'Unauthorized'));
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
},
|
||||
credentials: true
|
||||
};
|
||||
app.use(cors(corsOptions));
|
||||
|
||||
// Authentication middleware - Always enabled
|
||||
app.use((req: Request, res: Response, next: any) => {
|
||||
// Defensive check: if no API key is set, reject all requests
|
||||
if (!settings.apiKey || settings.apiKey.trim() === '') {
|
||||
return res.status(500).json(createErrorResponse(null, ErrorCodes.InternalError, 'Server misconfigured: No API key set'));
|
||||
}
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
const providedKey = authHeader?.replace('Bearer ', '');
|
||||
|
||||
if (providedKey !== settings.apiKey) {
|
||||
return res.status(401).json(createErrorResponse(null, ErrorCodes.InvalidRequest, 'Unauthorized'));
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// Origin validation for security (DNS rebinding protection)
|
||||
app.use((req: Request, res: Response, next: any) => {
|
||||
const host = req.headers.host;
|
||||
|
||||
|
||||
// Only allow localhost connections
|
||||
if (host && !host.startsWith('localhost') && !host.startsWith('127.0.0.1')) {
|
||||
return res.status(403).json(createErrorResponse(null, ErrorCodes.InvalidRequest, 'Only localhost connections allowed'));
|
||||
}
|
||||
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,38 +57,6 @@ export class MCPServerSettingTab extends PluginSettingTab {
|
||||
}
|
||||
}));
|
||||
|
||||
// CORS setting
|
||||
new Setting(containerEl)
|
||||
.setName('Enable CORS')
|
||||
.setDesc('Enable Cross-Origin Resource Sharing (requires restart)')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableCORS)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableCORS = value;
|
||||
await this.plugin.saveSettings();
|
||||
if (this.plugin.mcpServer?.isRunning()) {
|
||||
new Notice('⚠️ Server restart required for CORS changes to take effect');
|
||||
}
|
||||
}));
|
||||
|
||||
// Allowed origins
|
||||
new Setting(containerEl)
|
||||
.setName('Allowed origins')
|
||||
.setDesc('Comma-separated list of allowed origins (* for all, requires restart)')
|
||||
.addText(text => text
|
||||
.setPlaceholder('*')
|
||||
.setValue(this.plugin.settings.allowedOrigins.join(', '))
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.allowedOrigins = value
|
||||
.split(',')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s.length > 0);
|
||||
await this.plugin.saveSettings();
|
||||
if (this.plugin.mcpServer?.isRunning()) {
|
||||
new Notice('⚠️ Server restart required for origin changes to take effect');
|
||||
}
|
||||
}));
|
||||
|
||||
// Authentication
|
||||
new Setting(containerEl)
|
||||
.setName('Enable authentication')
|
||||
|
||||
Reference in New Issue
Block a user