directus
1import type { Agent, ClientRequestArgs } from 'node:http';2import { isIP } from 'node:net';3import { isDeniedIp } from './is-denied-ip.js';4
5/**
6* 'createConnection' is missing in 'Agent' type, but assigned in actual implementation:
7* https://github.com/nodejs/node/blob/8a41d9b636be86350cd32847c3f89d327c4f6ff7/lib/_http_agent.js#L215
8*/
9export type _Agent = Agent & { createConnection: ClientRequestArgs['createConnection'] };10
11const deniedError = (domain: string) => new Error(`Requested domain "${domain}" resolves to a denied IP address`);12
13/** Extends a HTTP agent with IP validation */
14export const agentWithIpValidation = (agent: Agent) => {15const _agent = agent as _Agent;16
17const { createConnection } = _agent;18
19_agent.createConnection = function (options, oncreate) {20const { host } = options;21
22/*23* Unexpected, but according to the types 'host' might be undefined.
24* In that case, the request is denied to be on the safe side,
25* since the host cannot be verified.
26*/
27if (!host) {28throw new Error('Request cannot be verified due to missing host');29}30
31/*32* At this point, host is only verified if it's already an IP address.
33* Otherwise it will be verified on 'lookup' event.
34*/
35if (isIP(host) !== 0 && isDeniedIp(host)) throw deniedError(host);36
37const socket = createConnection?.call(this, options, oncreate);38
39// Unexpected, but in that case the request is denied to be on the safe side40if (!socket) {41throw new Error('Request cannot be verified due to lost socket');42}43
44// Emitted after resolving the host name but before connecting.45socket.on('lookup', (error, address) => {46if (error || !isDeniedIp(address)) return;47
48return socket.destroy(deniedError(host));49});50
51return socket;52};53
54return agent;55};56