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.

141 lines (140 loc) 6.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.onModuleLoad = onModuleLoad; const getModuleInfoFromPath_1 = require("../getModuleInfoFromPath"); const isBuiltinModule_1 = require("../isBuiltinModule"); const getPackageVersionFromPath_1 = require("./getPackageVersionFromPath"); const codeTransformation_1 = require("./codeTransformation"); const instructions_1 = require("./instructions"); const removeNodePrefix_1 = require("../../../helpers/removeNodePrefix"); const AgentSingleton_1 = require("../../AgentSingleton"); const module_1 = require("module"); const processGetBuiltin_1 = require("./processGetBuiltin"); const wrapBuiltinExports_1 = require("./wrapBuiltinExports"); const builtinPatchedSymbol = Symbol("zen.instrumentation.builtin.patched"); function onModuleLoad(path, context, previousLoadResult) { var _a; try { // Ignore unsupported formats, e.g. wasm, native addons or json if ( // Sometimes the format is not set! previousLoadResult.format && ![ "builtin", "commonjs", "module", "commonjs-typescript", "module-typescript", ].includes(previousLoadResult.format)) { return previousLoadResult; } // The previousLoadResult.format must not be 'builtin' if it e.g. was modified by import-in-the-middle // import-in-the-middle returns 'module' for builtins that it patched to make the modification of builtins possible // The returned source is ignored if the format is 'builtin' const isBuiltin = previousLoadResult.format === "builtin" || (0, isBuiltinModule_1.isBuiltinModule)(path); // For Node.js builtin modules if (isBuiltin) { return patchBuiltin(path, previousLoadResult); } if (isSelfCheckImport(path)) { return updateSelfCheckSource(previousLoadResult); } return patchPackage(path, previousLoadResult); } catch (error) { // Do not break the module loading process, just log the error if (error instanceof Error) { (_a = (0, AgentSingleton_1.getInstance)()) === null || _a === void 0 ? void 0 : _a.onFailedToWrapModule(path, error); } return previousLoadResult; } } function patchPackage(path, previousLoadResult) { var _a, _b, _c; const moduleInfo = (0, getModuleInfoFromPath_1.getModuleInfoFromPath)(path); if (!moduleInfo) { // This is e.g. the case for user code (not a dependency) // We don't want to modify user code yet return previousLoadResult; } // Check if the version of the package is supported const pkgVersion = (0, getPackageVersionFromPath_1.getPackageVersionFromPath)(moduleInfo.base); if (!pkgVersion) { // We can't determine the version of the package return previousLoadResult; } (_a = (0, AgentSingleton_1.getInstance)()) === null || _a === void 0 ? void 0 : _a.onPackageRequired(moduleInfo.name, pkgVersion); if (!(0, instructions_1.shouldPatchPackage)(moduleInfo.name)) { // We don't want to modify this module return previousLoadResult; } if (!(0, instructions_1.shouldPatchFile)(moduleInfo.name, moduleInfo.path)) { // We don't want to patch this file return previousLoadResult; } // Right now we only allow one matching instruction set for one file // So if e.g. multiple version are matching, we only use the first one const matchingInstructions = (0, instructions_1.getPackageFileInstrumentationInstructions)(moduleInfo.name, pkgVersion, moduleInfo.path); // Report to the agent that the package was wrapped or not if it's version is not supported (_b = (0, AgentSingleton_1.getInstance)()) === null || _b === void 0 ? void 0 : _b.onPackageWrapped(moduleInfo.name, { version: pkgVersion, supported: !!matchingInstructions, }); if (!matchingInstructions) { // We don't want to patch this package version or file return previousLoadResult; } const pkgLoadFormat = (_c = previousLoadResult.format) !== null && _c !== void 0 ? _c : "unambiguous"; const sourceString = typeof previousLoadResult.source === "string" ? previousLoadResult.source : new TextDecoder("utf-8").decode(previousLoadResult.source); const newSource = (0, codeTransformation_1.transformCode)(moduleInfo.name, pkgVersion, path, sourceString, pkgLoadFormat, matchingInstructions); // Prevent returning empty or undefined source text if (!newSource) { return previousLoadResult; } return { format: previousLoadResult.format, shortCircuit: previousLoadResult.shortCircuit, source: newSource, }; } function patchBuiltin(builtinName, previousLoadResult) { const builtinNameWithoutPrefix = (0, removeNodePrefix_1.removeNodePrefix)(builtinName); const builtin = (0, instructions_1.shouldPatchBuiltin)(builtinNameWithoutPrefix); if (!builtin) { return previousLoadResult; } let orig = (0, processGetBuiltin_1.getBuiltinModuleWithoutPatching)(builtinName); if (!orig) { return previousLoadResult; } if (orig[builtinPatchedSymbol]) { // The builtin module has already been patched, so we don't need to do it again return previousLoadResult; } const newExports = (0, wrapBuiltinExports_1.wrapBuiltinExports)(builtinNameWithoutPrefix, orig); if (!newExports) { return previousLoadResult; } orig = newExports; (0, module_1.syncBuiltinESMExports)(); // Mark the builtin as patched to avoid double patching orig[builtinPatchedSymbol] = true; return previousLoadResult; } function isSelfCheckImport(path) { // We can't use getModuleInfoFromPath as it would not work in unit tests return path .replace(/\\/g, "/") .includes("hooks/instrumentation/zenHooksCheckImport."); // .js or .ts } function updateSelfCheckSource(previousLoadResult) { const sourceString = typeof previousLoadResult.source === "string" ? previousLoadResult.source : new TextDecoder("utf-8").decode(previousLoadResult.source); return { ...previousLoadResult, source: sourceString.replace("//SELF_CHECK_REPLACE", ":)"), }; }