@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.
134 lines (133 loc) • 6.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileSystem = void 0;
const Context_1 = require("../agent/Context");
const wrapExport_1 = require("../agent/hooks/wrapExport");
const getNodeVersion_1 = require("../helpers/getNodeVersion");
const isVersionGreaterOrEqual_1 = require("../helpers/isVersionGreaterOrEqual");
const checkContextForPathTraversal_1 = require("../vulnerabilities/path-traversal/checkContextForPathTraversal");
class FileSystem {
constructor() {
this.patchedPromises = false;
}
inspectPath(args, name, amountOfPathArgs) {
const context = (0, Context_1.getContext)();
if (!context) {
return undefined;
}
for (const path of args.slice(0, amountOfPathArgs)) {
if (typeof path === "string" ||
path instanceof Buffer ||
path instanceof URL) {
const result = (0, checkContextForPathTraversal_1.checkContextForPathTraversal)({
filename: path,
operation: `fs.${name}`,
context: context,
});
if (result) {
return result;
}
}
}
return undefined;
}
getFunctions() {
const functions = {
appendFile: { pathsArgs: 1, sync: true, promise: true },
chmod: { pathsArgs: 1, sync: true, promise: true },
chown: { pathsArgs: 1, sync: true, promise: true },
createReadStream: { pathsArgs: 1, sync: false, promise: false },
createWriteStream: { pathsArgs: 1, sync: false, promise: false },
lchown: { pathsArgs: 1, sync: true, promise: true },
lutimes: { pathsArgs: 1, sync: true, promise: true },
mkdir: { pathsArgs: 1, sync: true, promise: true },
open: { pathsArgs: 1, sync: true, promise: true },
opendir: { pathsArgs: 1, sync: true, promise: true },
readdir: { pathsArgs: 1, sync: true, promise: true },
readFile: { pathsArgs: 1, sync: true, promise: true },
readlink: { pathsArgs: 1, sync: true, promise: true },
unlink: { pathsArgs: 1, sync: true, promise: true },
realpath: { pathsArgs: 1, sync: true, promise: true },
rename: { pathsArgs: 2, sync: true, promise: true },
rmdir: { pathsArgs: 1, sync: true, promise: true },
rm: { pathsArgs: 1, sync: true, promise: true },
symlink: { pathsArgs: 2, sync: true, promise: true },
truncate: { pathsArgs: 1, sync: true, promise: true },
utimes: { pathsArgs: 1, sync: true, promise: true },
writeFile: { pathsArgs: 1, sync: true, promise: true },
copyFile: { pathsArgs: 2, sync: true, promise: true },
cp: { pathsArgs: 2, sync: true, promise: true },
link: { pathsArgs: 2, sync: true, promise: true },
watch: { pathsArgs: 1, sync: false, promise: false },
watchFile: { pathsArgs: 1, sync: false, promise: false },
mkdtemp: { pathsArgs: 1, sync: true, promise: true },
};
// Added in v19.8.0
if ((0, isVersionGreaterOrEqual_1.isVersionGreaterOrEqual)("19.8.0", (0, getNodeVersion_1.getSemverNodeVersion)())) {
functions.openAsBlob = { pathsArgs: 1, sync: false, promise: false };
}
// Only available on macOS
if (process.platform === "darwin") {
functions.lchmod = { pathsArgs: 1, sync: true, promise: true };
}
return functions;
}
wrapPromises(exports, pkgInfo) {
if (this.patchedPromises) {
// `require("fs").promises.readFile` is the same as `require("fs/promises").readFile`
// We only need to wrap the promise version once
return;
}
const functions = this.getFunctions();
Object.keys(functions).forEach((name) => {
const { pathsArgs, promise } = functions[name];
if (promise) {
(0, wrapExport_1.wrapExport)(exports, name, pkgInfo, {
kind: "fs_op",
inspectArgs: (args) => this.inspectPath(args, name, pathsArgs),
});
}
});
this.patchedPromises = true;
}
wrap(hooks) {
hooks.addBuiltinModule("fs").onRequire((exports, pkgInfo) => {
const functions = this.getFunctions();
Object.keys(functions).forEach((name) => {
const { pathsArgs, sync } = functions[name];
(0, wrapExport_1.wrapExport)(exports, name, pkgInfo, {
kind: "fs_op",
inspectArgs: (args) => {
return this.inspectPath(args, name, pathsArgs);
},
});
if (sync) {
(0, wrapExport_1.wrapExport)(exports, `${name}Sync`, pkgInfo, {
kind: "fs_op",
inspectArgs: (args) => {
return this.inspectPath(args, `${name}Sync`, pathsArgs);
},
});
}
});
// Wrap realpath.native
(0, wrapExport_1.wrapExport)(exports.realpath, "native", pkgInfo, {
kind: "fs_op",
inspectArgs: (args) => {
return this.inspectPath(args, "realpath.native", 1);
},
});
(0, wrapExport_1.wrapExport)(exports.realpathSync, "native", pkgInfo, {
kind: "fs_op",
inspectArgs: (args) => {
return this.inspectPath(args, "realpathSync.native", 1);
},
});
this.wrapPromises(exports.promises, pkgInfo);
});
hooks
.addBuiltinModule("fs/promises")
.onRequire((exports, pkgInfo) => this.wrapPromises(exports, pkgInfo));
}
}
exports.FileSystem = FileSystem;