UNPKG

@whatwg-node/node-fetch

Version:

Fetch API implementation for Node

86 lines (85 loc) 3.13 kB
"use strict"; 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; }