@aikidosec/firewall
Version:
Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks
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);
}