@aikidosec/firewall
Version:
Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks
145 lines (144 loc) • 5.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MongoDB = void 0;
const detectNoSQLInjection_1 = require("../vulnerabilities/nosql-injection/detectNoSQLInjection");
const isPlainObject_1 = require("../helpers/isPlainObject");
const Context_1 = require("../agent/Context");
const wrapExport_1 = require("../agent/hooks/wrapExport");
const OPERATIONS_WITH_FILTER = [
"count",
"countDocuments",
"find",
"findOne",
"findOneAndUpdate",
"findOneAndReplace",
"findOneAndDelete",
"deleteOne",
"deleteMany",
"updateOne",
"updateMany",
"replaceOne",
];
const BULK_WRITE_OPERATIONS_WITH_FILTER = [
"replaceOne",
"updateOne",
"updateMany",
"deleteOne",
"deleteMany",
];
class MongoDB {
inspectFilter(db, collection, request, filter, operation) {
const result = (0, detectNoSQLInjection_1.detectNoSQLInjection)(request, filter);
if (result.injection) {
return {
operation: `MongoDB.Collection.${operation}`,
kind: "nosql_injection",
source: result.source,
pathsToPayload: result.pathsToPayload,
metadata: {
db: db,
collection: collection,
operation: operation,
filter: JSON.stringify(filter),
},
payload: result.payload,
};
}
}
inspectBulkWriteOperation(operation, collection, context) {
for (const op of BULK_WRITE_OPERATIONS_WITH_FILTER) {
const options = operation[op];
if (options && options.filter) {
return this.inspectFilter(collection.dbName, collection.collectionName, context, options.filter, "bulkWrite");
}
}
return undefined;
}
inspectBulkWrite(args, collection) {
const context = (0, Context_1.getContext)();
if (!context) {
return undefined;
}
if (Array.isArray(args[0]) && args[0].length > 0) {
const operations = args[0];
for (const operation of operations) {
const result = this.inspectBulkWriteOperation(operation, collection, context);
if (result) {
return result;
}
}
}
return undefined;
}
inspectAggregate(args, collection) {
const context = (0, Context_1.getContext)();
if (!context) {
return undefined;
}
if (Array.isArray(args) && args.length > 0) {
const pipeline = args[0];
return this.inspectFilter(collection.dbName, collection.collectionName, context, pipeline, "aggregate");
}
return undefined;
}
inspectOperation(operation, args, collection) {
const context = (0, Context_1.getContext)();
if (!context) {
return undefined;
}
if (args.length > 0 && (0, isPlainObject_1.isPlainObject)(args[0])) {
const filter = args[0];
return this.inspectFilter(collection.dbName, collection.collectionName, context, filter, operation);
}
return undefined;
}
inspectDistinct(args, collection) {
const context = (0, Context_1.getContext)();
if (!context) {
return undefined;
}
if (args.length > 1 && (0, isPlainObject_1.isPlainObject)(args[1])) {
const filter = args[1];
return this.inspectFilter(collection.dbName, collection.collectionName, context, filter, "distinct");
}
return undefined;
}
wrapCollection(exports, pkgInfo) {
const collectionProto = exports.Collection.prototype;
OPERATIONS_WITH_FILTER.forEach((operation) => {
(0, wrapExport_1.wrapExport)(collectionProto, operation, pkgInfo, {
kind: "nosql_op",
inspectArgs: (args, agent, collection) => this.inspectOperation(operation, args, collection),
});
});
(0, wrapExport_1.wrapExport)(collectionProto, "bulkWrite", pkgInfo, {
kind: "nosql_op",
inspectArgs: (args, agent, collection) => this.inspectBulkWrite(args, collection),
});
(0, wrapExport_1.wrapExport)(collectionProto, "aggregate", pkgInfo, {
kind: "nosql_op",
inspectArgs: (args, agent, collection) => this.inspectAggregate(args, collection),
});
(0, wrapExport_1.wrapExport)(collectionProto, "distinct", pkgInfo, {
kind: "nosql_op",
inspectArgs: (args, agent, collection) => this.inspectDistinct(args, collection),
});
}
wrap(hooks) {
hooks
.addPackage("mongodb")
.withVersion("^4.0.0 || ^5.0.0 || ^6.0.0")
.onRequire((exports, pkgInfo) => {
// From mongodb v6.10.0, the Collection is undefined
// It's defined like:
// exports.Collection = void 0;
// const collection_1 = require("./collection");
// Object.defineProperty(exports, "Collection", { enumerable: true, get: function () { return collection_1.Collection; } });
// So we need to wait for the next tick to wrap the Collection
process.nextTick(() => {
this.wrapCollection(exports, pkgInfo);
});
});
}
}
exports.MongoDB = MongoDB;