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.

143 lines (142 loc) 5.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FunctionsFramework = void 0; exports.getFlushEveryMS = getFlushEveryMS; exports.getTimeoutInMS = getTimeoutInMS; exports.createCloudFunctionWrapper = createCloudFunctionWrapper; const AgentSingleton_1 = require("../agent/AgentSingleton"); const Context_1 = require("../agent/Context"); const wrapExport_1 = require("../agent/hooks/wrapExport"); const buildRouteFromURL_1 = require("../helpers/buildRouteFromURL"); const shouldDiscoverRoute_1 = require("./http-server/shouldDiscoverRoute"); const isPlainObject_1 = require("../helpers/isPlainObject"); function getFlushEveryMS() { if (process.env.AIKIDO_CLOUD_FUNCTION_FLUSH_EVERY_MS) { const parsed = parseInt(process.env.AIKIDO_CLOUD_FUNCTION_FLUSH_EVERY_MS, 10); // Minimum is 1 minute if (!isNaN(parsed) && parsed >= 60 * 1000) { return parsed; } } return 10 * 60 * 1000; // 10 minutes } function getTimeoutInMS() { if (process.env.AIKIDO_CLOUD_FUNCTION_TIMEOUT_MS) { const parsed = parseInt(process.env.AIKIDO_CLOUD_FUNCTION_TIMEOUT_MS, 10); // Minimum is 1 second if (!isNaN(parsed) && parsed >= 1000) { return parsed; } } return 1000; // 1 second } function createCloudFunctionWrapper(fn) { const agent = (0, AgentSingleton_1.getInstance)(); let lastFlushStatsAt = undefined; let startupEventSent = false; return async (req, res) => { // Send startup event on first invocation if (agent && !startupEventSent) { startupEventSent = true; try { await agent.onStart(getTimeoutInMS()); } catch (err) { // eslint-disable-next-line no-console console.error(`Aikido: Failed to start agent: ${err.message}`); } } const url = req.protocol + "://" + req.get("host") + req.originalUrl; return await (0, Context_1.runWithContext)({ method: req.method, remoteAddress: req.ip, body: req.body ? req.body : undefined, url: url, headers: req.headers, query: req.query, /* c8 ignore next */ cookies: req.cookies ? req.cookies : {}, routeParams: {}, source: "cloud-function/http", route: (0, buildRouteFromURL_1.buildRouteFromURL)(url), }, async () => { try { return await fn(req, res); } finally { const context = (0, Context_1.getContext)(); if (agent && context) { incrementStatsAndDiscoverAPISpec(context, agent, res.statusCode); await agent.getPendingEvents().waitUntilSent(getTimeoutInMS()); if (lastFlushStatsAt === undefined || lastFlushStatsAt + getFlushEveryMS() < performance.now()) { await agent.flushStats(getTimeoutInMS()); lastFlushStatsAt = performance.now(); } } } }); }; } function incrementStatsAndDiscoverAPISpec(context, agent, statusCode) { if (context.remoteAddress && agent.getConfig().isBypassedIP(context.remoteAddress)) { return; } if (context.route && context.method && Number.isInteger(statusCode)) { const shouldDiscover = (0, shouldDiscoverRoute_1.shouldDiscoverRoute)({ statusCode: statusCode, method: context.method, route: context.route, }); if (shouldDiscover) { agent.onRouteExecute(context); } if (context.remoteAddress && !context.blockedDueToIPOrBot && agent.getAttackWaveDetector().check(context)) { agent.onDetectedAttackWave({ request: context, }); agent.getInspectionStatistics().onAttackWaveDetected(); } } const stats = agent.getInspectionStatistics(); stats.onRequest(); } class FunctionsFramework { onRequire(exports, pkgInfo) { (0, wrapExport_1.wrapExport)(exports, "http", pkgInfo, { kind: undefined, modifyArgs: (args) => { if (args.length === 2 && typeof args[1] === "function") { const httpFunction = args[1]; args[1] = createCloudFunctionWrapper(httpFunction); } return args; }, }); } wrap(hooks) { hooks .addPackage("@google-cloud/functions-framework") .withVersion("^4.0.0 || ^3.0.0") .onRequire((exports, pkgInfo) => { this.onRequire(exports, pkgInfo); }) .addFileInstrumentation({ path: "build/src/function_registry.js", functions: [], accessLocalVariables: { names: ["module.exports"], cb: (vars, pkgInfo) => { if (vars.length > 0 && (0, isPlainObject_1.isPlainObject)(vars[0])) { const exports = vars[0]; this.onRequire(exports, pkgInfo); } }, }, }); } } exports.FunctionsFramework = FunctionsFramework;