- Add intelligent URL detection supporting BASE_URL, PUBLIC_URL, and proxy headers - Fix hardcoded localhost URLs in server console output - Add hostname validation to prevent host header injection attacks - Restrict URL schemes to http/https only (block javascript:, file://, etc.) - Remove sensitive environment data from API responses - Add GET endpoints (/, /mcp) for better API discovery - Fix version inconsistency between server implementations - Update HTTP bridge to use HOST/PORT environment variables - Add comprehensive test scripts for URL configuration and security This resolves issues #41 and #42 by making the HTTP server properly handle deployment behind reverse proxies and adds critical security validations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
96
scripts/test-security.ts
Normal file
96
scripts/test-security.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env node
|
||||
import axios from 'axios';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
async function testMaliciousHeaders() {
|
||||
console.log('🔒 Testing Security Fixes...\n');
|
||||
|
||||
// Start server with TRUST_PROXY enabled
|
||||
const serverProcess = spawn('node', ['dist/mcp/index.js'], {
|
||||
env: {
|
||||
...process.env,
|
||||
MCP_MODE: 'http',
|
||||
AUTH_TOKEN: 'test-security-token-32-characters-long',
|
||||
PORT: '3999',
|
||||
TRUST_PROXY: '1'
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for server to start
|
||||
await new Promise(resolve => {
|
||||
serverProcess.stdout.on('data', (data) => {
|
||||
if (data.toString().includes('Press Ctrl+C to stop')) {
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: 'Valid proxy headers',
|
||||
headers: {
|
||||
'X-Forwarded-Host': 'example.com',
|
||||
'X-Forwarded-Proto': 'https'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Malicious host header (with path)',
|
||||
headers: {
|
||||
'X-Forwarded-Host': 'evil.com/path/to/evil',
|
||||
'X-Forwarded-Proto': 'https'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Malicious host header (with @)',
|
||||
headers: {
|
||||
'X-Forwarded-Host': 'user@evil.com',
|
||||
'X-Forwarded-Proto': 'https'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Invalid hostname (multiple dots)',
|
||||
headers: {
|
||||
'X-Forwarded-Host': '.....',
|
||||
'X-Forwarded-Proto': 'https'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'IPv6 address',
|
||||
headers: {
|
||||
'X-Forwarded-Host': '[::1]:3000',
|
||||
'X-Forwarded-Proto': 'https'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
try {
|
||||
const response = await axios.get('http://localhost:3999/', {
|
||||
headers: testCase.headers,
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
const endpoints = response.data.endpoints;
|
||||
const healthUrl = endpoints?.health?.url || 'N/A';
|
||||
|
||||
console.log(`✅ ${testCase.name}`);
|
||||
console.log(` Response: ${healthUrl}`);
|
||||
|
||||
// Check if malicious headers were blocked
|
||||
if (testCase.name.includes('Malicious') || testCase.name.includes('Invalid')) {
|
||||
if (healthUrl.includes('evil.com') || healthUrl.includes('@') || healthUrl.includes('.....')) {
|
||||
console.log(' ❌ SECURITY ISSUE: Malicious header was not blocked!');
|
||||
} else {
|
||||
console.log(' ✅ Malicious header was blocked');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ ${testCase.name} - Request failed`);
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
serverProcess.kill();
|
||||
}
|
||||
|
||||
testMaliciousHeaders().catch(console.error);
|
||||
Reference in New Issue
Block a user