/** * MCP integration test for security features. * Tests that the actual tools properly reject dangerous inputs. */ import { queryToolHandler } from '../../src/tools/query.js'; import { parseSpec } from '../../src/lib/parser.js'; async function runTests() { console.log('\n=== MCP Integration Security Tests ===\n'); let passed = 0; let failed = 0; // Test 1: ReDoS protection in query-endpoints console.log('Test 1: query-endpoints rejects ReDoS pattern...'); try { const result = await queryToolHandler({ path: 'test/fixtures/petstore.yaml', pathPattern: '(a+)+$', }); const content = result.content[0]; if (content.type === 'text' && content.text.includes('nested quantifiers')) { console.log(' ✅ ReDoS pattern rejected'); passed++; } else { console.log(' ❌ ReDoS pattern NOT rejected'); console.log(' Response:', JSON.stringify(result).substring(0, 200)); failed++; } } catch (err) { console.log(' ❌ Unexpected error:', (err as Error).message); failed++; } // Test 2: Safe regex works console.log('Test 2: query-endpoints accepts safe regex...'); try { const result = await queryToolHandler({ path: 'test/fixtures/petstore.yaml', pathPattern: '/pets.*', }); const structured = result.structuredContent as { success: boolean }; if (structured.success) { console.log(' ✅ Safe regex accepted'); passed++; } else { console.log(' ❌ Safe regex rejected unexpectedly'); failed++; } } catch (err) { console.log(' ❌ Unexpected error:', (err as Error).message); failed++; } // Test 3: Path traversal protection console.log('Test 3: parseSpec rejects path traversal...'); try { await parseSpec('../../../etc/passwd'); console.log(' ❌ Path traversal NOT blocked'); failed++; } catch (err) { if ((err as Error).message.includes('traversal')) { console.log(' ✅ Path traversal blocked'); passed++; } else { console.log(' ❌ Wrong error:', (err as Error).message); failed++; } } // Test 4: SSRF protection - localhost console.log('Test 4: parseSpec rejects localhost URL...'); try { await parseSpec('http://localhost/spec.json'); console.log(' ❌ Localhost URL NOT blocked'); failed++; } catch (err) { if ((err as Error).message.includes('blocked')) { console.log(' ✅ Localhost URL blocked'); passed++; } else { console.log(' ❌ Wrong error:', (err as Error).message); failed++; } } // Test 5: SSRF protection - private IP console.log('Test 5: parseSpec rejects private IP...'); try { await parseSpec('http://192.168.1.1/spec.json'); console.log(' ❌ Private IP NOT blocked'); failed++; } catch (err) { if ((err as Error).message.includes('Private')) { console.log(' ✅ Private IP blocked'); passed++; } else { console.log(' ❌ Wrong error:', (err as Error).message); failed++; } } // Test 6: SSRF protection - file protocol console.log('Test 6: parseSpec rejects file:// protocol...'); try { await parseSpec('file:///etc/passwd'); console.log(' ❌ File protocol NOT blocked'); failed++; } catch (err) { if ((err as Error).message.includes('Protocol')) { console.log(' ✅ File protocol blocked'); passed++; } else { console.log(' ❌ Wrong error:', (err as Error).message); failed++; } } // Test 7: Valid local file works console.log('Test 7: parseSpec accepts valid local file...'); try { const result = await parseSpec('test/fixtures/petstore.yaml'); if (result.metadata.title) { console.log(' ✅ Valid local file parsed'); passed++; } else { console.log(' ❌ Parse succeeded but no title'); failed++; } } catch (err) { console.log(' ❌ Unexpected error:', (err as Error).message); failed++; } // Test 8: Private IPs allowed with option console.log('Test 8: parseSpec allows private IP with allowPrivateIPs option...'); try { await parseSpec('http://192.168.1.1/spec.json', { security: { allowPrivateIPs: true } }); // This will fail to connect, but that's expected - the security check should pass console.log(' ❌ Should have thrown network error'); failed++; } catch (err) { const msg = (err as Error).message; if (msg.includes('Private') || msg.includes('blocked')) { console.log(' ❌ Private IP still blocked despite option'); failed++; } else { // Network error is expected (no server there) console.log(' ✅ Security check passed (network error expected)'); passed++; } } // Summary console.log('\n=== Summary ==='); console.log(`Passed: ${passed}/${passed + failed}`); console.log(`Failed: ${failed}/${passed + failed}`); if (failed > 0) { process.exit(1); } console.log('\n✅ All MCP integration security tests passed!\n'); } runTests().catch(console.error);