mbouclas/mcp-test-server
criticalFirst attempt at an mcp server
MCP server (purpose undetermined)
265server.tool(
266 "query_custom_service",
267 "Query your custom service with parameters",
268 {
269 endpoint: z.string().describe("The API endpoint to call"),
270 method: z.enum(["GET", "POST", "PUT", "DELETE"]).default("GET").describe("HTTP method to use"),
271 data: z.object({}).optional().describe("Data to send with the request (for POST/PUT)"),
272 headers: z.record(z.string()).optional().describe("Additional headers to include"),
273 },
274 async ({ endpoint, method, data, headers }) => {
275 const baseUrl = process.env.SERVICE_BASE_URL || "http://localhost:3000";
276 const url = `${baseUrl}${endpoint}`;// Exploitable if MCP is exposed to untrusted prompts (network_exposed).
The 'query_custom_service' tool accepts an arbitrary 'endpoint' string from the user and concatenates it with a base URL to form a request URL. There is no validation or sanitization of the endpoint, allowing an attacker to control the full URL path and potentially redirect requests to internal or external hosts, enabling Server-Side Request Forgery (SSRF).
ImpactAn attacker could make the server send HTTP requests to arbitrary internal or external hosts, potentially accessing internal services (e.g., cloud metadata endpoints, internal APIs) or performing port scanning.
FixValidate the 'endpoint' parameter against a whitelist of allowed paths. Use a URL parser to ensure the endpoint does not contain protocol or host parts. Alternatively, restrict the base URL and only allow predefined endpoints.
313server.tool(
314 "execute_query",
315 "Execute a database query through your service",
316 {
317 query: z.string().describe("SQL query to execute"),
318 parameters: z.array(z.any()).optional().describe("Query parameters"),
319 },
320 async ({ query, parameters }) => {
321 const baseUrl = process.env.SERVICE_BASE_URL || "http://localhost:3000";
322 const url = `${baseUrl}/api/query`;// Exploitable if MCP is exposed to untrusted prompts (network_exposed).
The 'execute_query' tool sends a POST request to a fixed endpoint '/api/query' on the base URL. While the endpoint is fixed, the base URL is derived from an environment variable with a default of localhost. If an attacker can control the environment variable or if the server is misconfigured, this could be used to send SQL queries to an arbitrary host. However, the primary risk is that the tool sends user-supplied SQL queries to an internal service, which could be exploited if the internal service is malicious or if the query is used for injection.
ImpactAn attacker could potentially send arbitrary SQL queries to an internal database service, leading to data exfiltration or modification. Additionally, if the base URL is controllable, SSRF is possible.
FixRestrict the base URL to a trusted internal service and validate that the query does not contain malicious SQL. Use parameterized queries on the backend.
392server.tool(
393 "file_operations",
394 "Perform file operations through your service",
395 {
396 operation: z.enum(["list", "read", "write", "delete"]).describe("File operation to perform"),
397 path: z.string().describe("File path"),
398 content: z.string().optional().describe("Content for write operations"),
399 },
400 async ({ operation, path, content }) => {
401 const baseUrl = process.env.SERVICE_BASE_URL || "http://localhost:3000";
402 const url = `${baseUrl}/api/files`;359server.tool(
360 "service_health",
361 "Check the health and status of your custom service",
362 {},
363 async () => {
364 const baseUrl = process.env.SERVICE_BASE_URL || "http://localhost:3000";
365 const url = `${baseUrl}/health`;// Exploitable if MCP is exposed to untrusted prompts (network_exposed) and attacker can influence environment.
The 'service_health' tool makes a request to a configurable base URL. If an attacker can control the environment variable SERVICE_BASE_URL, they could redirect the health check to an arbitrary host, enabling SSRF.
ImpactAn attacker with control over environment variables could make the server send requests to arbitrary internal or external hosts.
FixHardcode the base URL or restrict it to a trusted internal service. Avoid using environment variables that can be set by untrusted users.
134async ({ expression, operation, number }) => {
135 try {
136 let result: any;
137
138 switch (operation) {
139 case "evaluate":
140 // Safe evaluation of mathematical expressions
141 const sanitizedExpression = expression.replace(/[^0-9+\-*/().\s]/g, '');
142 if (sanitizedExpression !== expression) {
143 throw new Error("Invalid characters in expression. Only numbers, operators, and parentheses are allowed.");
144 }
145 result = Function(`"use strict"; return (${sanitizedExpression})`)();// Exploitable if MCP is exposed to untrusted prompts (network_exposed).
The calculator tool uses the Function constructor to evaluate mathematical expressions. Although it sanitizes the input by removing characters that are not digits, operators, or parentheses, the sanitization regex may be bypassed (e.g., using Unicode characters or other tricks). Additionally, the Function constructor can still execute arbitrary code if the sanitization fails. This is a potential arbitrary code execution risk.
ImpactIf the sanitization is bypassed, an attacker could execute arbitrary JavaScript code on the server, leading to full compromise.
FixUse a proper math expression parser and evaluator library (e.g., mathjs) instead of the Function constructor. Avoid dynamic code evaluation entirely.
438server.tool(
439 "url_utilities",
440 "Perform URL operations like validation, shortening, QR code generation, and expansion",
441 {
442 operation: z.enum(["validate", "shorten", "expand", "qr_code", "analyze"]).describe("URL operation to perform"),
443 url: z.string().describe("URL to process"),
444 options: z.object({...}).optional().describe("Additional options for the operation")
445 },
446 async ({ operation, url, options }) => {
447 try {
448 let result = "";
449
450 switch (operation) {
451 case "validate":
452 try {
453 const urlObj = new URL(url);
454 const isValid = urlObj.protocol === 'http:' || urlObj.protocol === 'https:';// Exploitable if MCP is exposed to untrusted prompts (network_exposed) and tool is upgraded to make real requests.
The 'url_utilities' tool accepts an arbitrary URL string and processes it. While the 'validate' operation checks if the URL is valid, other operations like 'shorten', 'expand', and 'qr_code' use the URL in mock responses without any validation. If these operations were to make actual HTTP requests (as indicated by comments), they would be vulnerable to SSRF. Currently, they are mock implementations, but the design is risky.
ImpactIf the mock implementations are replaced with real API calls, an attacker could use the tool to make requests to arbitrary URLs, enabling SSRF.
FixIf real HTTP requests are added, validate the URL against a whitelist of allowed domains. For now, ensure no actual requests are made.