@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.
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;