UNPKG

@aikidosec/firewall

Version:

Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks

114 lines (113 loc) 4.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.detectNoSQLInjection = detectNoSQLInjection; /* eslint-disable max-lines-per-function */ const util_1 = require("util"); const Source_1 = require("../../agent/Source"); const attackPath_1 = require("../../helpers/attackPath"); const isPlainObject_1 = require("../../helpers/isPlainObject"); const tryDecodeAsJWT_1 = require("../../helpers/tryDecodeAsJWT"); const detectDbJsInjection_1 = require("../js-injection/detectDbJsInjection"); function matchFilterPartInUser(userInput, filterPart, pathToPayload = []) { if (typeof userInput === "string") { // Check for js injection in $where if ((0, detectDbJsInjection_1.detectDbJsInjection)(userInput, filterPart)) { return { match: true, pathToPayload: (0, attackPath_1.buildPathToPayload)(pathToPayload), }; } const jwt = (0, tryDecodeAsJWT_1.tryDecodeAsJWT)(userInput); if (jwt.jwt) { return matchFilterPartInUser(jwt.object, filterPart, pathToPayload.concat([{ type: "jwt" }])); } } if ((0, isPlainObject_1.isPlainObject)(userInput)) { const filteredInput = removeKeysThatDontStartWithDollarSign(userInput); if ((0, util_1.isDeepStrictEqual)(filteredInput, filterPart)) { return { match: true, pathToPayload: (0, attackPath_1.buildPathToPayload)(pathToPayload) }; } for (const key in userInput) { const match = matchFilterPartInUser(userInput[key], filterPart, pathToPayload.concat([{ type: "object", key: key }])); if (match.match) { return match; } } } if (Array.isArray(userInput)) { for (let index = 0; index < userInput.length; index++) { const match = matchFilterPartInUser(userInput[index], filterPart, pathToPayload.concat([{ type: "array", index: index }])); if (match.match) { return match; } } } return { match: false, }; } function removeKeysThatDontStartWithDollarSign(filter) { return Object.keys(filter).reduce((acc, key) => { if (key.startsWith("$")) { return { ...acc, [key]: filter[key] }; } return acc; }, {}); } function findFilterPartWithOperators(userInput, partOfFilter) { if ((0, isPlainObject_1.isPlainObject)(partOfFilter)) { const object = removeKeysThatDontStartWithDollarSign(partOfFilter); if (Object.keys(object).length > 0) { const result = matchFilterPartInUser(userInput, object); if (result.match) { return { found: true, pathToPayload: result.pathToPayload, payload: object, }; } } for (const key in partOfFilter) { const result = findFilterPartWithOperators(userInput, partOfFilter[key]); if (result.found) { return { found: true, pathToPayload: result.pathToPayload, payload: result.payload, }; } } } if (Array.isArray(partOfFilter)) { for (const value of partOfFilter) { const result = findFilterPartWithOperators(userInput, value); if (result.found) { return { found: true, pathToPayload: result.pathToPayload, payload: result.payload, }; } } } return { found: false }; } function detectNoSQLInjection(request, filter) { if (!(0, isPlainObject_1.isPlainObject)(filter) && !Array.isArray(filter)) { return { injection: false }; } for (const source of Source_1.SOURCES) { if (request[source]) { const result = findFilterPartWithOperators(request[source], filter); if (result.found) { return { injection: true, source: source, pathsToPayload: [result.pathToPayload], payload: result.payload, }; } } } return { injection: false }; }