UNPKG

@aikidosec/firewall

Version:

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

90 lines (89 loc) 3.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.shouldRateLimitOperation = shouldRateLimitOperation; const isLocalhostIP_1 = require("../../helpers/isLocalhostIP"); const extractTopLevelFieldsFromDocument_1 = require("./extractTopLevelFieldsFromDocument"); function shouldRateLimitOperation(agent, context, executeArgs) { const topLevelFields = (0, extractTopLevelFieldsFromDocument_1.extractTopLevelFieldsFromDocument)(executeArgs.document, executeArgs.operationName ? executeArgs.operationName : undefined); if (!topLevelFields) { return { block: false }; } const isProduction = process.env.NODE_ENV === "production"; // Allow requests from localhost in development to be rate limited // In production, we don't want to rate limit localhost const isFromLocalhostInProduction = context.remoteAddress ? (0, isLocalhostIP_1.isLocalhostIP)(context.remoteAddress) && isProduction : false; // Allow requests from allowed IPs, e.g. never rate limit office IPs const isBypassedIP = context.remoteAddress ? agent.getConfig().isBypassedIP(context.remoteAddress) : false; for (const field of topLevelFields.fields) { const result = shouldRateLimitField(agent, context, field, topLevelFields.type, isFromLocalhostInProduction, isBypassedIP); if (result.block) { return result; } } return { block: false }; } // eslint-disable-next-line max-lines-per-function function shouldRateLimitField(agent, context, field, operationType, isFromLocalhostInProduction, isBypassedIP) { const match = agent .getConfig() .getGraphQLField(context, field.name.value, operationType); if (!match || !match.graphql) { return { block: false }; } const rateLimitedField = match; if (!rateLimitedField || !rateLimitedField.rateLimiting || !rateLimitedField.rateLimiting.enabled) { return { block: false }; } if (context.remoteAddress && !isFromLocalhostInProduction && !isBypassedIP) { const allowed = agent .getRateLimiter() .isAllowed(`${context.method}:${context.route}:ip:${context.remoteAddress}:${operationType}:${field.name.value}`, rateLimitedField.rateLimiting.windowSizeInMS, rateLimitedField.rateLimiting.maxRequests); if (!allowed) { return { block: true, field: field, source: "ip", remoteAddress: context.remoteAddress, operationType: operationType, endpoint: match, }; } } if (context.rateLimitGroup) { const allowed = agent .getRateLimiter() .isAllowed(`${context.method}:${context.route}:group:${context.rateLimitGroup}:${operationType}:${field.name.value}`, rateLimitedField.rateLimiting.windowSizeInMS, rateLimitedField.rateLimiting.maxRequests); if (!allowed) { return { block: true, field: field, source: "group", groupId: context.rateLimitGroup, operationType: operationType, endpoint: match, }; } } if (context.user) { const allowed = agent .getRateLimiter() .isAllowed(`${context.method}:${context.route}:user:${context.user.id}:${operationType}:${field.name.value}`, rateLimitedField.rateLimiting.windowSizeInMS, rateLimitedField.rateLimiting.maxRequests); if (!allowed) { return { block: true, field: field, source: "user", userId: context.user.id, operationType: operationType, endpoint: match, }; } } return { block: false }; }