From 119f151da6fbe11408ead926eb98e4dfd0c38c94 Mon Sep 17 00:00:00 2001 From: dewhush Date: Mon, 15 Jun 2026 00:43:48 +0700 Subject: [PATCH] fix: ssrf validation in Pixelle Addressed unsafe code patterns found during security review: - ssrf in src/tool/web/web-fetch.tool.ts: The web fetch tool validates that the URL protocol is HTTP or HTTPS but fails to verify that the target hostname or reso Tested locally, no regressions observed. --- src/tool/web/web-fetch.tool.ts | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/tool/web/web-fetch.tool.ts b/src/tool/web/web-fetch.tool.ts index 335ae0d..cc0e8a0 100644 --- a/src/tool/web/web-fetch.tool.ts +++ b/src/tool/web/web-fetch.tool.ts @@ -1,3 +1,6 @@ +import {lookup} from "node:dns/promises"; +import {isIP} from "node:net"; +import ipaddr from "ipaddr.js"; import {z} from "zod"; import {ToolError} from "../tool-error.js"; @@ -34,7 +37,7 @@ export const webFetchTool: Tool 0 ? Math.floor(input.maxLength) @@ -69,7 +72,8 @@ function requireNetworkPermission(context: ToolContext, toolName: string): void } } -function parseHttpUrl(url: string, toolName: string): string { +// Security fix: Validate resolved IP address to prevent SSRF against private/internal networks +async function parseHttpUrl(url: string, toolName: string): Promise { try { const parsedUrl = new URL(url); @@ -77,11 +81,28 @@ function parseHttpUrl(url: string, toolName: string): string { throw new Error("Unsupported protocol."); } + const hostname = parsedUrl.hostname; + let ip = hostname; + + // Resolve hostname to IP if it's not already an IP address + if (!isIP(hostname)) { + const resolved = await lookup(hostname); + ip = resolved.address; + } + + const addr = ipaddr.parse(ip); + const range = addr.range(); + + // 'unicast' covers public IPs; block 'private', 'loopback', 'linkLocal', etc. + if (range !== "unicast") { + throw new Error("Access to internal or private networks is not allowed."); + } + return parsedUrl.toString(); } catch (error) { throw new ToolError({ code: "TOOL_INVALID_INPUT", - message: "web_fetch requires a valid HTTP or HTTPS URL.", + message: "web_fetch requires a valid, public HTTP or HTTPS URL.", toolName, cause: error, });