UNPKG

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

161 lines (160 loc) 7.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Prisma = void 0; const wrapNewInstance_1 = require("../agent/hooks/wrapNewInstance"); const SQLDialectMySQL_1 = require("../vulnerabilities/sql-injection/dialects/SQLDialectMySQL"); const SQLDialectGeneric_1 = require("../vulnerabilities/sql-injection/dialects/SQLDialectGeneric"); const SQLDialectPostgres_1 = require("../vulnerabilities/sql-injection/dialects/SQLDialectPostgres"); const SQLDialectSQLite_1 = require("../vulnerabilities/sql-injection/dialects/SQLDialectSQLite"); const checkContextForSqlInjection_1 = require("../vulnerabilities/sql-injection/checkContextForSqlInjection"); const Context_1 = require("../agent/Context"); const onInspectionInterceptorResult_1 = require("../agent/hooks/onInspectionInterceptorResult"); const AgentSingleton_1 = require("../agent/AgentSingleton"); const detectNoSQLInjection_1 = require("../vulnerabilities/nosql-injection/detectNoSQLInjection"); const NOSQL_OPERATIONS_WITH_FILTER = ["findRaw"]; const NOSQL_OPERATIONS_WITH_PIPELINE = ["aggregateRaw"]; const SQL_OPERATIONS_TO_PROTECT = ["$queryRawUnsafe", "$executeRawUnsafe"]; class Prisma { // Check if the prisma client is a NoSQL client isNoSQLClient(clientInstance) { if (!clientInstance || typeof clientInstance !== "object" || !("_engineConfig" in clientInstance) || !clientInstance._engineConfig || typeof clientInstance._engineConfig !== "object" || !("activeProvider" in clientInstance._engineConfig) || typeof clientInstance._engineConfig.activeProvider !== "string") { return false; } return clientInstance._engineConfig.activeProvider === "mongodb"; } // Try to detect the SQL dialect used by the Prisma client, so we can use the correct SQL dialect for the SQL injection detection. getClientSQLDialect(clientInstance) { // https://github.com/prisma/prisma/blob/559988a47e50b4d4655dc45b11ceb9b5c73ef053/packages/generator-helper/src/types.ts#L75 if (!clientInstance || typeof clientInstance !== "object" || !("_engineConfig" in clientInstance) || !clientInstance._engineConfig || typeof clientInstance._engineConfig !== "object" || !("activeProvider" in clientInstance._engineConfig) || typeof clientInstance._engineConfig.activeProvider !== "string") { return new SQLDialectGeneric_1.SQLDialectGeneric(); } switch (clientInstance._engineConfig.activeProvider) { case "mysql": return new SQLDialectMySQL_1.SQLDialectMySQL(); case "postgresql": case "postgres": return new SQLDialectPostgres_1.SQLDialectPostgres(); case "sqlite": return new SQLDialectSQLite_1.SQLDialectSQLite(); default: return new SQLDialectGeneric_1.SQLDialectGeneric(); } } inspectSQLQuery(args, operation, dialect) { const context = (0, Context_1.getContext)(); if (!context) { return undefined; } if (args.length > 0 && typeof args[0] === "string" && args[0].length > 0) { const sql = args[0]; return (0, checkContextForSqlInjection_1.checkContextForSqlInjection)({ sql: sql, context: context, operation: `prisma.${operation}`, dialect: dialect, }); } return undefined; } inspectNoSQLQuery(args, operation, model) { const context = (0, Context_1.getContext)(); if (!context) { return undefined; } if (!args || typeof args !== "object") { return undefined; } let filter; if (NOSQL_OPERATIONS_WITH_FILTER.includes(operation) && "filter" in args) { filter = args.filter; } if (NOSQL_OPERATIONS_WITH_PIPELINE.includes(operation) && "pipeline" in args) { filter = args.pipeline; } if (filter) { return this.inspectNoSQLFilter(model !== null && model !== void 0 ? model : "", context, filter, operation); } return undefined; } inspectNoSQLFilter(collection, request, filter, operation) { const result = (0, detectNoSQLInjection_1.detectNoSQLInjection)(request, filter); if (result.injection) { return { operation: `prisma.${operation}`, kind: "nosql_injection", source: result.source, pathsToPayload: result.pathsToPayload, metadata: { collection: collection, operation: operation, filter: JSON.stringify(filter), }, payload: result.payload, }; } } onClientOperation({ model, operation, args, query, isNoSQLClient, sqlDialect, agent, pkgInfo, }) { let inspectionResult; const start = performance.now(); if (!isNoSQLClient && SQL_OPERATIONS_TO_PROTECT.includes(operation)) { inspectionResult = this.inspectSQLQuery(args, operation, sqlDialect || new SQLDialectGeneric_1.SQLDialectGeneric()); } if (isNoSQLClient) { inspectionResult = this.inspectNoSQLQuery(args, operation, model); } if (inspectionResult) { // Run the logic to handle a detected attack (0, onInspectionInterceptorResult_1.onInspectionInterceptorResult)((0, Context_1.getContext)(), agent, inspectionResult, pkgInfo, start, operation, isNoSQLClient ? "nosql_op" : "sql_op"); } return query(args); } wrap(hooks) { hooks .addPackage("@prisma/client") .withVersion("^5.0.0 || ^6.0.0") .onRequire((exports, pkgInfo) => { (0, wrapNewInstance_1.wrapNewInstance)(exports, "PrismaClient", pkgInfo, (instance) => { const isNoSQLClient = this.isNoSQLClient(instance); const agent = (0, AgentSingleton_1.getInstance)(); if (!agent) { return; } // Extend all operations of the Prisma client // https://www.prisma.io/docs/orm/prisma-client/client-extensions/query#modify-all-operations-in-all-models-of-your-schema return instance.$extends({ query: { $allOperations: ({ model, operation, args, query, }) => { return this.onClientOperation({ model, operation, args, query, isNoSQLClient, sqlDialect: !isNoSQLClient ? this.getClientSQLDialect(instance) : undefined, agent, pkgInfo, }); }, }, }); }); }); } } exports.Prisma = Prisma;