[verified] refactor: harden operations and redesign device console
This commit is contained in:
+24
-1
@@ -1,10 +1,26 @@
|
||||
import { ConfigValidationError } from './errors.js'
|
||||
import { isIP } from 'node:net'
|
||||
|
||||
export function normalizeBaseUrl(rawUrl) {
|
||||
let url
|
||||
try { url = new URL(rawUrl) } catch { throw new ConfigValidationError('instance URL is invalid') }
|
||||
if (!['http:', 'https:'].includes(url.protocol)) throw new ConfigValidationError('instance URL must use http or https')
|
||||
if (url.username || url.password) throw new ConfigValidationError('instance URL must not contain credentials')
|
||||
const hostname = url.hostname.replace(/^\[|\]$/g, '').toLowerCase()
|
||||
const ipv4 = hostname.split('.').map(Number)
|
||||
const isIpv4 = ipv4.length === 4 && ipv4.every(part => Number.isInteger(part) && part >= 0 && part <= 255)
|
||||
const prohibitedIpv4 = isIpv4 && (
|
||||
ipv4[0] === 127 || ipv4[0] === 0 ||
|
||||
(ipv4[0] === 169 && ipv4[1] === 254) ||
|
||||
(ipv4[0] >= 224)
|
||||
)
|
||||
const prohibitedIpv6 = hostname === '::' || hostname === '::1' || /^fe[89ab][0-9a-f]:/.test(hostname) || hostname.startsWith('ff') || hostname.startsWith('::ffff:') || hostname.startsWith('64:ff9b:')
|
||||
const prohibitedMetadata = hostname === '100.100.100.200' || hostname === '168.63.129.16'
|
||||
const prohibitedTransition = hostname.startsWith('2002:')
|
||||
const isIpLiteral = isIP(hostname) !== 0
|
||||
if (!isIpLiteral || prohibitedIpv4 || prohibitedIpv6 || prohibitedMetadata || prohibitedTransition) {
|
||||
throw new ConfigValidationError('instance target must be a permitted IP literal; hostnames and local/metadata addresses are prohibited')
|
||||
}
|
||||
url.hash = ''
|
||||
url.search = ''
|
||||
return url.toString().replace(/\/$/, '')
|
||||
@@ -38,8 +54,15 @@ export function normalizeConfig(raw = {}, env = process.env) {
|
||||
}
|
||||
const port = Number(env.PORT ?? raw.server?.port ?? 8788)
|
||||
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new ConfigValidationError('server.port must be an integer from 1 to 65535')
|
||||
const host = String(env.HOST ?? raw.server?.host ?? '127.0.0.1').trim()
|
||||
const bareHost = host.replace(/^\[|\]$/g, '').toLowerCase()
|
||||
const octets = bareHost.split('.')
|
||||
const ipv4Loopback = octets.length === 4 && octets[0] === '127' && octets.every(part => /^\d{1,3}$/.test(part) && Number(part) <= 255)
|
||||
if (!(bareHost === 'localhost' || bareHost === '::1' || bareHost === '0:0:0:0:0:0:0:1' || ipv4Loopback)) {
|
||||
throw new ConfigValidationError('server.host must be a loopback host (127/8, ::1, or localhost)')
|
||||
}
|
||||
return {
|
||||
server: { host: raw.server?.host ?? env.HOST ?? '127.0.0.1', port },
|
||||
server: { host, port },
|
||||
instances,
|
||||
raw: { ...raw, instances },
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user