diff --git a/packages/data-provider/src/actions.ts b/packages/data-provider/src/actions.ts index d6230f3f4b..f6ab82526c 100644 --- a/packages/data-provider/src/actions.ts +++ b/packages/data-provider/src/actions.ts @@ -1,7 +1,6 @@ import { z } from 'zod'; import { URL } from 'url'; import _axios from 'axios'; -import * as net from 'net'; import crypto from 'crypto'; import { load } from 'js-yaml'; import type { ActionMetadata, ActionMetadataRuntime } from './types/agents'; @@ -567,6 +566,32 @@ export type ValidationResult = { serverUrl?: string; }; +/** + * Cross-platform IP validation (works in Node.js and browser). + * @param input - String to check if it's an IP address + * @returns 0 if not IP, 4 for IPv4, 6 for IPv6 + */ +function isIP(input: string): number { + // IPv4 regex - matches 0.0.0.0 to 255.255.255.255 + const ipv4Regex = + /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + + if (ipv4Regex.test(input)) { + return 4; + } + + // IPv6 regex - simplified but covers most cases + // Handles compressed (::), full, and mixed notations + const ipv6Regex = + /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; + + if (ipv6Regex.test(input)) { + return 6; + } + + return 0; +} + /** * Extracts domain from URL (protocol + hostname). * @param url - URL to extract from @@ -576,8 +601,8 @@ export function extractDomainFromUrl(url: string): string { try { /** Parsed URL object */ const parsedUrl = new URL(url); - // Preserve brackets for IPv6 addresses using net.isIP - const ipVersion = net.isIP(parsedUrl.hostname); + // Preserve brackets for IPv6 addresses using isIP + const ipVersion = isIP(parsedUrl.hostname); const hostname = ipVersion === 6 ? `[${parsedUrl.hostname}]` : parsedUrl.hostname; return `${parsedUrl.protocol}//${hostname}`; } catch { @@ -616,7 +641,7 @@ export function validateActionDomain( /** Spec hostname only */ const specHostname = specUrl.hostname; /** Spec domain with protocol (handle IPv6 brackets) */ - const specIpVersion = net.isIP(specHostname); + const specIpVersion = isIP(specHostname); const normalizedSpecDomain = specIpVersion === 6 ? `${specUrl.protocol}//[${specHostname}]` @@ -651,8 +676,8 @@ export function validateActionDomain( const normalizedClientHostname = clientHostname.replace(/^\[(.+)\]$/, '$1'); const normalizedSpecHostname = specHostname.replace(/^\[(.+)\]$/, '$1'); - /** Check if hostname is valid IP using Node.js built-in net module */ - const isIPAddress = net.isIP(normalizedClientHostname) !== 0; + /** Check if hostname is valid IP using cross-platform isIP */ + const isIPAddress = isIP(normalizedClientHostname) !== 0; /** Normalized client domain */ let normalizedClientDomain: string; @@ -662,7 +687,7 @@ export function validateActionDomain( // IP addresses inherit protocol from spec, domains default to https if (isIPAddress) { // IPv6 addresses need brackets in URLs - const ipVersion = net.isIP(normalizedClientHostname); + const ipVersion = isIP(normalizedClientHostname); const hostname = ipVersion === 6 && !clientHostname.startsWith('[') ? `[${normalizedClientHostname}]`