@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.
96 lines (95 loc) • 3.79 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.Path = void 0;
const Context_1 = require("../agent/Context");
const wrapExport_1 = require("../agent/hooks/wrapExport");
const isWindows_1 = require("../helpers/isWindows");
const checkContextForPathTraversal_1 = require("../vulnerabilities/path-traversal/checkContextForPathTraversal");
class Path {
constructor() {
this.patchedPosix = false;
this.patchedWin32 = false;
}
inspectPath(args, operation) {
const context = (0, Context_1.getContext)();
if (!context) {
return undefined;
}
for (let i = 0; i < args.length; i++) {
const path = args[i];
if (typeof path === "string") {
const result = (0, checkContextForPathTraversal_1.checkContextForPathTraversal)({
filename: path,
operation: `path.${operation}`,
context: context,
// Only check the first argument for absolute paths, unless the operation is `resolve`.
//
// For most path operations (join, normalize), only the first argument matters
// because subsequent absolute paths are treated as relative components.
// However, path.resolve() is special: ANY absolute path in ANY position
// becomes the new resolved path, discarding all previous arguments.
//
// Examples:
// - path.join('/tmp', '/etc/passwd') → '/tmp/etc/passwd'
// - path.resolve('/tmp', '/etc/passwd') → '/etc/passwd'
checkPathStart: i === 0 || operation === "resolve",
});
if (result) {
return result;
}
}
}
return undefined;
}
wrapFunctions(exports, pkgInfo) {
const functions = ["join", "resolve", "normalize"];
for (const func of functions) {
(0, wrapExport_1.wrapExport)(exports, func, pkgInfo, {
kind: "fs_op",
inspectArgs: (args) => this.inspectPath(args, func),
});
}
}
wrapMainModule(exports, pkgInfo) {
// If `path/win32` or `path/posix` was not required before `path`, we should wrap the functions in `path`
if (!this.patchedWin32 && !this.patchedPosix) {
this.wrapFunctions(exports, pkgInfo);
}
if ((0, isWindows_1.isWindows)()) {
// `require("path").join` is the same as `require("path/win32").join`
this.patchedWin32 = true;
}
else {
// `require("path").join` is the same as `require("path/posix").join`
this.patchedPosix = true;
}
this.wrapPosix(exports.posix, pkgInfo);
this.wrapWin32(exports.win32, pkgInfo);
}
wrapPosix(exports, pkgInfo) {
if (this.patchedPosix) {
return;
}
this.wrapFunctions(exports, pkgInfo);
this.patchedPosix = true;
}
wrapWin32(exports, pkgInfo) {
if (this.patchedWin32) {
return;
}
this.wrapFunctions(exports, pkgInfo);
this.patchedWin32 = true;
}
wrap(hooks) {
hooks
.addBuiltinModule("path")
.onRequire((exports, pkgInfo) => this.wrapMainModule(exports, pkgInfo));
hooks
.addBuiltinModule("path/posix")
.onRequire((exports, pkgInfo) => this.wrapPosix(exports, pkgInfo));
hooks
.addBuiltinModule("path/win32")
.onRequire((exports, pkgInfo) => this.wrapWin32(exports, pkgInfo));
}
}
exports.Path = Path;
;