@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
JavaScript
;
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 };
}