UNPKG

@aikidosec/firewall

Version:

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

75 lines (74 loc) 3.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getContext = getContext; exports.updateContext = updateContext; exports.runWithContext = runWithContext; exports.bindContext = bindContext; const ContextStorage_1 = require("./context/ContextStorage"); const async_hooks_1 = require("async_hooks"); const Source_1 = require("./Source"); /** * Get the current request context that is being handled * * We don't want to allow the user to modify the context directly, so we use `Readonly<Context>` */ function getContext() { return ContextStorage_1.ContextStorage.getStore(); } function isSourceKey(key) { return Source_1.SOURCES.includes(key); } // We need to use a function to mutate the context because we need to clear the cache when the user input changes function updateContext(context, key, value) { context[key] = value; if (context.cache && isSourceKey(key)) { context.cache.delete(key); } } /** * Executes a function with a given request context * * The code executed inside the function will have access to the context using {@link getContext} * * This is needed because Node.js is single-threaded, so we can't use a global variable to store the context. */ function runWithContext(context, fn) { const current = ContextStorage_1.ContextStorage.getStore(); // If there is already a context, we just update it // In this way we don't lose the `attackDetected` flag if (current) { current.url = context.url; current.method = context.method; current.query = context.query; current.headers = context.headers; current.routeParams = context.routeParams; current.remoteAddress = context.remoteAddress; current.body = context.body; current.cookies = context.cookies; current.source = context.source; current.route = context.route; current.graphql = context.graphql; current.xml = context.xml; current.subdomains = context.subdomains; current.outgoingRequestRedirects = context.outgoingRequestRedirects; current.markUnsafe = context.markUnsafe; // Clear all the cached user input strings delete current.cache; return fn(); } // Cleanup lingering cache // In tests the context is often passed by reference // Make sure to clean up the cache before running the function delete context.cache; // If there's no context yet, we create a new context and run the function with it return ContextStorage_1.ContextStorage.run(context, fn); } /** * Binds the given function to the current execution context. * This fixes the issue that context is not available in event handlers that are called outside of runWithContext * Static method AsyncLocalStorage.bind(fn) was added in Node.js v19.8.0 and v18.16.0, so we can't use it yet, but it does the same thing. * Also done by OpenTelemetry: https://github.com/open-telemetry/opentelemetry-js/blob/a6020fb113a60ae6abc1aa925fa6744880e7fa15/api/src/api/context.ts#L86 */ function bindContext(fn) { return async_hooks_1.AsyncResource.bind(fn); }