UNPKG

@aikidosec/firewall

Version:

Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks

74 lines (73 loc) 3.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createRequestListener = createRequestListener; const Context_1 = require("../../agent/Context"); const isPackageInstalled_1 = require("../../helpers/isPackageInstalled"); const checkIfRequestIsBlocked_1 = require("./checkIfRequestIsBlocked"); const contextFromRequest_1 = require("./contextFromRequest"); const readBodyStream_1 = require("./readBodyStream"); const shouldDiscoverRoute_1 = require("./shouldDiscoverRoute"); function createRequestListener(listener, module, agent) { const isMicroInstalled = (0, isPackageInstalled_1.isPackageInstalled)("micro"); return async function requestListener(req, res) { // Parse body only if next or micro is installed // We can only read the body stream once // This is tricky, see replaceRequestBody(...) // e.g. Hono uses web requests and web streams // (uses Readable.toWeb(req) to convert to a web stream) const readBody = "NEXT_DEPLOYMENT_ID" in process.env || isMicroInstalled; if (!readBody) { return callListenerWithContext(listener, req, res, module, agent, ""); } const result = await (0, readBodyStream_1.readBodyStream)(req, res, agent); if (!result.success) { return; } return callListenerWithContext(listener, req, res, module, agent, result.body); }; } function callListenerWithContext(listener, req, res, module, agent, body) { const context = (0, contextFromRequest_1.contextFromRequest)(req, body, module); return (0, Context_1.runWithContext)(context, () => { // This method is called when the response is finished and discovers the routes for display in the dashboard // The bindContext function is used to ensure that the context is available in the callback // If using http2, the context is not available in the callback without this res.on("finish", (0, Context_1.bindContext)(createOnFinishRequestHandler(req, res, agent))); if ((0, checkIfRequestIsBlocked_1.checkIfRequestIsBlocked)(res, agent)) { // The return is necessary to prevent the listener from being called return; } return listener(req, res); }); } // Use symbol to avoid conflicts with other properties const countedRequest = Symbol("__zen_request_counted__"); function createOnFinishRequestHandler(req, res, agent) { return function onFinishRequest() { if (req[countedRequest]) { // The request has already been counted // This might happen if the server has multiple listeners return; } // Mark the request as counted req[countedRequest] = true; const context = (0, Context_1.getContext)(); if (context && context.route && context.method) { const shouldDiscover = (0, shouldDiscoverRoute_1.shouldDiscoverRoute)({ statusCode: res.statusCode, route: context.route, method: context.method, }); if (shouldDiscover) { agent.onRouteExecute(context); } if (shouldDiscover || context.rateLimitedEndpoint) { agent.getInspectionStatistics().onRequest(); } if (context.rateLimitedEndpoint) { agent.getInspectionStatistics().onRateLimitedRequest(); agent.onRouteRateLimited(context.rateLimitedEndpoint); } } }; }