@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.
75 lines (74 loc) • 3.15 kB
JavaScript
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);
}
;