Previously assertSafeProxyTarget resolved the target hostname and validated the IPs, but safeProxyFetch then passed the raw URL to node-fetch, which performed its own DNS lookup. In the window between the two resolutions an attacker who controls the domain's TTL could flip the record to 169.254.169.254 (cloud-metadata), causing fetch() to connect to the instance- metadata service even though the block-list check passed. Fix: assertSafeProxyTarget now returns the validated IP addresses. On each hop of safeProxyFetch we call createPinnedAgent() to build an http/https.Agent whose lookup hook unconditionally returns the pre-validated IP. node-fetch uses that hook instead of the OS resolver, closing the TOCTOU window entirely. Refactoring: isBlockedProxyAddress, assertSafeProxyTarget, and the new createPinnedAgent are extracted to server/src/proxy-security.ts so they can be unit-tested in isolation. A vitest suite is added to the server package (the first unit tests for this package) with 26 cases covering the block-list, DNS validation, IP-pinning, and the TOCTOU guarantee itself.
52 lines
1.4 KiB
JSON
52 lines
1.4 KiB
JSON
{
|
|
"name": "@modelcontextprotocol/inspector-server",
|
|
"version": "1.0.0",
|
|
"description": "Server-side application for the Model Context Protocol inspector",
|
|
"license": "SEE LICENSE IN LICENSE",
|
|
"author": "Model Context Protocol a Series of LF Projects, LLC.",
|
|
"homepage": "https://modelcontextprotocol.io",
|
|
"bugs": "https://github.com/modelcontextprotocol/inspector/issues",
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "https://github.com/modelcontextprotocol/inspector",
|
|
"directory": "server"
|
|
},
|
|
"type": "module",
|
|
"bin": {
|
|
"mcp-inspector-server": "build/index.js"
|
|
},
|
|
"files": [
|
|
"build",
|
|
"static",
|
|
"LICENSE"
|
|
],
|
|
"scripts": {
|
|
"build": "tsc && shx cp -R static build",
|
|
"start": "node build/index.js",
|
|
"dev": "tsx watch --clear-screen=false src/index.ts",
|
|
"dev:windows": "tsx watch --clear-screen=false src/index.ts < NUL",
|
|
"test": "vitest run",
|
|
"test:watch": "vitest"
|
|
},
|
|
"devDependencies": {
|
|
"@types/cors": "^2.8.19",
|
|
"@types/express": "^5.0.0",
|
|
"@types/node": "^22.0.0",
|
|
"@types/shell-quote": "^1.7.5",
|
|
"@types/ws": "^8.5.12",
|
|
"tsx": "^4.19.0",
|
|
"typescript": "^5.6.2",
|
|
"vitest": "^4.1.0"
|
|
},
|
|
"dependencies": {
|
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
"cors": "^2.8.5",
|
|
"express": "^5.1.0",
|
|
"shell-quote": "^1.8.4",
|
|
"shx": "^0.3.4",
|
|
"spawn-rx": "^5.1.2",
|
|
"ws": "^8.18.0",
|
|
"zod": "^3.25.76",
|
|
"express-rate-limit": "^8.2.1"
|
|
}
|
|
}
|