| 46 | } | 50 | } |
| 47 | | 51 | |
| 48 | /** | 52 | /** |
| | 53 | * Checks if a string is a valid hostname according to RFC 1123 |
| | 54 | * @param {string} hostname The string to test |
| | 55 | * @returns {boolean} True if the string is a valid hostname |
| | 56 | */ |
| | 57 | function isValidHostname(hostname) { |
| | 58 | const hostnameRegex = /^(([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9])$/i; |
| | 59 | return hostnameRegex.test(hostname); |
| | 60 | } |
| | 61 | |
| | 62 | /** |
| | 63 | * Checks if a string is an IP address, CIDR notation, or IP wildcard |
| | 64 | * @param {string} entry The string to test |
| | 65 | * @returns {boolean} True if the string matches any IP format |
| | 66 | */ |
| | 67 | function isIpFormat(entry) { |
| | 68 | // Match CIDR notation (e.g. 192.168.0.0/24) |
| | 69 | if (entry.includes('/')) { |
| | 70 | return true; |
| | 71 | } |
| | 72 | |
| | 73 | // Match exact IP address |
| | 74 | if (ipRegex({ exact: true }).test(entry)) { |
| | 75 | return true; |
| | 76 | } |
| | 77 | |
| | 78 | // Match IPv4 with wildcards (e.g. 192.168.*.* or 192.168.0.*) |
| | 79 | const ipWildcardRegex = /^(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/; |
| | 80 | return ipWildcardRegex.test(entry); |
| | 81 | } |
| | 82 | |
| | 83 | /** |
| | 84 | * Validate the IP addresses in the whitelist. |
| | 85 | * Hostnames are resolved to IP addresses. |
| | 86 | */ |
| | 87 | async function resolveHostnames() { |
| | 88 | for (let i = 0; i < whitelist.length; i++) { |
| | 89 | try { |
| | 90 | const entry = whitelist[i]; |
| | 91 | |
| | 92 | // Skip if entry appears to be an IP address, CIDR notation, or IP wildcard |
| | 93 | if (isIpFormat(entry)) { |
| | 94 | continue; |
| | 95 | } |
| | 96 | |
| | 97 | if (isValidHostname(entry)) { |
| | 98 | const result = await dns.promises.lookup(entry); |
| | 99 | |
| | 100 | if (result.address) { |
| | 101 | console.info('Resolved whitelist hostname', entry, 'to IP address', result.address); |
| | 102 | whitelist[i] = result.address; |
| | 103 | } |
| | 104 | } |
| | 105 | } catch { |
| | 106 | // Ignore errors when resolving hostnames |
| | 107 | } |
| | 108 | } |
| | 109 | } |
| | 110 | |
| | 111 | /** |
| 49 | * Returns a middleware function that checks if the client IP is in the whitelist. | 112 | * Returns a middleware function that checks if the client IP is in the whitelist. |
| 50 | * @returns {import('express').RequestHandler} The middleware function | 113 | * @returns {import('express').RequestHandler} The middleware function |
| 51 | */ | 114 | */ |