@aikidosec/firewall
Version:
Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks
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;