MCPcopy Index your code
hub / github.com/simstudioai/sim / validateMcpServerSsrf

Function validateMcpServerSsrf

apps/sim/lib/mcp/domain-check.ts:153–215  ·  view source on GitHub ↗
(url: string | undefined)

Source from the content-addressed store, hash-verified

151 * @throws McpSsrfError if the URL resolves to a blocked IP address
152 */
153export async function validateMcpServerSsrf(url: string | undefined): Promise<string | null> {
154 if (!url) return null
155 if (getAllowedMcpDomainsFromEnv() !== null) return null
156 if (hasEnvVarInHostname(url)) return null
157
158 let hostname: string
159 try {
160 hostname = new URL(url).hostname
161 } catch {
162 throw new McpSsrfError('MCP server URL is not a valid URL')
163 }
164
165 const cleanHostname =
166 hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname
167
168 if (isLocalhostHostname(cleanHostname)) {
169 if (isHosted) {
170 throw new McpSsrfError('MCP server URL cannot point to a loopback address')
171 }
172 return null
173 }
174
175 if (ipaddr.isValid(cleanHostname)) {
176 if (isPrivateOrReservedIP(cleanHostname)) {
177 throw new McpSsrfError('MCP server URL cannot point to a private or reserved IP address')
178 }
179 // Public IP literal: pin to this exact address so the caller's pinned fetch
180 // (createPinnedFetch) keeps every redirect hop on it. Returning null here
181 // would fall back to the default fetch, which follows a 3xx redirect to a
182 // private/metadata host and escapes SSRF controls.
183 return cleanHostname
184 }
185
186 let address: string
187 try {
188 const lookup = await dns.lookup(cleanHostname, { verbatim: true })
189 address = lookup.address
190 } catch (error) {
191 logger.warn('DNS lookup failed for MCP server URL', {
192 hostname,
193 error: toError(error).message,
194 })
195 throw new McpDnsResolutionError(cleanHostname)
196 }
197
198 if (isLoopbackIP(address)) {
199 if (isHosted) {
200 logger.warn('MCP server URL resolves to loopback address', {
201 hostname,
202 resolvedIP: address,
203 })
204 throw new McpSsrfError('MCP server URL resolves to a loopback address')
205 }
206 } else if (isPrivateOrReservedIP(address)) {
207 logger.warn('MCP server URL resolves to blocked IP address', {
208 hostname,
209 resolvedIP: address,
210 })

Callers 5

resolveConfigEnvVarsMethod · 0.90
validateMcpServerUrlFunction · 0.90
route.tsFile · 0.90

Calls 7

isPrivateOrReservedIPFunction · 0.90
toErrorFunction · 0.90
isLocalhostHostnameFunction · 0.85
isLoopbackIPFunction · 0.85
hasEnvVarInHostnameFunction · 0.70
warnMethod · 0.65

Tested by

no test coverage detected