@aikidosec/firewall
Version:
Zen by Aikido is an embedded Web Application Firewall that autonomously protects Node.js apps against common and critical attacks
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;