UNPKG

@open-condo/miniapp-utils

Version:

A set of helper functions / components / hooks used to build new condo apps fast

1 lines 39.8 kB
{"version":3,"sources":["../../../src/helpers/proxying/utils.ts","../../../src/helpers/ip/utils.ts","../../../src/helpers/ip/ipv4.ts","../../../src/helpers/ip/ipv6.ts","../../../src/helpers/ip/index.ts","../../../src/helpers/proxying/proxy.ts"],"sourcesContent":["import jwt from 'jsonwebtoken'\nimport proxyAddr from 'proxy-addr'\nimport { z } from 'zod'\n\nimport { isInSubnet } from '../ip'\n\nimport type { IncomingMessage } from 'http'\n\ntype ProxyConfig = {\n address: string | Array<string>\n secret: string\n}\n\ntype ProxyId = string\n\nexport type KnownProxies = Record<ProxyId, ProxyConfig>\n\nexport type TrustProxyFunction = (proxyAddr: string, idx: number) => boolean\n\nconst _ipSchema = z.union([z.ipv4(), z.ipv6()])\nconst _timeStampBasicRegexp = /^\\d+$/\n\nconst DEFAULT_PROXY_TIMEOUT_IN_MS = 5_000 // 5 sec to pass request is enough\nconst X_PROXY_ID_HEADER = 'x-proxy-id' as const\nconst X_PROXY_IP_HEADER = 'x-proxy-ip' as const\nconst X_PROXY_TIMESTAMP_HEADER = 'x-proxy-timestamp' as const\nconst X_PROXY_SIGNATURE_HEADER = 'x-proxy-signature' as const\n\nexport type ProxyHeaders = {\n [X_PROXY_ID_HEADER]: string\n [X_PROXY_IP_HEADER]: string\n [X_PROXY_TIMESTAMP_HEADER]: string\n [X_PROXY_SIGNATURE_HEADER]: string\n}\n\nfunction _getTimestampFromHeader (timestamp: string) {\n if (!_timeStampBasicRegexp.test(timestamp)) return Number.NaN\n return (new Date(parseInt(timestamp))).getTime()\n}\n\nfunction _isProxyIP (ip: string, proxyConfig: KnownProxies[string]) {\n const addresses = Array.isArray(proxyConfig.address) ? proxyConfig.address : [proxyConfig.address]\n const config = addresses.reduce((acc, addr) => {\n const isSubnet = addr.split('/').length > 1\n if (isSubnet) {\n acc.subnets.push(addr)\n } else {\n acc.ips.push(addr)\n }\n\n return acc\n }, { ips: [] as Array<string>, subnets: [] as Array<string> })\n\n if (config.ips.length && config.ips.includes(ip)) {\n return true\n }\n\n return !!(config.subnets.length && isInSubnet(ip, config.subnets))\n}\n\nexport function getRequestIp (req: IncomingMessage, trustProxyFn: TrustProxyFunction, knownProxies?: KnownProxies): string {\n // NOTE: That's what express does under the hood: https://github.com/expressjs/express/blob/4.x/lib/request.js#L349\n const originalIP = proxyAddr(req, trustProxyFn)\n\n if (!knownProxies) return originalIP\n\n const xProxyId = req.headers[X_PROXY_ID_HEADER]\n const xProxyIp = req.headers[X_PROXY_IP_HEADER]\n // NOTE: used to prevent relay attacks\n const xProxyTimestamp = req.headers[X_PROXY_TIMESTAMP_HEADER]\n const xProxySignature = req.headers[X_PROXY_SIGNATURE_HEADER]\n\n if (\n typeof xProxyId !== 'string' ||\n typeof xProxyIp !== 'string' ||\n typeof xProxyTimestamp !== 'string' ||\n typeof xProxySignature !== 'string'\n ) {\n return originalIP\n }\n\n // NOTE: validate, that x-proxy-ip is correct IP\n const { success: isValidIp } = _ipSchema.safeParse(xProxyIp)\n if (!isValidIp) {\n return originalIP\n }\n\n // NOTE: validate timestamp: it should less than now and no more than 5s less than now (recent enough)\n const timestamp = _getTimestampFromHeader(xProxyTimestamp)\n const now = Date.now()\n if (\n Number.isNaN(timestamp) ||\n timestamp > now ||\n now - timestamp > DEFAULT_PROXY_TIMEOUT_IN_MS\n ) {\n return originalIP\n }\n\n // NOTE: validate signature and proxy IP\n if (!Object.hasOwn(knownProxies, xProxyId)) {\n return originalIP\n }\n const proxyConfig = knownProxies[xProxyId]\n\n if (!proxyConfig || !_isProxyIP(originalIP, proxyConfig)) {\n return originalIP\n }\n\n try {\n // NOTE: config is passed from outside, where its obtained from .env, so its not hard-coded\n // nosemgrep: javascript.jsonwebtoken.security.jwt-hardcode.hardcoded-jwt-secret\n const jwtPayload = jwt.verify(xProxySignature, proxyConfig.secret, { algorithms: ['HS256'] })\n const expectedPayloadSchema = z.object({\n [X_PROXY_TIMESTAMP_HEADER]: z.literal(xProxyTimestamp),\n [X_PROXY_IP_HEADER]: z.literal(xProxyIp),\n [X_PROXY_ID_HEADER]: z.literal(xProxyId),\n method: z.literal(req.method),\n url: z.literal(req.url),\n })\n const { success: isMatchingSignature } = expectedPayloadSchema.safeParse(jwtPayload)\n\n return isMatchingSignature ? xProxyIp : originalIP\n } catch {\n return originalIP\n }\n}\n\nexport function getProxyHeadersForIp (method: string, url: string, ip: string, proxyId: string, secret: string): ProxyHeaders {\n const timestampString = String(Date.now())\n\n return {\n [X_PROXY_IP_HEADER]: ip,\n [X_PROXY_ID_HEADER]: proxyId,\n [X_PROXY_TIMESTAMP_HEADER]: timestampString,\n [X_PROXY_SIGNATURE_HEADER]: jwt.sign({\n [X_PROXY_IP_HEADER]: ip,\n [X_PROXY_ID_HEADER]: proxyId,\n [X_PROXY_TIMESTAMP_HEADER]: timestampString,\n method,\n url,\n }, secret, {\n expiresIn: Math.round(DEFAULT_PROXY_TIMEOUT_IN_MS / 1000),\n algorithm: 'HS256',\n }),\n }\n}\n\nexport function isRelativeUrl (url: string) {\n return url.startsWith('/')\n}\n\nexport function replaceUpstreamEndpoint ({\n endpoint,\n proxyPrefix,\n upstreamPrefix,\n upstreamOrigin,\n rewrites = {},\n}: {\n endpoint: string\n proxyPrefix: string\n upstreamPrefix: string\n upstreamOrigin: string\n rewrites?: Record<string, string>\n}) {\n const isRelativeLocation = isRelativeUrl(endpoint)\n const locationUrl = new URL(endpoint, 'https://_')\n\n let targetLocation\n\n const lookupUrl = new URL(endpoint, upstreamOrigin)\n lookupUrl.search = ''\n // First lookup relative location ('/some/path')\n if (isRelativeLocation || lookupUrl.origin === upstreamOrigin) {\n targetLocation ??= rewrites[lookupUrl.pathname]\n }\n\n // Then lookup absolute location ('https://upstreamhost.com/some/path')\n targetLocation ??= rewrites[lookupUrl.toString()]\n\n // If found lookup, perform smart replacement\n if (targetLocation) {\n const isRelativeTarget = isRelativeUrl(targetLocation)\n const targetUrl = new URL(targetLocation, upstreamOrigin)\n const targetSearchParams = new URLSearchParams(targetUrl.searchParams)\n // Replace target search params with location search params, then apply target search params on top\n if (!targetUrl.hash) {\n targetUrl.hash = locationUrl.hash\n }\n targetUrl.search = locationUrl.search\n for (const [name] of targetSearchParams.entries()) {\n targetUrl.searchParams.delete(name)\n }\n for (const [name, value] of targetSearchParams.entries()) {\n targetUrl.searchParams.append(name, value)\n }\n\n if (isRelativeTarget) {\n return targetUrl.pathname + targetUrl.search + targetUrl.hash\n } else {\n return targetUrl.toString()\n }\n }\n\n // If location is relative or has same as upstream domain, try to replace back upstreamPrefix with proxyPrefix\n if ((isRelativeLocation || locationUrl.origin === upstreamOrigin) && locationUrl.pathname.startsWith(upstreamPrefix)) {\n locationUrl.pathname = proxyPrefix + locationUrl.pathname.slice(upstreamPrefix.length)\n\n return locationUrl.pathname + locationUrl.search + locationUrl.hash\n }\n\n return endpoint\n}\n","// RegExp for testing if a string represents an IPv4 address\nconst v4Seg = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'\nconst v4Str = `(${v4Seg}[.]){3}${v4Seg}`\nconst IPv4Reg = new RegExp(`^${v4Str}$`)\n\n// RegExp for testing if a string represents an IPv6 address\nconst v6Seg = '(?:[0-9a-fA-F]{1,4})'\nconst IPv6Reg = new RegExp(\n '^(' +\n `(?:${v6Seg}:){7}(?:${v6Seg}|:)|` +\n `(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|` +\n `(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|` +\n `(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|` +\n `(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|` +\n `(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|` +\n `(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|` +\n `(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:))` +\n ')(%[0-9a-zA-Z]{1,})?$'\n)\n\n/**\n * Returns true if the string represents an IPv4 address. Matches Node.js net.isIPv4\n * functionality.\n */\nexport function isIPv4 (s: string) {\n return IPv4Reg.test(s)\n}\n\n/**\n * Returns true if the string represents an IPv6 address. Matches Node.js net.isIPv6\n * functionality.\n */\nexport function isIPv6 (s: string) {\n return IPv6Reg.test(s)\n}\n\nexport function isIP (s: string) {\n if (isIPv4(s)) return 4\n if (isIPv6(s)) return 6\n return 0\n}\n","import ipRanges from './ranges'\nimport * as util from './utils'\n\n/**\n * Given an IPv4 address, convert it to a 32-bit long integer.\n * @param ip the IPv4 address to expand\n * @throws if the string is not a valid IPv4 address\n */\nfunction ipv4ToLong (ip: string) {\n if (!util.isIPv4(ip)) {\n throw new Error(`not a valid IPv4 address: ${ip}`)\n }\n const octets = ip.split('.')\n return (\n ((parseInt(octets[0], 10) << 24) +\n (parseInt(octets[1], 10) << 16) +\n (parseInt(octets[2], 10) << 8) +\n parseInt(octets[3], 10)) >>>\n 0\n )\n}\n\n// this is the most optimised checker.\nfunction createLongChecker (subnet: string): (addressLong: number) => boolean {\n const [subnetAddress, prefixLengthString] = subnet.split('/')\n const prefixLength = parseInt(prefixLengthString, 10)\n if (!subnetAddress || !Number.isInteger(prefixLength)) {\n throw new Error(`not a valid IPv4 subnet: ${subnet}`)\n }\n\n if (prefixLength < 0 || prefixLength > 32) {\n throw new Error(`not a valid IPv4 prefix length: ${prefixLength} (from ${subnet})`)\n }\n\n const subnetLong = ipv4ToLong(subnetAddress)\n return addressLong => {\n if (prefixLength === 0) {\n return true\n }\n const subnetPrefix = subnetLong >> (32 - prefixLength)\n const addressPrefix = addressLong >> (32 - prefixLength)\n\n return subnetPrefix === addressPrefix\n }\n}\n\n/**\n * The functional version, creates a checking function that takes an IPv4 Address and\n * returns whether or not it is contained in (one of) the subnet(s).\n * @param subnetOrSubnets the IPv4 CIDR to test (or an array of them)\n * @throws if the subnet is not a valid IP addresses, or the CIDR prefix length\n * is not valid\n */\nexport function createChecker (\n subnetOrSubnets: string | string[]\n): (address: string) => boolean {\n if (Array.isArray(subnetOrSubnets)) {\n const checks = subnetOrSubnets.map(subnet => createLongChecker(subnet))\n return address => {\n const addressLong = ipv4ToLong(address)\n return checks.some(check => check(addressLong))\n }\n }\n const check = createLongChecker(subnetOrSubnets)\n return address => {\n const addressLong = ipv4ToLong(address)\n return check(addressLong)\n }\n}\n\n/**\n * Test if the given IPv4 address is contained in the specified subnet.\n * @param address the IPv4 address to check\n * @param subnetOrSubnets the IPv4 CIDR to test (or an array of them)\n * @throws if the address or subnet are not valid IP addresses, or the CIDR prefix length\n * is not valid\n */\nexport function isInSubnet (address: string, subnetOrSubnets: string | string[]): boolean {\n return createChecker(subnetOrSubnets)(address)\n}\n\n// cache these special subnet checkers\nconst specialNetsCache: Record<string, (address: string) => boolean> = {}\n\n/** Test if the given IP address is a private/internal IP address. */\nexport function isPrivate (address: string) {\n if (!('private' in specialNetsCache)) {\n specialNetsCache['private'] = createChecker(ipRanges.private.ipv4)\n }\n return specialNetsCache['private'](address)\n}\n\n/** Test if the given IP address is a localhost address. */\nexport function isLocalhost (address: string) {\n if (!('localhost' in specialNetsCache)) {\n specialNetsCache['localhost'] = createChecker(ipRanges.localhost.ipv4)\n }\n return specialNetsCache['localhost'](address)\n}\n\n/** Test if the given IP address is in a known reserved range and not a normal host IP */\nexport function isReserved (address: string) {\n if (!('reserved' in specialNetsCache)) {\n specialNetsCache['reserved'] = createChecker(ipRanges.reserved.ipv4)\n }\n return specialNetsCache['reserved'](address)\n}\n\n/**\n * Test if the given IP address is a special address of any kind (private, reserved,\n * localhost)\n */\nexport function isSpecial (address: string) {\n if (!('special' in specialNetsCache)) {\n specialNetsCache['special'] = createChecker([\n ...ipRanges.private.ipv4,\n ...ipRanges.localhost.ipv4,\n ...ipRanges.reserved.ipv4,\n ])\n }\n return specialNetsCache['special'](address)\n}","import ipRanges from './ranges'\nimport * as util from './utils'\n\n// Note: Profiling shows that on recent versions of Node, string.split(RegExp) is faster\n// than string.split(string).\nconst dot = /\\./\nconst mappedIpv4 = /^(.+:ffff:)(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(?:%.+)?$/\nconst colon = /:/\nconst doubleColon = /::/\n\n/**\n * Given a mapped IPv4 address (e.g. ::ffff:203.0.113.38 or similar), convert it to the\n * equivalent standard IPv6 address.\n * @param ip the IPv4-to-IPv6 mapped address\n */\nfunction mappedIpv4ToIpv6 (ip: string) {\n const matches = ip.match(mappedIpv4)\n\n if (!matches || !util.isIPv4(matches[2])) {\n throw new Error(`not a mapped IPv4 address: ${ip}`)\n }\n\n // mapped IPv4 address\n const prefix = matches[1]\n const ipv4 = matches[2]\n\n const parts = ipv4.split(dot).map(x => parseInt(x, 10))\n\n const segment7 = ((parts[0] << 8) + parts[1]).toString(16)\n const segment8 = ((parts[2] << 8) + parts[3]).toString(16)\n\n return `${prefix}${segment7}:${segment8}`\n}\n\n/**\n * Given a mapped IPv4 address, return the bare IPv4 equivalent.\n */\nexport function extractMappedIpv4 (ip: string) {\n const matches = ip.match(mappedIpv4)\n\n if (!matches || !util.isIPv4(matches[2])) {\n throw new Error(`not a mapped IPv4 address: ${ip}`)\n }\n\n return matches[2]\n}\n\n/**\n * Given an IP address that may have double-colons, expand all segments and return them\n * as an array of 8 segments (16-bit words). As a peformance enhancement (indicated by\n * profiling), for any segment that was missing but should be a '0', returns undefined.\n * @param ip the IPv6 address to expand\n * @throws if the string is not a valid IPv6 address\n */\nfunction getIpv6Segments (ip: string): string[] {\n if (!util.isIPv6(ip)) {\n throw new Error(`not a valid IPv6 address: ${ip}`)\n }\n\n if (dot.test(ip)) {\n return getIpv6Segments(mappedIpv4ToIpv6(ip))\n }\n\n // break it into an array, including missing \"::\" segments\n const [beforeChunk, afterChunk] = ip.split(doubleColon)\n\n const beforeParts = (beforeChunk && beforeChunk.split(colon)) || []\n const afterParts = (afterChunk && afterChunk.split(colon)) || []\n const missingSegments = new Array<string>(8 - (beforeParts.length + afterParts.length))\n\n return beforeParts.concat(missingSegments, afterParts)\n}\n\n/**\n * Test if the given IPv6 address is contained in the specified subnet.\n * @param address the IPv6 address to check\n * @param subnetOrSubnets the IPv6 CIDR to test (or an array of them)\n * @throws if the address or subnet are not valid IP addresses, or the CIDR prefix length\n * is not valid\n */\nexport function isInSubnet (address: string, subnetOrSubnets: string | string[]): boolean {\n return createChecker(subnetOrSubnets)(address)\n}\n\n/**\n * Create a function to test if a given IPv6 address is contained in the specified subnet.\n * @param subnetOrSubnets the IPv6 CIDR to test (or an array of them)\n * @throws if the subnet(s) are not valid IP addresses, or the CIDR prefix lengths\n * are not valid\n */\nexport function createChecker (\n subnetOrSubnets: string | string[]\n): (address: string) => boolean {\n if (Array.isArray(subnetOrSubnets)) {\n const checks = subnetOrSubnets.map(subnet => createSegmentChecker(subnet))\n return address => {\n const segments = getIpv6Segments(address)\n return checks.some(check => check(segments))\n }\n }\n const check = createSegmentChecker(subnetOrSubnets)\n return address => {\n const segments = getIpv6Segments(address)\n return check(segments)\n }\n}\n\n// This creates the last function that works on the most deconstructed data\nfunction createSegmentChecker (subnet: string): (segments: string[]) => boolean {\n const [subnetAddress, prefixLengthString] = subnet.split('/')\n const prefixLength = parseInt(prefixLengthString, 10)\n\n if (!subnetAddress || !Number.isInteger(prefixLength)) {\n throw new Error(`not a valid IPv6 CIDR subnet: ${subnet}`)\n }\n\n if (prefixLength < 0 || prefixLength > 128) {\n throw new Error(`not a valid IPv6 prefix length: ${prefixLength} (from ${subnet})`)\n }\n\n // the next line throws if the address is not a valid IPv6 address\n const subnetSegments = getIpv6Segments(subnetAddress)\n\n return addressSegments => {\n for (let i = 0; i < 8; ++i) {\n const bitCount = Math.min(prefixLength - i * 16, 16)\n\n if (bitCount <= 0) {\n break\n }\n\n const subnetPrefix =\n ((subnetSegments[i] && parseInt(subnetSegments[i], 16)) || 0) >> (16 - bitCount)\n\n const addressPrefix =\n ((addressSegments[i] && parseInt(addressSegments[i], 16)) || 0) >>\n (16 - bitCount)\n\n if (subnetPrefix !== addressPrefix) {\n return false\n }\n }\n\n return true\n }\n}\n\n// cache these special subnet checkers\nconst specialNetsCache: Record<string, (address: string) => boolean> = {}\n\n/** Test if the given IP address is a private/internal IP address. */\nexport function isPrivate (address: string) {\n if (!('private' in specialNetsCache)) {\n specialNetsCache['private'] = createChecker(ipRanges.private.ipv6)\n }\n return specialNetsCache['private'](address)\n}\n\n/** Test if the given IP address is a localhost address. */\nexport function isLocalhost (address: string) {\n if (!('localhost' in specialNetsCache)) {\n specialNetsCache['localhost'] = createChecker(ipRanges.localhost.ipv6)\n }\n return specialNetsCache['localhost'](address)\n}\n\n/** Test if the given IP address is an IPv4 address mapped onto IPv6 */\nexport function isIPv4MappedAddress (address: string) {\n if (!('mapped' in specialNetsCache)) {\n specialNetsCache['mapped'] = createChecker('::ffff:0:0/96')\n }\n if (specialNetsCache['mapped'](address)) {\n const matches = address.match(mappedIpv4)\n return Boolean(matches && util.isIPv4(matches[2]))\n }\n return false\n}\n\n/** Test if the given IP address is in a known reserved range and not a normal host IP */\nexport function isReserved (address: string) {\n if (!('reserved' in specialNetsCache)) {\n specialNetsCache['reserved'] = createChecker(ipRanges.reserved.ipv6)\n }\n return specialNetsCache['reserved'](address)\n}\n\n/**\n * Test if the given IP address is a special address of any kind (private, reserved,\n * localhost)\n */\nexport function isSpecial (address: string) {\n if (!('special' in specialNetsCache)) {\n specialNetsCache['special'] = createChecker([\n ...ipRanges.private.ipv6,\n ...ipRanges.localhost.ipv6,\n ...ipRanges.reserved.ipv6,\n ])\n }\n return specialNetsCache['special'](address)\n}","import * as IPv4 from './ipv4'\nimport * as IPv6 from './ipv6'\nimport * as util from './utils'\n\nexport { isIP, isIPv4, isIPv6 } from './utils'\nexport { IPv4, IPv6 }\n\n/**\n * Test if the given IP address is contained in the specified subnet.\n * @param address the IPv4 or IPv6 address to check\n * @param subnetOrSubnets the IPv4 or IPv6 CIDR to test (or an array of them)\n * @throws if any of the address or subnet(s) are not valid IP addresses, or the CIDR\n * prefix length is not valid\n */\nexport function isInSubnet (address: string, subnetOrSubnets: string | string[]): boolean {\n return createChecker(subnetOrSubnets)(address)\n}\n/**\n * Create a function to test if the given IP address is contained in the specified subnet.\n * @param subnetOrSubnets the IPv4 or IPv6 CIDR to test (or an array of them)\n * @throws if any of the subnet(s) are not valid IP addresses, or the CIDR\n * prefix length is not valid\n */\nexport function createChecker (\n subnetOrSubnets: string | string[]\n): (address: string) => boolean {\n if (!Array.isArray(subnetOrSubnets)) {\n return createChecker([subnetOrSubnets])\n }\n\n const subnetsByVersion = subnetOrSubnets.reduce(\n (acc, subnet) => {\n const ip = subnet.split('/')[0];\n (acc[util.isIP(ip)] as string[]).push(subnet)\n return acc\n },\n { 0: [], 4: [], 6: [] }\n )\n\n if (subnetsByVersion[0].length !== 0) {\n throw new Error(`some subnets are not valid IP addresses: ${subnetsByVersion[0]}`)\n }\n\n const check4 = IPv4.createChecker(subnetsByVersion[4])\n const check6 = IPv6.createChecker(subnetsByVersion[6])\n\n return address => {\n if (!util.isIP(address)) {\n throw new Error(`not a valid IPv4 or IPv6 address: ${address}`)\n }\n\n // for mapped IPv4 addresses, compare against both IPv6 and IPv4 subnets\n if (util.isIPv6(address) && IPv6.isIPv4MappedAddress(address)) {\n return check6(address) || check4(IPv6.extractMappedIpv4(address))\n }\n\n if (util.isIPv6(address)) {\n return check6(address)\n } else {\n return check4(address)\n }\n }\n}\n\n/** Test if the given IP address is a private/internal IP address. */\nexport function isPrivate (address: string) {\n if (util.isIPv6(address)) {\n if (IPv6.isIPv4MappedAddress(address)) {\n return IPv4.isPrivate(IPv6.extractMappedIpv4(address))\n }\n return IPv6.isPrivate(address)\n } else {\n return IPv4.isPrivate(address)\n }\n}\n\n/** Test if the given IP address is a localhost address. */\nexport function isLocalhost (address: string) {\n if (util.isIPv6(address)) {\n if (IPv6.isIPv4MappedAddress(address)) {\n return IPv4.isLocalhost(IPv6.extractMappedIpv4(address))\n }\n return IPv6.isLocalhost(address)\n } else {\n return IPv4.isLocalhost(address)\n }\n}\n\n/** Test if the given IP address is an IPv4 address mapped onto IPv6 */\nexport function isIPv4MappedAddress (address: string) {\n if (util.isIPv6(address)) {\n return IPv6.isIPv4MappedAddress(address)\n } else {\n return false\n }\n}\n\n/** Test if the given IP address is in a known reserved range and not a normal host IP */\nexport function isReserved (address: string) {\n if (util.isIPv6(address)) {\n if (IPv6.isIPv4MappedAddress(address)) {\n return IPv4.isReserved(IPv6.extractMappedIpv4(address))\n }\n return IPv6.isReserved(address)\n } else {\n return IPv4.isReserved(address)\n }\n}\n\n/**\n * Test if the given IP address is a special address of any kind (private, reserved,\n * localhost)\n */\nexport function isSpecial (address: string) {\n if (util.isIPv6(address)) {\n if (IPv6.isIPv4MappedAddress(address)) {\n return IPv4.isSpecial(IPv6.extractMappedIpv4(address))\n }\n return IPv6.isSpecial(address)\n } else {\n return IPv4.isSpecial(address)\n }\n}\n\nexport const check = isInSubnet","import httpProxy from 'http-proxy'\n\nimport { getProxyHeadersForIp, getRequestIp, replaceUpstreamEndpoint } from './utils'\n\nimport type { KnownProxies, TrustProxyFunction } from './utils'\nimport type { IncomingMessage, ServerResponse } from 'http'\n\ntype IpProxyingOptions = {\n /** ID of the proxy to pass as x-proxy-id header */\n proxyId: string\n /** secret to sign x-proxy-signature header */\n proxySecret: string\n /** List of known proxies before current one from which IP can be extracted */\n knownProxies?: KnownProxies\n /**\n * Function to determine if a given IP address should be as x-forwarded-for header source.\n * Defaults to () => false, which means all IP addresses are trusted\n * */\n trustProxyFn?: TrustProxyFunction\n}\n\ntype LoggerType = {\n error: (data: unknown) => void\n}\n\ntype RelativeOrAbsoluteEndpoint = string\n\nexport type ProxyOptions = {\n /** Name of the proxy. Primarily used to set \"via\" header */\n name: string\n /** Proxy prefix which will be removed from request url */\n proxyPrefix: string\n /** Upstream host to proxy requests to */\n upstreamOrigin: string\n /** Upstream prefix to add to request url */\n upstreamPrefix: string\n /** IP proxying options, if specified, IP will be passed used signed x-proxy-id, x-proxy-ip, x-proxy-timestamp, x-proxy-signature headers */\n ipProxying?: IpProxyingOptions\n /** \n * Map of location header rewrites for redirects. \n * Key: upstream location, Value: rewritten location for client.\n * Used to rewrite Location headers in 3xx redirect responses.\n */\n locationRewrites?: Record<RelativeOrAbsoluteEndpoint, RelativeOrAbsoluteEndpoint>\n /** \n * Map of cookie path rewrites for Set-Cookie headers.\n * Key: upstream cookie path, Value: rewritten path for client.\n * Used to adjust cookie scope when proxying between different path prefixes.\n */\n cookiePathRewrites?: Record<RelativeOrAbsoluteEndpoint, RelativeOrAbsoluteEndpoint>\n /** \n * Logger instance for error reporting. Defaults to console if not provided.\n * Must implement an error method that accepts any data type.\n */\n logger?: LoggerType\n}\n\ntype ProxyHandler = (req: IncomingMessage, res: ServerResponse) => void\n\nexport function createProxy (options: ProxyOptions): ProxyHandler {\n const {\n name,\n proxyPrefix,\n upstreamOrigin,\n upstreamPrefix,\n ipProxying,\n locationRewrites,\n cookiePathRewrites,\n logger = console,\n } = options\n\n const proxy = httpProxy.createProxy({\n target: upstreamOrigin,\n changeOrigin: true,\n })\n\n const trustProxyFn = ipProxying?.trustProxyFn ?? (() => false)\n\n proxy.on('proxyReq', (proxyReq, req) => {\n if (req.url?.startsWith(proxyPrefix)) {\n proxyReq.path = upstreamPrefix + req.url.slice(proxyPrefix.length)\n }\n proxyReq.setHeader('via', name)\n\n if (req.url && req.method && ipProxying) {\n const ip = getRequestIp(req, trustProxyFn, ipProxying.knownProxies)\n const headers = getProxyHeadersForIp(req.method, proxyReq.path, ip, ipProxying.proxyId, ipProxying.proxySecret)\n for (const [headerName, headerValue] of Object.entries(headers)) {\n proxyReq.setHeader(headerName, headerValue)\n }\n }\n })\n proxy.on('proxyRes', (proxyRes, _req, _res) => {\n if (proxyRes.headers.location) {\n proxyRes.headers.location = replaceUpstreamEndpoint({\n endpoint: proxyRes.headers.location,\n proxyPrefix,\n upstreamPrefix,\n upstreamOrigin,\n rewrites: locationRewrites,\n })\n }\n\n // Handle Set-Cookie headers to rewrite cookie paths\n const setCookieHeaders = proxyRes.headers['set-cookie']\n if (setCookieHeaders) {\n proxyRes.headers['set-cookie'] = setCookieHeaders.map(cookieString => {\n return cookieString.replace(/;\\s*Path=([^;]+)/i, (match, pathValue) => {\n const rewrittenPath = replaceUpstreamEndpoint({\n endpoint: pathValue,\n proxyPrefix,\n upstreamPrefix,\n upstreamOrigin,\n rewrites: cookiePathRewrites,\n })\n return match.replace(pathValue, rewrittenPath)\n })\n })\n }\n })\n\n return function syncProxyHandler (req, res) {\n proxy.web(req, res, {}, (err) => {\n if (err) {\n // TODO: Add more complex loggers and standard error handling in next iterations\n logger.error({ msg: 'Proxy error', err })\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'application/json' })\n res.end(JSON.stringify({ errors: [{ message: 'Proxy error' }] }))\n } else {\n res.end()\n }\n }\n })\n }\n}"],"mappings":"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","names":["check","createChecker","check","createChecker","createChecker"]}