@aikidosec/firewall
Version:
Zen by Aikido is an embedded Application Firewall that autonomously protects Node.js apps against common and critical attacks, provides rate limiting, detects malicious traffic (including bots), and more.
132 lines (131 loc) • 4.22 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUrlFromHTTPRequestArgs = getUrlFromHTTPRequestArgs;
const net_1 = require("net");
const tryParseURL_1 = require("../../helpers/tryParseURL");
const isOptionsObject_1 = require("./isOptionsObject");
/**
* Gets the url from the arguments of a node:http(s) outgoing request function call.
*/
function getUrlFromHTTPRequestArgs(args, module) {
if (!args || !args.length) {
return undefined;
}
let url;
// Url provided as a string
if (typeof args[0] === "string" && args[0].length > 0) {
url = (0, tryParseURL_1.tryParseURL)(args[0]);
}
// Url provided as a URL object
if (args[0] instanceof URL) {
url = args[0];
}
const requestOptions = getRequestOptions(args);
if (requestOptions) {
if (!url) {
return getUrlFromRequestOptions(requestOptions, module);
}
return mergeURLWithRequestOptions(requestOptions, url);
}
return url;
}
/**
* Request options can be provided as the first argument or as the second argument.
* But thy can also be not provided at all.
*/
function getRequestOptions(args) {
if ((0, isOptionsObject_1.isOptionsObject)(args[0]) && !(args[0] instanceof URL)) {
return args[0];
}
else if (args.length > 1 &&
(0, isOptionsObject_1.isOptionsObject)(args[1]) &&
!(args[1] instanceof URL)) {
return args[1];
}
return undefined;
}
/**
* Build a URL object from the outgoing http(s) request options.
*/
function getUrlFromRequestOptions(options, module) {
let str = "";
if (typeof options.protocol === "string") {
str += options.protocol;
}
else if (module) {
str += `${module}:`;
}
str += "//";
if (typeof options.hostname === "string") {
str += wrapWithSquareBracketsIfNeeded(options.hostname);
}
else if (typeof options.host === "string") {
str += wrapWithSquareBracketsIfNeeded(options.host);
}
if (options.port) {
if (typeof options.port === "number" && options.port > 0) {
str += `:${options.port}`;
}
if (typeof options.port === "string" && options.port.length > 0) {
str += `:${options.port}`;
}
}
if (typeof options.path === "string") {
str += options.path;
}
return (0, tryParseURL_1.tryParseURL)(str);
}
// Many HTTP clients pass the separate URL parts as properties of the options object, example:
// http.request({ protocol: 'http:', hostname: 'example.com', port: 80, path: '/path' })
//
// When you have an IPv6 address as the hostname, a client might pass it without square brackets:
// http.request({ hostname: '::', ... })
//
// When we reconstruct a URL from these options, we need to wrap the hostname in square brackets if it's an IPv6 address
// Otherwise the URL will be invalid
// http://:::80/path
// should be
// http://[::]:80/path
function wrapWithSquareBracketsIfNeeded(hostname) {
if ((0, net_1.isIPv6)(hostname)) {
return `[${hostname}]`;
}
return hostname;
}
/**
* Merge the url created from the request options with the url provided as a string or URL object.
* Node.js docs: If both url and options are specified, the objects are merged, with the options properties taking precedence.
*/
function mergeURLWithRequestOptions(options, url) {
let urlStr = "";
if (options.protocol) {
urlStr += options.protocol;
}
else {
urlStr += url.protocol;
}
urlStr += "//";
if (options.hostname) {
urlStr += wrapWithSquareBracketsIfNeeded(options.hostname);
}
else if (options.host) {
urlStr += wrapWithSquareBracketsIfNeeded(options.host);
}
else {
urlStr += wrapWithSquareBracketsIfNeeded(url.hostname);
}
if (options.port) {
urlStr += `:${options.port}`;
}
else if (url.port) {
urlStr += `:${url.port}`;
}
if (options.path) {
// Include the query string and hash
urlStr += options.path;
}
else {
urlStr += url.pathname + url.search;
}
return (0, tryParseURL_1.tryParseURL)(urlStr);
}
;