geelen/workers-mcp-server
highTalk to a Cloudflare Worker from Claude Desktop!
This MCP server allows Claude Desktop to interact with Cloudflare Workers by exposing worker methods as tools. It enables taking screenshots of URLs v...
31async takeScreenshot(url: string) {
32 const browser = await puppeteer.launch(this.env.MYBROWSER)
33 const page = await browser.newPage()
34 await page.setViewport({ width: 768, height: 1024 })
35 await page.goto(url)
36
37 const img = await page.screenshot()
38 await browser.close()
39
40 return new Response(img, { headers: { 'Content-Type': 'image/png' } })
41 }// Exploitable by any user with access to the MCP server (network-exposed).
The takeScreenshot method accepts a URL directly from the user without any validation or sanitization. It passes this URL to page.goto(), which can be used to make requests to internal network resources (e.g., 169.254.169.254 for cloud metadata, internal services, or localhost). This is a classic Server-Side Request Forgery (SSRF) vulnerability.
ImpactAn attacker could use this tool to probe internal network services, access cloud metadata endpoints (e.g., AWS/GCP/Cloudflare metadata), or perform port scanning of internal infrastructure. Since the MCP is network-exposed, this could lead to information disclosure or further compromise.
FixValidate the URL against an allowlist of permitted domains or URL patterns. Reject URLs pointing to private IP ranges (RFC 1918), loopback addresses, or metadata endpoints. Consider using a URL parser to check the hostname and block internal addresses.
53async sendEmail(recipient: string, subject: string, contentType: string, body: string) {
54 try {
55 const msg = createMimeMessage()
56 const from = this.env.EMAIL_FROM
57
58 msg.setSender({ name: 'Claude Desktop', addr: from })
59 msg.setRecipient(recipient)
60 msg.setSubject(subject)
61 msg.addMessage({
62 contentType: contentType,
63 data: body,
64 })
65
66 await this.env.EMAIL.send(new EmailMessage(from, recipient, msg.asRaw()))
67 return 'Email sent successfully!'
68 } catch (error) {
69 console.error(error)
70 throw error
71 }
72 }