@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
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);
}