[ ⌘K ]
← BACK TO SEARCH

mbouclas/mcp-test-server

critical

First attempt at an mcp server

MCP server (purpose undetermined)

purpose: MCP server (purpose undetermined)threat: network exposed
TypeScript0May 20, 2026May 20, 2026GITHUB
aimcpnodejsollama
5/20/2026
high1 finding
src/index.ts
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}`;
src/index.ts:265

// 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.

high1 finding
src/index.ts
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`;
src/index.ts:313

// 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.

high2 findings
src/index.ts
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`;
high1 finding
src/index.ts
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`;
src/index.ts:359

// 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.

medium1 finding
src/index.ts
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})`)();
src/index.ts:134

// 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.

medium1 finding
src/index.ts
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:';
src/index.ts:438

// 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.

network.httpenv.exposureshell.execfilesystem.readfilesystem.writeauth.none
100
LLM-based
high findings+100
medium findings+45