@whatwg-node/node-fetch
Version:
Fetch API implementation for Node
86 lines (85 loc) • 3.13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeIpAddress = normalizeIpAddress;
exports.needsIpv6SanWorkaround = needsIpv6SanWorkaround;
exports.checkServerIdentityIpv6San = checkServerIdentityIpv6San;
exports.getHttpsCheckServerIdentity = getHttpsCheckServerIdentity;
const tslib_1 = require("tslib");
const node_net_1 = require("node:net");
const node_tls_1 = tslib_1.__importDefault(require("node:tls"));
/**
* Canonicalize an IP for equality checks (`::1` vs `0:0:0:0:0:0:0:1`).
*/
function normalizeIpAddress(ip) {
const bare = ip.replace(/^\[|\]$/g, '');
const family = (0, node_net_1.isIP)(bare);
if (family === 4) {
return bare;
}
if (family === 6) {
// WHATWG URL hostname normalizes IPv6 to a canonical form.
return new URL(`http://[${bare}]`).hostname.replace(/^\[|\]$/g, '').toLowerCase();
}
return bare.toLowerCase();
}
function collectCertIpAddresses(cert) {
const alt = cert.subjectaltname;
if (!alt) {
return [];
}
const ips = [];
for (const part of alt.split(', ')) {
if (part.startsWith('IP Address:')) {
ips.push(part.slice('IP Address:'.length));
}
}
return ips;
}
let probedIpv6SanWorkaround = false;
let needsIpv6SanWorkaroundValue = false;
/**
* Detect Node.js IPv6 IP-SAN regression in `tls.checkServerIdentity`
* (https://github.com/nodejs/node/issues/64032). Probes once, on first use.
*/
function needsIpv6SanWorkaround() {
if (!probedIpv6SanWorkaround) {
probedIpv6SanWorkaround = true;
try {
needsIpv6SanWorkaroundValue =
node_tls_1.default.checkServerIdentity('::1', {
subject: {},
subjectaltname: 'IP Address:::1',
}) != null;
}
catch {
// If the probe itself throws, prefer the workaround.
needsIpv6SanWorkaroundValue = true;
}
}
return needsIpv6SanWorkaroundValue;
}
/**
* Custom verifier that correctly matches IPv6 literals against `IP Address` SANs.
*/
function checkServerIdentityIpv6San(hostname, cert) {
const bareHost = hostname.replace(/^\[|\]$/g, '').replace(/\.$/, '');
if ((0, node_net_1.isIP)(bareHost)) {
const certIps = collectCertIpAddresses(cert);
const want = normalizeIpAddress(bareHost);
if (certIps.some(ip => normalizeIpAddress(ip) === want)) {
return undefined;
}
const reason = `Hostname/IP does not match certificate's altnames: IP: ${bareHost} is not in the cert's list: ${certIps.join(', ')}`;
const error = new Error(reason);
error.reason = reason;
error.host = bareHost;
error.cert = cert;
error.code = 'ERR_TLS_CERT_ALTNAME_INVALID';
return error;
}
return node_tls_1.default.checkServerIdentity(hostname, cert);
}
/** Lazy: only set when the first https request probes an affected Node build. */
function getHttpsCheckServerIdentity() {
return needsIpv6SanWorkaround() ? checkServerIdentityIpv6San : undefined;
}