🔒 feat: Implement Cross-Platform IP Validation Functionality

* Added a new `isIP` function for validating IP addresses in both Node.js and browser environments, replacing the previous reliance on the Node.js `net` module.
* Updated domain extraction and validation logic to utilize the new `isIP` function, ensuring consistent IP validation across the application.
* Enhanced handling of IPv4 and IPv6 addresses, including proper formatting for URLs.
This commit is contained in:
Danny Avila 2025-11-19 17:48:29 -05:00
parent 086e9a92dc
commit ba974604b1
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956

View file

@ -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}]`