UNPKG

request-filtering-agent

Version:

An http(s).Agent implementation that block request Private IP address.

209 lines 9.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.useAgent = exports.globalHttpsAgent = exports.globalHttpAgent = exports.RequestFilteringHttpsAgent = exports.RequestFilteringHttpAgent = exports.DefaultRequestFilteringAgentOptions = void 0; const net = __importStar(require("net")); const http = __importStar(require("http")); const https = __importStar(require("https")); const ipaddr_js_1 = __importDefault(require("ipaddr.js")); const dns = __importStar(require("dns")); exports.DefaultRequestFilteringAgentOptions = { allowPrivateIPAddress: false, allowMetaIPAddress: false, allowIPAddressList: [], denyIPAddressList: [] }; /** * validate the address that is matched the validation options * @param address ip address * @param host optional * @param family optional * @param options */ const validateIPAddress = ({ address, host, family }, options) => { // if it is not IP address, skip it if (net.isIP(address) === 0) { return; } try { const addr = ipaddr_js_1.default.parse(address); const range = addr.range(); // prefer allowed list if (options.allowIPAddressList.length > 0 && options.allowIPAddressList.includes(address)) { return; } if (!options.allowMetaIPAddress) { // address === "0.0.0.0" || address == "::" if (range === "unspecified") { return new Error(`DNS lookup ${address}(family:${family}, host:${host}) is not allowed. Because, It is meta IP address.`); } } // TODO: rename option name if (!options.allowPrivateIPAddress && range !== "unicast") { return new Error(`DNS lookup ${address}(family:${family}, host:${host}) is not allowed. Because, It is private IP address.`); } if (options.denyIPAddressList.length > 0 && options.denyIPAddressList.includes(address)) { return new Error(`DNS lookup ${address}(family:${family}, host:${host}) is not allowed. Because It is defined in denyIPAddressList.`); } } catch (error) { return error; // if can not parse IP address, throw error } return; }; const makeLookup = (createConnectionOptions, requestFilterOptions) => { // @ts-expect-error - @types/node has a poor definition of this callback return (hostname, options, cb) => { const lookup = createConnectionOptions.lookup || dns.lookup; let lookupCb; if (options.all) { lookupCb = ((err, addresses) => { if (err) { cb(err); return; } for (const { address, family } of addresses) { const validationError = validateIPAddress({ address, family, host: hostname }, requestFilterOptions); if (validationError) { cb(validationError); return; } } cb(null, addresses); }); } else { lookupCb = ((err, address, family) => { if (err) { cb(err); return; } const validationError = validateIPAddress({ address: address, family: family, host: hostname }, requestFilterOptions); if (validationError) { cb(validationError); return; } cb(null, address, family); }); } // @ts-expect-error - @types/node has a poor definition of this callback lookup(hostname, options, lookupCb); }; }; /** * A subclass of http.Agent with request filtering */ class RequestFilteringHttpAgent extends http.Agent { requestFilterOptions; constructor(options) { super(options); this.requestFilterOptions = { allowPrivateIPAddress: options && options.allowPrivateIPAddress !== undefined ? options.allowPrivateIPAddress : exports.DefaultRequestFilteringAgentOptions.allowPrivateIPAddress, allowMetaIPAddress: options && options.allowMetaIPAddress !== undefined ? options.allowMetaIPAddress : exports.DefaultRequestFilteringAgentOptions.allowMetaIPAddress, allowIPAddressList: options && options.allowIPAddressList ? options.allowIPAddressList : exports.DefaultRequestFilteringAgentOptions.allowIPAddressList, denyIPAddressList: options && options.denyIPAddressList ? options.denyIPAddressList : exports.DefaultRequestFilteringAgentOptions.denyIPAddressList }; } // override http.Agent#createConnection // https://nodejs.org/api/http.html#http_agent_createconnection_options_callback // https://nodejs.org/api/net.html#net_net_createconnection_options_connectlistener createConnection(options, connectionListener) { const { host } = options; if (host !== undefined) { // Direct ip address request without dns-lookup // Example: http://127.0.0.1 // https://nodejs.org/api/net.html#net_socket_connect_options_connectlistener const validationError = validateIPAddress({ address: host }, this.requestFilterOptions); if (validationError) { throw validationError; } } // https://nodejs.org/api/net.html#net_socket_connect_options_connectlistener // @ts-expect-error - @types/node does not defined createConnection return super.createConnection({ ...options, lookup: makeLookup(options, this.requestFilterOptions) }, connectionListener); } } exports.RequestFilteringHttpAgent = RequestFilteringHttpAgent; /** * A subclass of https.Agent with request filtering */ class RequestFilteringHttpsAgent extends https.Agent { requestFilterOptions; constructor(options) { super(options); this.requestFilterOptions = { allowPrivateIPAddress: options && options.allowPrivateIPAddress !== undefined ? options.allowPrivateIPAddress : false, allowMetaIPAddress: options && options.allowMetaIPAddress !== undefined ? options.allowMetaIPAddress : false, allowIPAddressList: options && options.allowIPAddressList ? options.allowIPAddressList : [], denyIPAddressList: options && options.denyIPAddressList ? options.denyIPAddressList : [] }; } // override http.Agent#createConnection // https://nodejs.org/api/http.html#http_agent_createconnection_options_callback // https://nodejs.org/api/net.html#net_net_createconnection_options_connectlistener createConnection(options, connectionListener) { const { host } = options; if (host !== undefined) { // Direct ip address request without dns-lookup // Example: http://127.0.0.1 // https://nodejs.org/api/net.html#net_socket_connect_options_connectlistener const validationError = validateIPAddress({ address: host }, this.requestFilterOptions); if (validationError) { throw validationError; } } // https://nodejs.org/api/net.html#net_socket_connect_options_connectlistener // @ts-expect-error - @types/node does not defined createConnection return super.createConnection({ ...options, lookup: makeLookup(options, this.requestFilterOptions) }, connectionListener); } } exports.RequestFilteringHttpsAgent = RequestFilteringHttpsAgent; exports.globalHttpAgent = new RequestFilteringHttpAgent(); exports.globalHttpsAgent = new RequestFilteringHttpsAgent(); /** * Get an agent for the url * return http or https agent * @param url * @param options */ const useAgent = (url, options) => { if (!options) { return url.startsWith("https") ? exports.globalHttpsAgent : exports.globalHttpAgent; } return url.startsWith("https") ? new RequestFilteringHttpsAgent(options) : new RequestFilteringHttpAgent(options); }; exports.useAgent = useAgent; //# sourceMappingURL=request-filtering-agent.js.map