UNPKG

@aikidosec/firewall

Version:

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

92 lines (91 loc) 2.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildPathToPayload = buildPathToPayload; exports.getPathsToPayload = getPathsToPayload; const isPlainObject_1 = require("./isPlainObject"); const tryDecodeAsJWT_1 = require("./tryDecodeAsJWT"); // Default match count to return const DEFAULT_MATCH_COUNT = 1; // Maximum depth to traverse const MAX_DEPTH = 30; // Maximum array length to traverse const MAX_ARRAY_LENGTH = 100; function buildPathToPayload(pathToPayload) { if (pathToPayload.length === 0) { return "."; } return pathToPayload.reduce((acc, part) => { if (part.type === "jwt") { return `${acc}<jwt>`; } if (part.type === "object") { return `${acc}.${part.key}`; } if (part.type === "array") { return `${acc}.[${part.index}]`; } /* c8 ignore next */ return acc; }, ""); } class Matches { constructor(max) { this.max = max; this.matches = []; if (max < 1) { throw new Error("Max must be greater than 0"); } } add(path) { this.matches.push(buildPathToPayload(path)); } getMatches() { return this.matches; } found() { return this.matches.length >= this.max; } } function getPathsToPayload(attackPayload, obj, matchCount = DEFAULT_MATCH_COUNT) { const matches = new Matches(matchCount); const attackPayloadLowercase = attackPayload.toLowerCase(); const traverse = (value, path = [], depth = 0) => { if (matches.found() || depth > MAX_DEPTH) { return; } if (typeof value === "string") { if (value.toLowerCase() === attackPayloadLowercase) { matches.add(path); return; } const jwt = (0, tryDecodeAsJWT_1.tryDecodeAsJWT)(value); if (jwt.jwt) { traverse(jwt.object, path.concat({ type: "jwt" }), depth + 1); } } if (Array.isArray(value)) { if (value.length > 1 && value.length < MAX_ARRAY_LENGTH && value.join().toLowerCase() === attackPayloadLowercase) { matches.add(path); return; } for (const [index, item] of value.entries()) { if (matches.found() || index > MAX_ARRAY_LENGTH) { break; } traverse(item, path.concat({ type: "array", index }), depth); } } if ((0, isPlainObject_1.isPlainObject)(value)) { for (const key in value) { if (matches.found()) { break; } traverse(value[key], path.concat({ type: "object", key }), depth + 1); } } }; traverse(obj); return matches.getMatches(); }