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.

83 lines (82 loc) 3.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FunctionSink = void 0; const node_path_1 = require("node:path"); const AgentSingleton_1 = require("../agent/AgentSingleton"); const Context_1 = require("../agent/Context"); const wrapExport_1 = require("../agent/hooks/wrapExport"); const envToBool_1 = require("../helpers/envToBool"); const getLibraryRoot_1 = require("../helpers/getLibraryRoot"); const getNodeVersion_1 = require("../helpers/getNodeVersion"); const checkContextForJsInjection_1 = require("../vulnerabilities/js-injection/checkContextForJsInjection"); const node_fs_1 = require("node:fs"); class FunctionSink { inspectFunction(args) { if (args.length === 0) { return undefined; } const code = args[0]; if (!code || typeof code !== "string") { return undefined; } const context = (0, Context_1.getContext)(); if (!context) { return undefined; } return (0, checkContextForJsInjection_1.checkContextForJsInjection)({ js: code, operation: "new Function/eval", context, }); } loadNativeAddon() { const majorVersion = (0, getNodeVersion_1.getMajorNodeVersion)(); const arch = process.arch; const platform = process.platform; const nodeInternalsDir = (0, node_path_1.join)((0, getLibraryRoot_1.getLibraryRoot)(), "node_internals"); const binaryPath = (0, node_path_1.join)(nodeInternalsDir, `zen-internals-node-${platform}-${arch}-node${majorVersion}.node`); if (!(0, node_fs_1.existsSync)(binaryPath)) { // oxlint-disable-next-line no-console console.warn(`AIKIDO: Cannot find native addon for Node.js ${majorVersion} on ${platform}-${arch}. Code injection attacks via eval() and new Function() will not be blocked. You can request support at https://github.com/AikidoSec/firewall-node/issues`); return; } const bindings = require(binaryPath); if (!bindings || typeof bindings.setCodeGenerationCallback !== "function") { // oxlint-disable-next-line no-console console.warn(`AIKIDO: Native addon for Node.js ${majorVersion} on ${platform}-${arch} is invalid. Function sink will not be instrumented.`); return; } return bindings; } wrap(_) { if ((0, envToBool_1.envToBool)(process.env.AIKIDO_DISABLE_CODE_GENERATION_HOOK)) { return; } const bindings = this.loadNativeAddon(); if (!bindings) { return; } bindings.setCodeGenerationCallback((code) => { const agent = (0, AgentSingleton_1.getInstance)(); if (!agent) { return; } const context = (0, Context_1.getContext)(); if (!context) { return; } try { (0, wrapExport_1.inspectArgs)([code], this.inspectFunction, context, agent, { name: "Function/eval", type: "global", }, "<compile>", "eval_op"); } catch (error) { // In blocking mode, onInspectionInterceptorResult would throw to block the operation // To block the code generation, we need to return a string that will be used for the thrown error message return error.message; } }); } } exports.FunctionSink = FunctionSink;