@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.
89 lines (88 loc) • 3.33 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetch = fetch;
const http_1 = require("http");
const https_1 = require("https");
const zlib_1 = require("zlib");
const AgentSingleton_1 = require("../agent/AgentSingleton");
const getPortFromURL_1 = require("./getPortFromURL");
function request({ url, method, body, headers, signal, agent, }) {
const request = url.protocol === "https:" ? https_1.request : http_1.request;
trackRequest(url);
return new Promise((resolve, reject) => {
// Convert URL object to string for compatibility with old https-proxy-agent versions
// Old agent-base library (used by https-proxy-agent) only works with string URLs
// and fails when passed URL objects, causing communication with our dashboard to fail
const req = request(url.toString(), {
method,
headers,
signal,
agent,
}, (response) => {
let stream = response;
if (response.headers["content-encoding"] === "gzip") {
const gunzip = (0, zlib_1.createGunzip)();
stream = response.pipe(gunzip);
}
let data = "";
stream.on("data", (chunk) => {
data += chunk;
});
stream.on("end", () => {
// We don't throw errors unless the request times out, is aborted or fails for low level reasons
// Error objects are annoying to work with
// That's why we use `resolve` instead of `reject`
resolve({
body: data,
statusCode: response.statusCode || 0,
});
});
});
req.on("error", (error) => {
reject(error);
});
req.end(body);
});
}
async function fetch({ url, method = "GET", headers = {}, body = "", timeoutInMS = 5000, agent, }) {
const abort = new AbortController();
return await Promise.race([
request({
url,
method,
headers,
signal: abort.signal,
body,
agent,
}),
new Promise((_, reject) => {
const timeout = setTimeout(() => {
abort.abort();
reject(new Error(`Request to ${url.toString()} timed out after ${timeoutInMS}ms`));
}, timeoutInMS);
// We don't want to keep Node.js running because of this timeout
timeout.unref();
}),
]);
}
// Add our own requests as outbound connections (Heartbeats, realtime polling, etc.)
// Only for new instrumentation (see below)
function trackRequest(url) {
const agent = (0, AgentSingleton_1.getInstance)();
if (!agent) {
// This should not happen
return;
}
// If the old (non ESM) hook system is used, the fetch function used
// here is already a patched version that tracks requests.
// If the new hook system is used, the import is executed before
// the hooks are applied, so we need to track the request here.
if (!agent.isUsingNewInstrumentation()) {
return;
}
const port = (0, getPortFromURL_1.getPortFromURL)(url);
if (!port) {
return;
}
agent.onConnectHostname(url.hostname, port);
}