request-filtering-agent
Version:
An http(s).Agent implementation that block request Private IP address.
209 lines • 9.4 kB
JavaScript
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
;