T09 · Insecure Skill Coding Practices
- Location
reference/zapier-backend.ts:108- Finding
Server-Side Request Forgery Through Unrestricted MCP URLs
- Content
View full analysis
{ try { const response = await fetch(mcpUrl, { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list", }), }); ``` The save handler only verifies that the supplied string begins with `https://` before passing it to the request function: ```typescript const mcpUrl = typeof params?.mcpUrl === "string" ? params.mcpUrl.trim() : ""; if (!mcpUrl) { respond(true, { success: false, error: "MCP URL is required" }); return; } // Validate URL format if (!mcpUrl.startsWith("https://")) { respond(true, { success: false, error: "MCP URL must use HTTPS" }); return; } // Test the URL first const testResult = await testZapierUrl(mcpUrl); if (!testResult.success) { respond(true, { success: false, error: `Connection test failed: ${testResult.error}` }); return; } ``` The test handler accepts an arbitrary URL without even applying the save handler's prefix check: ```typescript let mcpUrl = typeof params?.mcpUrl === "string" ? params.mcpUrl.trim() : ""; if (!mcpUrl) { // Use saved URL if not provided const status = getZapierConfig(); if (!status.mcpUrl) { respond(true, { success: false, error: "No URL configured" }); return; } mcpUrl = status.mcpUrl; } const testResult = await testZapierUrl(mcpUrl); respond(true, testResult); ``` ### Technical Analysis The integration documentation expects personalized URLs under `https://actions.zapier.com/mcp/...`, but the backend does not enforce that destination. The ...[truncated 2678 chars]- Remediation
View remediation
