UNPKG

@aikidosec/firewall

Version:

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

113 lines (112 loc) 3.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Routes = void 0; const getMaxApiDiscoverySamples_1 = require("../helpers/getMaxApiDiscoverySamples"); const getApiInfo_1 = require("./api-discovery/getApiInfo"); const updateApiInfo_1 = require("./api-discovery/updateApiInfo"); const AikidoDAST_1 = require("./AikidoDAST"); class Routes { constructor(maxEntries = 1000, maxGraphQLSchemas = 10) { this.maxEntries = maxEntries; this.maxGraphQLSchemas = maxGraphQLSchemas; // Routes are only registered at the end of the request, so we need to store the schema in a separate map this.graphQLSchemas = new Map(); this.routes = new Map(); } addRoute(context) { if ((0, AikidoDAST_1.isAikidoDASTRequest)(context)) { return; } const { method, route: path } = context; if (!method || !path) { return; } const key = this.getKey(method, path); const existing = this.routes.get(key); const maxSamples = (0, getMaxApiDiscoverySamples_1.getMaxApiDiscoverySamples)(); if (existing) { (0, updateApiInfo_1.updateApiInfo)(context, existing, maxSamples); existing.hits++; return; } // Get info about body and query schema let apispec = {}; if (maxSamples > 0) { apispec = (0, getApiInfo_1.getApiInfo)(context) || {}; } this.evictLeastUsedRouteIfNecessary(); this.routes.set(key, { method, path, hits: 1, apispec, }); } evictLeastUsedRouteIfNecessary() { if (this.routes.size >= this.maxEntries) { this.evictLeastUsedRoute(); } } getKey(method, path) { return `${method}:${path}`; } hasGraphQLSchema(method, path) { const key = this.getKey(method, path); return this.graphQLSchemas.has(key); } setGraphQLSchema(method, path, schema) { if (schema.length > 0 && this.graphQLSchemas.size < this.maxGraphQLSchemas) { const key = this.getKey(method, path); this.graphQLSchemas.set(key, schema); } } getGraphQLKey(method, path, type, name) { return `${method}:${path}:${type}:${name}`; } addGraphQLField(method, path, type, name) { const key = this.getGraphQLKey(method, path, type, name); const existing = this.routes.get(key); if (existing) { existing.hits++; return; } this.evictLeastUsedRouteIfNecessary(); this.routes.set(key, { method, path, hits: 1, graphql: { type, name }, apispec: {}, }); } evictLeastUsedRoute() { let leastUsedKey = null; let leastHits = Infinity; for (const [key, route] of this.routes.entries()) { if (route.hits < leastHits) { leastHits = route.hits; leastUsedKey = key; } } if (leastUsedKey !== null) { this.routes.delete(leastUsedKey); } } clear() { this.routes.clear(); } asArray() { return Array.from(this.routes.entries()).map(([key, route]) => { return { method: route.method, path: route.path, hits: route.hits, graphql: route.graphql, apispec: route.apispec, graphQLSchema: this.graphQLSchemas.get(key), }; }); } } exports.Routes = Routes;