UNPKG

@aikidosec/firewall

Version:

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

129 lines (128 loc) 4.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createLambdaWrapper = createLambdaWrapper; const AgentSingleton_1 = require("../agent/AgentSingleton"); const Context_1 = require("../agent/Context"); const isJsonContentType_1 = require("../helpers/isJsonContentType"); const isPlainObject_1 = require("../helpers/isPlainObject"); const parseCookies_1 = require("../helpers/parseCookies"); function isAsyncHandler(handler) { return handler.length <= 2; } function convertToAsyncFunction(originalHandler) { return async (event, context) => { if (isAsyncHandler(originalHandler)) { return originalHandler(event, context); } return new Promise((resolve, reject) => { try { originalHandler(event, context, (error, result) => { if (error) { reject(error); } else { resolve(result); } }); } catch (error) { reject(error); } }); }; } function normalizeHeaders(headers) { const normalized = {}; for (const key in headers) { normalized[key.toLowerCase()] = headers[key]; } return normalized; } function tryParseAsJSON(json) { try { return JSON.parse(json); } catch { return undefined; } } function parseBody(event) { const headers = event.headers ? normalizeHeaders(event.headers) : {}; if (!event.body || !(0, isJsonContentType_1.isJsonContentType)(headers["content-type"] || "")) { return undefined; } return tryParseAsJSON(event.body); } function isGatewayEvent(event) { return (0, isPlainObject_1.isPlainObject)(event) && "httpMethod" in event && "headers" in event; } function isSQSEvent(event) { return (0, isPlainObject_1.isPlainObject)(event) && "Records" in event; } // eslint-disable-next-line max-lines-per-function function createLambdaWrapper(handler) { const asyncHandler = convertToAsyncFunction(handler); const agent = (0, AgentSingleton_1.getInstance)(); let lastFlushStatsAt = undefined; const flushEveryMS = 10 * 60 * 1000; // eslint-disable-next-line max-lines-per-function return async (event, context) => { var _a, _b, _c; let agentContext = undefined; if (isSQSEvent(event)) { const body = event.Records.map((record) => tryParseAsJSON(record.body)).filter((body) => body); agentContext = { url: undefined, method: undefined, remoteAddress: undefined, body: { Records: body.map((record) => ({ body: record, })), }, routeParams: {}, headers: {}, query: {}, cookies: {}, source: "lambda/sqs", route: undefined, }; } else if (isGatewayEvent(event)) { agentContext = { url: undefined, method: event.httpMethod, remoteAddress: (_b = (_a = event.requestContext) === null || _a === void 0 ? void 0 : _a.identity) === null || _b === void 0 ? void 0 : _b.sourceIp, body: parseBody(event), headers: event.headers, routeParams: event.pathParameters ? event.pathParameters : {}, query: event.queryStringParameters ? event.queryStringParameters : {}, cookies: ((_c = event.headers) === null || _c === void 0 ? void 0 : _c.cookie) ? (0, parseCookies_1.parse)(event.headers.cookie) : {}, source: "lambda/gateway", route: event.resource ? event.resource : undefined, }; } if (!agentContext) { // We don't know what the type of the event is // We can't provide any context for the underlying sinks // So we just run the handler without any context return await asyncHandler(event, context); } try { return await (0, Context_1.runWithContext)(agentContext, async () => { return await asyncHandler(event, context); }); } finally { if (agent) { const stats = agent.getInspectionStatistics(); stats.onRequest(); if (lastFlushStatsAt === undefined || lastFlushStatsAt + flushEveryMS < performance.now()) { await agent.flushStats(1000); lastFlushStatsAt = performance.now(); } } } }; }