UNPKG

@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.

82 lines (81 loc) 3.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getHostnameAndPortFromArgs = getHostnameAndPortFromArgs; const getPortFromURL_1 = require("../../helpers/getPortFromURL"); const normalizeHostname_1 = require("../../helpers/normalizeHostname"); const tryParseURL_1 = require("../../helpers/tryParseURL"); const isOptionsObject_1 = require("../http-request/isOptionsObject"); /** * Extract hostname and port from the arguments of a undici request. * Used for SSRF detection. */ function getHostnameAndPortFromArgs(args) { let url; if (args.length > 0) { // URL provided as a string if (typeof args[0] === "string" && args[0].length > 0) { url = (0, tryParseURL_1.tryParseURL)(args[0]); } // Fetch accepts any object with a stringifier. User input may be an array if the user provides an array // query parameter (e.g., ?example[0]=https://example.com/) in frameworks like Express. Since an Array has // a default stringifier, this is exploitable in a default setup. // The following condition ensures that we see the same value as what's passed down to the sink. if (Array.isArray(args[0])) { url = (0, tryParseURL_1.tryParseURL)(args[0].toString()); } // URL provided as a URL object if (args[0] instanceof URL) { url = args[0]; } // If url is not undefined, extract the hostname and port if (url && url.hostname.length > 0) { return { hostname: (0, normalizeHostname_1.normalizeHostname)(url.hostname), port: (0, getPortFromURL_1.getPortFromURL)(url), }; } // Check if it can be a request options object if ((0, isOptionsObject_1.isOptionsObject)(args[0])) { return parseOptionsObject(args[0]); } } return undefined; } /** * Parse a undici request options object to extract hostname and port. */ function parseOptionsObject(obj) { // Origin is preferred over hostname // See https://github.com/nodejs/undici/blob/c926a43ac5952b8b5a6c7d15529b56599bc1b762/lib/core/util.js#L177 // oxlint-disable-next-line eqeqeq if (obj.origin != null && typeof obj.origin === "string") { const url = (0, tryParseURL_1.tryParseURL)(obj.origin); if (url) { return { hostname: (0, normalizeHostname_1.normalizeHostname)(url.hostname), port: (0, getPortFromURL_1.getPortFromURL)(url), }; } // Undici should throw an error if the origin is not a valid URL return undefined; } let port = 80; if (typeof obj.protocol === "string") { port = obj.protocol === "https:" ? 443 : 80; } if (typeof obj.port === "number") { port = obj.port; } else if (typeof obj.port === "string" && Number.isInteger(parseInt(obj.port, 10))) { port = parseInt(obj.port, 10); } // hostname is required by undici and host is not supported if (typeof obj.hostname !== "string" || obj.hostname.length === 0) { return undefined; } return { hostname: (0, normalizeHostname_1.normalizeHostname)(obj.hostname.toLowerCase()), port, }; }