scripts/security-audit-network-guard.mjs
Rama main · réplica de lectura del repositorio original.
import http from 'node:http';
import https from 'node:https';
import net from 'node:net';
import tls from 'node:tls';
function normalizedHost(value) {
if (value === undefined || value === null || value === '') return 'localhost';
const text = String(value).toLowerCase();
if (text.startsWith('[')) return text.slice(1, text.indexOf(']'));
if (net.isIP(text)) return text;
const colon = text.lastIndexOf(':');
return colon > -1 && text.indexOf(':') === colon ? text.slice(0, colon) : text;
}
function isLoopback(value) {
const host = normalizedHost(value);
return host === 'localhost' || host === '::1' || host === '0.0.0.0' ||
host === '::' || /^127(?:\.\d{1,3}){3}$/.test(host) ||
host === '::ffff:127.0.0.1';
}
function targetHost(target, fallback) {
if (target instanceof URL) return target.hostname;
if (typeof target === 'string') {
try {
return new URL(target).hostname;
} catch {
return fallback;
}
}
return target?.hostname ?? target?.host ?? fallback;
}
function blockedError(host) {
const target = normalizedHost(host);
const error = new Error(`[audit-network-guard] blocked external connection to ${target}`);
error.code = 'AUDIT_EXTERNAL_NETWORK_BLOCKED';
return error;
}
function blocked(host) {
throw blockedError(host);
}
const originalFetch = globalThis.fetch;
if (originalFetch) {
globalThis.fetch = function auditGuardedFetch(input, init) {
const raw = input instanceof Request ? input.url : String(input);
let url;
try {
url = new URL(raw);
} catch {
return originalFetch.call(this, input, init);
}
if ((url.protocol === 'http:' || url.protocol === 'https:') && !isLoopback(url.hostname)) {
return Promise.reject(blockedError(url.hostname));
}
return originalFetch.call(this, input, init);
};
}
function guardRequest(module, method) {
const original = module[method];
module[method] = function auditGuardedRequest(target, ...args) {
const options = target && typeof target === 'object' && !(target instanceof URL)
? target
: args.find((arg) => arg && typeof arg === 'object' && !(arg instanceof URL));
const host = targetHost(target, options?.hostname ?? options?.host);
if (!isLoopback(host)) blocked(host);
return original.call(this, target, ...args);
};
}
guardRequest(http, 'request');
guardRequest(http, 'get');
guardRequest(https, 'request');
guardRequest(https, 'get');
function guardSocket(module, method) {
const original = module[method];
module[method] = function auditGuardedSocket(...args) {
const first = args[0];
if (typeof first === 'string' && !/^\d+$/.test(first)) {
return original.apply(this, args);
}
const options = first && typeof first === 'object' ? first : undefined;
const host = options?.host ?? options?.hostname ??
(typeof args[1] === 'string' ? args[1] : undefined);
if (!isLoopback(host)) blocked(host);
return original.apply(this, args);
};
}
guardSocket(net, 'connect');
guardSocket(net, 'createConnection');
guardSocket(tls, 'connect');