| tiêu đề | eyaushev swagger-testcase-mcp Latest Server-Side Request Forgery |
|---|
| Mô tả | Summary
A Server-Side Request Forgery (SSRF) vulnerability exists in the swagger-testcase-mcp MCP server implementation.
The root cause is that the source parameter (and old_source / new_source in compare_specs) is only validated as a plain string (z.string()) and then passed directly to fetch() inside loadSource().
Because there is no destination allowlisting or network boundary validation, the server process can be induced to send HTTP requests to attacker-controlled or internal targets (for example localhost, private network services, or cloud metadata endpoints).
Details
The MCP server exposes multiple tools that load OpenAPI/Swagger specs from a remote URL or local file. Confirmed affected tools: fetch_swagger, compare_specs, validate_spec, suggest_missing_tests.
All of these call loadSpec() or fetchAndParse(), which delegate to the shared loadSource() function.
Although sourceParam is defined with Zod, it uses z.string() only — there is no URL format check and no destination restriction.
Optional auth_header and headers parameters are forwarded into the outbound request, enabling credential exfiltration to attacker-controlled endpoints.
This means attacker-controlled tool input can reach the HTTP request sink directly.
Vulnerable Code
Version: Latest (1.0.0)
Files: src/utils/swagger-parser.ts, src/index.ts
Shared HTTP sink (src/utils/swagger-parser.ts):
async function loadSource(source: string, options?: FetchOptions): Promise<string> {
// Local file path
if (isFilePath(source)) {
if (!existsSync(source)) {
throw new Error(`File not found: ${source}`);
}
return await readFile(source, "utf-8");
}
// URL (http/https)
const headers: Record<string, string> = {
Accept: "application/json, application/x-yaml, text/yaml, */*",
...options?.headers,
};
if (options?.authHeader) {
headers["Authorization"] = options.authHeader;
}
const response = await fetch(source, { headers });
if (!response.ok) {
throw new Error(
`Failed to fetch spec from ${source}: ${response.status} ${response.statusText}`
);
}
return await response.text();
}
Tool entry point example (src/index.ts):
const sourceParam = z
.string()
.describe(
"Swagger/OpenAPI spec source: URL (https://...) or local file path (/path/to/spec.json, ./spec.yaml)"
);
server.tool(
"fetch_swagger",
/* ... */,
{
source: sourceParam,
auth_header: authHeaderParam,
headers: headersParam,
},
async ({ source, auth_header, headers }) => {
try {
const spec = await loadSpec(source, auth_header, headers);
// ...
} catch (error: any) {
// ...
}
}
);
Current validation does not restrict:
destination hostname/domain
resolved IP range (loopback / private / link-local / metadata)
destination port
redirect targets
outbound request headers (auth_header, headers)
As a result, attacker-controlled source input reaches the outbound request sink directly.
Data Flow (source → sink)
MCP client invokes tools/call for fetch_swagger (or validate_spec, suggest_missing_tests, compare_specs) with attacker-controlled source.
MCP server receives the URL argument in the tool handler.
Handler calls loadSpec(source, ...) → fetchAndParse(source, ...) → loadSource(source, ...).
Server executes fetch(source, { headers }).
Outbound request is sent from the server network context to an attacker-selected destination.
Response body is parsed as JSON/YAML OpenAPI spec and returned to the caller (full-read SSRF). On parse failure, error messages may still leak HTTP status and target URL.
Malicious attackers can inject malicious tool parameters through methods such as prompt/message injection or hijacking MCP client behavior, thereby triggering SSRF.
Using MCP Inspector (Proof of Concept)
Prerequisites
Dependencies installed for swagger-testcase-mcp (npm install && npm run build)
MCP Inspector available (npx @modelcontextprotocol/inspector)
Local HTTP listener to capture requests
Steps
Start a local receiver endpoint: python -m http.server 8000
Start Inspector with the vulnerable server: npx @modelcontextprotocol/inspector node dist/index.js
In Inspector:
Select transport stdio
Command: node
Args: dist/index.js
Click Connect
Start the local server using PowerShell: python -m http.server 8000
Trigger SSRF:
Open Tools
Select fetch_swagger
Use payload: http://127.0.0.1:8000/ssrf-test
Click Run Tool
On the local receiver terminal (python -m http.server 8000), a new request appears (e.g. GET /ssrf-test), confirming SSRF.
Impact
Server-Side Request Forgery (SSRF) |
|---|
| Nguồn | ⚠️ https://github.com/eyaushev/swagger-testcase-mcp/issues/1 |
|---|
| Người dùng | skywings (UID 98274) |
|---|
| Đệ trình | 25/06/2026 11:02 (cách đây 2 các tháng) |
|---|
| Kiểm duyệt | 13/08/2026 18:56 (2 months later) |
|---|
| Trạng thái | được chấp nhận |
|---|
| Mục VulDB | 389668 [eyaushev swagger-testcase-mcp 5babb27c951fb404bc2b25ec80593616e49054e5 fetch_swagger swagger-parser.ts loadSource nâng cao đặc quyền] |
|---|
| điểm | 20 |
|---|