@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.
97 lines (96 loc) • 4.4 kB
JavaScript
;
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");
const colorText_1 = require("../helpers/colorText");
const warnBox_1 = require("../helpers/warnBox");
const isMusl_1 = require("../helpers/isMusl");
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");
let binaryPath = (0, node_path_1.join)(nodeInternalsDir, `zen-internals-node-${platform}-${arch}-node${majorVersion}.node`);
if ((0, isMusl_1.isMusl)()) {
binaryPath = (0, node_path_1.join)(nodeInternalsDir, `zen-internals-node-${platform}-${arch}-musl-node${majorVersion}.node`);
}
if (!(0, node_fs_1.existsSync)(binaryPath)) {
// oxlint-disable-next-line no-console
console.warn((0, colorText_1.colorText)("red", (0, warnBox_1.warnBox)(`Zen will NOT block code injection attacks (eval, new Function). Cannot find native addon for Node.js ${majorVersion} on ${platform}-${arch}. Request support: https://github.com/AikidoSec/firewall-node/issues`)));
return;
}
let bindings;
try {
bindings = require(binaryPath);
}
catch (error) {
// oxlint-disable-next-line no-console
console.warn((0, colorText_1.colorText)("red", (0, warnBox_1.warnBox)(`Zen will NOT block code injection attacks (eval, new Function). Failed to load native addon for Node.js ${majorVersion} on ${platform}-${arch}: ${error.message}`)));
return;
}
if (!bindings || typeof bindings.setCodeGenerationCallback !== "function") {
// oxlint-disable-next-line no-console
console.warn((0, colorText_1.colorText)("red", (0, warnBox_1.warnBox)(`Zen will NOT block code injection attacks (eval, new Function). Native addon for Node.js ${majorVersion} on ${platform}-${arch} is invalid.`)));
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;