diff --git a/deps.ts b/deps.ts index fe02e00..f138d01 100644 --- a/deps.ts +++ b/deps.ts @@ -10,6 +10,7 @@ export { encodeUrl } from 'https://deno.land/x/encodeurl@1.0.0/mod.ts' export { charset, contentType, lookup } from 'https://deno.land/x/media_types@v2.6.1/mod.ts' export { default as rg } from 'https://esm.sh/regexparam' export { forwarded } from 'https://deno.land/x/forwarded@v0.0.2/mod.ts' +export * from 'https://deno.land/x/proxy_addr@v0.0.0/mod.ts' import type { ServerRequest as Req, Response as ServerResponse } from 'https://deno.land/std@0.88.0/http/server.ts' interface Res extends ServerResponse { diff --git a/extensions/req/security.ts b/extensions/req/security.ts index e0aabe9..5b501d5 100644 --- a/extensions/req/security.ts +++ b/extensions/req/security.ts @@ -1,7 +1,5 @@ -import { isIP } from '../../deps.ts' -import { Req } from '../../deps.ts' +import { isIP, Req, compile, all, proxyaddr } from '../../deps.ts' import { Protocol } from '../../types.ts' -import { compile, proxyaddr, all } from '../../utils/proxyAddr.ts' export const trustRemoteAddress = (req: Request) => { const val = (req.conn.remoteAddr as Deno.NetAddr).hostname diff --git a/utils/compileTrust.ts b/utils/compileTrust.ts index 79ebf81..0b98e65 100644 --- a/utils/compileTrust.ts +++ b/utils/compileTrust.ts @@ -1,4 +1,4 @@ -import { compile } from './proxyAddr.ts' +import { compile } from '../deps.ts' type TrustValue = ((...args: any[]) => any) | boolean | string | number | string[] @@ -7,9 +7,7 @@ export function compileTrust(value: TrustValue) { if (value === true) { // Support plain true / false - return function () { - return true - } + return () => true } if (typeof value === 'number') { diff --git a/utils/proxyAddr.ts b/utils/proxyAddr.ts deleted file mode 100644 index 4f23910..0000000 --- a/utils/proxyAddr.ts +++ /dev/null @@ -1,195 +0,0 @@ -// deno-lint-ignore-file - -import { Req } from '../deps.ts' -import { ipaddr, IPv4, IPv6, forwarded } from '../deps.ts' - -const DIGIT_REGEXP = /^[0-9]+$/ -const isip = ipaddr.isValid -const parseip = ipaddr.parse -/** - * Pre-defined IP ranges. - */ -const IP_RANGES: Record = { - linklocal: ['169.254.0.0/16', 'fe80::/10'], - loopback: ['127.0.0.1/8', '::1/128'], - uniquelocal: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fc00::/7'] -} -/** - * Get all addresses in the request, optionally stopping - * at the first untrusted. - * - * @param request - * @param trust - */ -function alladdrs(req: Req, trust: ((...args: any[]) => any) | any[] | string[] | string) { - // get addresses - - const addrs = forwarded(req) - - if (!trust) return addrs - - if (typeof trust !== 'function') trust = compile(trust) - - for (let i = 0; i < addrs.length - 1; i++) { - if (trust(addrs[i], i)) continue - addrs.length = i + 1 - } - return addrs -} -/** - * Compile argument into trust function. - * - * @param val - */ -function compile(val: string | string[]) { - let trust - if (typeof val === 'string') trust = [val] - else if (Array.isArray(val)) trust = val.slice() - else throw new TypeError('unsupported trust argument') - - for (let i = 0; i < trust.length; i++) { - val = trust[i] - - if (!Object.prototype.hasOwnProperty.call(IP_RANGES, val)) { - continue - } - - // Splice in pre-defined range - val = IP_RANGES[val] - trust.splice.apply(trust, [i, 1, ...val]) - i += val.length - 1 - } - - return compileTrust(compileRangeSubnets(trust)) -} -/** - * Compile `arr` elements into range subnets. - */ -function compileRangeSubnets(arr: any[]) { - const rangeSubnets = new Array(arr.length) - for (let i = 0; i < arr.length; i++) rangeSubnets[i] = parseIPNotation(arr[i]) - - return rangeSubnets -} -/** - * Compile range subnet array into trust function. - * - * @param rangeSubnets - */ -function compileTrust(rangeSubnets: any[]) { - // Return optimized function based on length - const len = rangeSubnets.length - return len === 0 ? trustNone : len === 1 ? trustSingle(rangeSubnets[0]) : trustMulti(rangeSubnets) -} -/** - * Parse IP notation string into range subnet. - * - * @param {String} note - * @private - */ -export function parseIPNotation(note: string) { - const pos = note.lastIndexOf('/') - const str = pos !== -1 ? note.substring(0, pos) : note - - if (!isip(str)) throw new TypeError('invalid IP address: ' + str) - - let ip = parseip(str) - - if (pos === -1 && ip.kind() === 'ipv6') { - ip = ip as IPv6 - - if (ip.isIPv4MappedAddress()) ip = ip.toIPv4Address() - } - - const max = ip.kind() === 'ipv6' ? 128 : 32 - - let range: string | number | null = pos !== -1 ? note.substring(pos + 1, note.length) : null - - if (range === null) range = max - else if (DIGIT_REGEXP.test(range)) range = parseInt(range, 10) - else if (ip.kind() === 'ipv4' && isip(range)) range = parseNetmask(range) - else range = null - - if (range && (range <= 0 || range > max)) throw new TypeError('invalid range on address: ' + note) - - return [ip, range] -} -/** - * Parse netmask string into CIDR range. - * - * @param netmask - * @private - */ -function parseNetmask(netmask: string) { - const ip = parseip(netmask) - return ip.kind() === 'ipv4' ? ip.prefixLengthFromSubnetMask() : null -} -/** - * Determine address of proxied request. - * - * @param request - * @param trust - * @public - */ -export function proxyaddr(req: Req, trust: ((...args: any[]) => any) | any[] | string[] | string) { - const addrs = alladdrs(req, trust) - - return addrs[addrs.length - 1] -} -/** - * Static trust function to trust nothing. - * - */ -const trustNone = () => false -/** - * Compile trust function for multiple subnets. - */ -function trustMulti(subnets: any[]) { - return function trust(addr: string) { - if (!isip(addr)) return false - const ip = parseip(addr) - let ipconv - const kind = ip.kind() - for (let i = 0; i < subnets.length; i++) { - const subnet = subnets[i] - const subnetip = subnet[0] - const subnetkind = subnetip.kind() - const subnetrange = subnet[1] - let trusted = ip - if (kind !== subnetkind) { - if (subnetkind === 'ipv4' && !(ip as IPv6).isIPv4MappedAddress()) continue - - if (!ipconv) ipconv = subnetkind === 'ipv4' ? (ip as IPv6).toIPv4Address() : (ip as IPv4).toIPv4MappedAddress() - - trusted = ipconv - } - if ((trusted as IPv4).match(subnetip, subnetrange)) return true - } - return false - } -} -/** - * Compile trust function for single subnet. - * - * @param subnet - */ -function trustSingle(subnet: any[]) { - const subnetip = subnet[0] - const subnetkind = subnetip.kind() - const subnetisipv4 = subnetkind === 'ipv4' - const subnetrange = subnet[1] - return function trust(addr: string) { - if (!isip(addr)) return false - let ip = parseip(addr) - const kind = ip.kind() - if (kind !== subnetkind) { - if (subnetisipv4 && !(ip as IPv6).isIPv4MappedAddress()) return false - - ip = subnetisipv4 ? (ip as IPv6).toIPv4Address() : (ip as IPv4).toIPv4MappedAddress() - } - return (ip as IPv6).match(subnetip, subnetrange) - } -} - -export { alladdrs as all } -export { compile }