@aikidosec/firewall
Version:
Zen by Aikido is an embedded Application Firewall that autonomously protects Node.js apps against common and critical attacks, provides rate limiting, detects malicious traffic (including bots), and more.
204 lines (203 loc) • 7.85 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceConfig = void 0;
const IPMatcher_1 = require("../helpers/ip-matcher/IPMatcher");
const matchEndpoints_1 = require("../helpers/matchEndpoints");
const isPrivateIP_1 = require("../vulnerabilities/ssrf/isPrivateIP");
const safeCreateRegExp_1 = require("./safeCreateRegExp");
class ServiceConfig {
constructor(endpoints, lastUpdatedAt, blockedUserIds, bypassedIPAddresses, receivedAnyStats, blockedIPAddresses, allowedIPAddresses) {
this.lastUpdatedAt = lastUpdatedAt;
this.receivedAnyStats = receivedAnyStats;
this.blockedUserIds = new Map();
this.nonGraphQLEndpoints = [];
this.graphqlFields = [];
this.blockedIPAddresses = [];
// If not empty, only ips in this list are allowed to access the service
// e.g. for country allowlists
this.allowedIPAddresses = [];
this.monitoredIPAddresses = [];
this.userAgentDetails = [];
this.setBlockedUserIds(blockedUserIds);
this.setBypassedIPAddresses(bypassedIPAddresses);
this.setEndpoints(endpoints);
this.setBlockedIPAddresses(blockedIPAddresses);
this.setAllowedIPAddresses(allowedIPAddresses);
}
setEndpoints(endpointConfigs) {
this.nonGraphQLEndpoints = [];
this.graphqlFields = [];
for (const endpoint of endpointConfigs) {
let allowedIPAddresses = undefined;
if (Array.isArray(endpoint.allowedIPAddresses) &&
endpoint.allowedIPAddresses.length > 0) {
allowedIPAddresses = new IPMatcher_1.IPMatcher(endpoint.allowedIPAddresses);
}
const endpointConfig = { ...endpoint, allowedIPAddresses };
if (endpoint.graphql) {
this.graphqlFields.push(endpointConfig);
}
else {
this.nonGraphQLEndpoints.push(endpointConfig);
}
}
}
getEndpoints(context) {
return (0, matchEndpoints_1.matchEndpoints)(context, this.nonGraphQLEndpoints);
}
getGraphQLField(context, name, operationType) {
const endpoints = (0, matchEndpoints_1.matchEndpoints)(context, this.graphqlFields.filter((field) => {
if (!field.graphql) {
return false;
}
return (field.graphql.name === name && field.graphql.type === operationType);
}));
return endpoints.length > 0 ? endpoints[0] : undefined;
}
setBypassedIPAddresses(ipAddresses) {
if (ipAddresses.length === 0) {
this.bypassedIPAddresses = undefined;
return;
}
this.bypassedIPAddresses = new IPMatcher_1.IPMatcher(ipAddresses);
}
isBypassedIP(ip) {
return this.bypassedIPAddresses ? this.bypassedIPAddresses.has(ip) : false;
}
setBlockedUserIds(blockedUserIds) {
this.blockedUserIds = new Map();
blockedUserIds.forEach((userId) => {
this.blockedUserIds.set(userId, userId);
});
}
isUserBlocked(userId) {
return this.blockedUserIds.has(userId);
}
isIPAddressBlocked(ip) {
const blocklist = this.blockedIPAddresses.find((blocklist) => blocklist.blocklist.has(ip));
if (blocklist) {
return { blocked: true, reason: blocklist.description };
}
return { blocked: false };
}
setBlockedIPAddresses(blockedIPAddresses) {
this.blockedIPAddresses = [];
for (const source of blockedIPAddresses) {
this.blockedIPAddresses.push({
key: source.key,
blocklist: new IPMatcher_1.IPMatcher(source.ips),
description: source.description,
});
}
}
updateBlockedIPAddresses(blockedIPAddresses) {
this.setBlockedIPAddresses(blockedIPAddresses);
}
updateMonitoredIPAddresses(monitoredIPAddresses) {
this.monitoredIPAddresses = [];
for (const source of monitoredIPAddresses) {
this.monitoredIPAddresses.push({
key: source.key,
list: new IPMatcher_1.IPMatcher(source.ips),
});
}
}
updateBlockedUserAgents(blockedUserAgents) {
if (!blockedUserAgents) {
// If an empty string is passed, we want to set the regex to undefined
// e.g. new RegExp("").test("abc") == true
this.blockedUserAgentRegex = undefined;
return;
}
this.blockedUserAgentRegex = (0, safeCreateRegExp_1.safeCreateRegExp)(blockedUserAgents, "i");
}
isUserAgentBlocked(ua) {
if (this.blockedUserAgentRegex) {
return { blocked: this.blockedUserAgentRegex.test(ua) };
}
return { blocked: false };
}
updateUserAgentDetails(userAgentDetails) {
this.userAgentDetails = [];
for (const detail of userAgentDetails) {
const regex = (0, safeCreateRegExp_1.safeCreateRegExp)(detail.pattern, "i");
if (regex) {
this.userAgentDetails.push({
key: detail.key,
pattern: regex,
});
}
}
}
updateMonitoredUserAgents(monitoredUserAgent) {
if (!monitoredUserAgent) {
// If an empty string is passed, we want to set the regex to undefined
// e.g. new RegExp("").test("abc") == true
this.monitoredUserAgentRegex = undefined;
return;
}
this.monitoredUserAgentRegex = (0, safeCreateRegExp_1.safeCreateRegExp)(monitoredUserAgent, "i");
}
isMonitoredUserAgent(ua) {
if (this.monitoredUserAgentRegex) {
return this.monitoredUserAgentRegex.test(ua);
}
return false;
}
getMatchingUserAgentKeys(ua) {
return this.userAgentDetails
.filter((details) => details.pattern.test(ua))
.map((details) => details.key);
}
getMatchingBlockedIPListKeys(ip) {
return this.blockedIPAddresses
.filter((list) => list.blocklist.has(ip))
.map((list) => list.key);
}
getMatchingMonitoredIPListKeys(ip) {
return this.monitoredIPAddresses
.filter((list) => list.list.has(ip))
.map((list) => list.key);
}
setAllowedIPAddresses(ipAddresses) {
this.allowedIPAddresses = [];
for (const source of ipAddresses) {
// Skip empty allowlists
if (source.ips.length === 0) {
continue;
}
this.allowedIPAddresses.push({
allowlist: new IPMatcher_1.IPMatcher(source.ips),
description: source.description,
});
}
}
updateAllowedIPAddresses(ipAddresses) {
this.setAllowedIPAddresses(ipAddresses);
}
isAllowedIPAddress(ip) {
if (this.allowedIPAddresses.length < 1) {
return { allowed: true };
}
// Always allow access from local IP addresses
if ((0, isPrivateIP_1.isPrivateIP)(ip)) {
return { allowed: true };
}
const allowlist = this.allowedIPAddresses.find((list) => list.allowlist.has(ip));
return { allowed: !!allowlist };
}
updateConfig(endpoints, lastUpdatedAt, blockedUserIds, bypassedIPAddresses, hasReceivedAnyStats) {
this.setEndpoints(endpoints);
this.setBlockedUserIds(blockedUserIds);
this.setBypassedIPAddresses(bypassedIPAddresses);
this.lastUpdatedAt = lastUpdatedAt;
this.receivedAnyStats = hasReceivedAnyStats;
}
getLastUpdatedAt() {
return this.lastUpdatedAt;
}
hasReceivedAnyStats() {
return this.receivedAnyStats;
}
}
exports.ServiceConfig = ServiceConfig;