Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions end2end/server/src/handlers/lists.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ const {
getBlockedIPAddresses,
getBlockedUserAgents,
getAllowedIPAddresses,
getMonitoredUserAgents,
getMonitoredIPAddresses,
getUserAgentDetails,
} = require("../zen/config");

module.exports = function lists(req, res) {
Expand All @@ -12,6 +15,9 @@ module.exports = function lists(req, res) {
const blockedIps = getBlockedIPAddresses(req.app);
const blockedUserAgents = getBlockedUserAgents(req.app);
const allowedIps = getAllowedIPAddresses(req.app);
const monitoredUserAgents = getMonitoredUserAgents(req.app);
const monitoredIps = getMonitoredIPAddresses(req.app);
const userAgentDetails = getUserAgentDetails(req.app);

res.json({
success: true,
Expand All @@ -20,22 +26,37 @@ module.exports = function lists(req, res) {
blockedIps.length > 0
? [
{
key: "geoip/Belgium;BE",
source: "geoip",
description: "geo restrictions",
ips: blockedIps,
},
]
: [],
blockedUserAgents: blockedUserAgents,
monitoredUserAgents: monitoredUserAgents,
userAgentDetails: userAgentDetails,
allowedIPAddresses:
allowedIps.length > 0
? [
{
key: "geoip/Belgium;BE",
source: "geoip",
description: "geo restrictions",
ips: allowedIps,
},
]
: [],
monitoredIPAddresses:
monitoredIps.length > 0
? monitoredIps
: [
{
key: "geoip/Belgium;BE",
source: "geoip",
description: "geo restrictions",
ips: monitoredIps,
},
],
});
};
24 changes: 24 additions & 0 deletions end2end/server/src/handlers/updateLists.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ const {
updateBlockedIPAddresses,
updateBlockedUserAgents,
updateAllowedIPAddresses,
updateMonitoredUserAgents,
updateMonitoredIPAddresses,
updateUserAgentDetails,
} = require("../zen/config");

module.exports = function updateIPLists(req, res) {
Expand Down Expand Up @@ -46,5 +49,26 @@ module.exports = function updateIPLists(req, res) {
updateAllowedIPAddresses(req.app, req.body.allowedIPAddresses);
}

if (
req.body.monitoredUserAgents &&
typeof req.body.monitoredUserAgents === "string"
) {
updateMonitoredUserAgents(req.app, req.body.monitoredUserAgents);
}

if (
req.body.monitoredIPAddresses &&
Array.isArray(req.body.monitoredIPAddresses)
) {
updateMonitoredIPAddresses(req.app, req.body.monitoredIPAddresses);
}

if (
req.body.userAgentDetails &&
Array.isArray(req.body.userAgentDetails)
) {
updateUserAgentDetails(req.app, req.body.userAgentDetails);
}

res.json({ success: true });
};
85 changes: 83 additions & 2 deletions end2end/server/src/zen/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ function updateAppConfig(app, newConfig) {
const blockedIPAddresses = [];
const blockedUserAgents = [];
const allowedIPAddresses = [];
const monitoredUserAgents = [];
const monitoredIPAddresses = [];
const userAgentDetails = [];

function updateBlockedIPAddresses(app, ips) {
let entry = blockedIPAddresses.find((ip) => ip.serviceId === app.serviceId);
Expand Down Expand Up @@ -90,7 +93,7 @@ function getAllowedIPAddresses(app) {
}

function updateBlockedUserAgents(app, uas) {
let entry = blockedUserAgents.find((e) => e.serviceId === e.serviceId);
let entry = blockedUserAgents.find((e) => e.serviceId === app.serviceId);

if (entry) {
entry.userAgents = uas;
Expand All @@ -104,7 +107,7 @@ function updateBlockedUserAgents(app, uas) {
}

function getBlockedUserAgents(app) {
const entry = blockedUserAgents.find((e) => e.serviceId === e.serviceId);
const entry = blockedUserAgents.find((e) => e.serviceId === app.serviceId);

if (entry) {
return entry.userAgents;
Expand All @@ -113,6 +116,78 @@ function getBlockedUserAgents(app) {
return "";
}

function updateMonitoredUserAgents(app, uas) {
let entry = monitoredUserAgents.find((e) => e.serviceId === app.serviceId);

if (entry) {
entry.userAgents = uas;
} else {
entry = { serviceId: app.serviceId, userAgents: uas };
monitoredUserAgents.push(entry);
}

// Bump lastUpdatedAt
updateAppConfig(app, {});
}

function getMonitoredUserAgents(app) {
const entry = monitoredUserAgents.find((e) => e.serviceId === app.serviceId);

if (entry) {
return entry.userAgents;
}

return "";
}

function updateMonitoredIPAddresses(app, ips) {
let entry = monitoredIPAddresses.find((e) => e.serviceId === app.serviceId);

if (entry) {
entry.ipAddresses = ips;
} else {
entry = { serviceId: app.serviceId, ipAddresses: ips };
monitoredIPAddresses.push(entry);
}

// Bump lastUpdatedAt
updateAppConfig(app, {});
}

function getMonitoredIPAddresses(app) {
const entry = monitoredIPAddresses.find((e) => e.serviceId === app.serviceId);

if (entry) {
return entry.ipAddresses;
}

return [];
}

function updateUserAgentDetails(app, uas) {
let entry = userAgentDetails.find((e) => e.serviceId === app.serviceId);

if (entry) {
entry.userAgents = uas;
} else {
entry = { serviceId: app.serviceId, userAgents: uas };
userAgentDetails.push(entry);
}

// Bump lastUpdatedAt
updateAppConfig(app, {});
}

function getUserAgentDetails(app) {
const entry = userAgentDetails.find((e) => e.serviceId === app.serviceId);

if (entry) {
return entry.userAgents;
}

return [];
}

module.exports = {
getAppConfig,
updateAppConfig,
Expand All @@ -122,4 +197,10 @@ module.exports = {
getBlockedUserAgents,
getAllowedIPAddresses,
updateAllowedIPAddresses,
updateMonitoredUserAgents,
getMonitoredUserAgents,
updateMonitoredIPAddresses,
getMonitoredIPAddresses,
updateUserAgentDetails,
getUserAgentDetails,
};
151 changes: 151 additions & 0 deletions end2end/tests/hono-xml-monitored-lists.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
const t = require("tap");
const { spawn } = require("child_process");
const { resolve } = require("path");
const timeout = require("../timeout");

const pathToApp = resolve(__dirname, "../../sample-apps/hono-xml", "app.js");
const testServerUrl = "http://localhost:5874";

let token;
t.beforeEach(async () => {
const response = await fetch(`${testServerUrl}/api/runtime/apps`, {
method: "POST",
});
const body = await response.json();
token = body.token;

const lists = await fetch(`${testServerUrl}/api/runtime/firewall/lists`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: token,
},
body: JSON.stringify({
blockedIPAddresses: [],
monitoredIPAddresses: ["1.3.2.0/24", "e98c:a7ba:2329:8c69::/64"],
monitoredUserAgents: "monitored-bot",
userAgentDetails: [
{
key: "monitored-bot",
pattern: "monitored-bot",
},
],
}),
});
t.same(lists.status, 200);
});

t.test("it does not block monitored IPs", (t) => {
const server = spawn(`node`, [pathToApp, "4005"], {
env: {
...process.env,
AIKIDO_DEBUG: "true",
AIKIDO_BLOCKING: "true",
AIKIDO_TOKEN: token,
AIKIDO_ENDPOINT: testServerUrl,
},
});

server.on("close", () => {
t.end();
});

server.on("error", (err) => {
t.fail(err);
});

let stdout = "";
server.stdout.on("data", (data) => {
stdout += data.toString();
});

let stderr = "";
server.stderr.on("data", (data) => {
stderr += data.toString();
});

// Wait for the server to start
timeout(2000)
.then(async () => {
// Test IPv4 monitoring
const resp1 = await fetch("http://127.0.0.1:4005/add", {
method: "POST",
body: "<cat><name>Njuska</name></cat>",
headers: {
"Content-Type": "application/xml",
"X-Forwarded-For": "1.3.2.4",
},
signal: AbortSignal.timeout(5000),
});
t.same(resp1.status, 200);
t.same(await resp1.text(), JSON.stringify({ success: true }));

// Test IPv6 monitoring
const resp2 = await fetch("http://127.0.0.1:4005/add", {
method: "POST",
body: "<cat><name>Harry</name></cat>",
headers: {
"Content-Type": "application/xml",
"X-Forwarded-For": "e98c:a7ba:2329:8c69:a13a:8aff:a932:13f2",
},
signal: AbortSignal.timeout(5000),
});
t.same(resp2.status, 200);
t.same(await resp2.text(), JSON.stringify({ success: true }));
})
.catch((error) => {
t.fail(error);
})
.finally(() => {
server.kill();
});
});

t.test("it does not block monitored user agents", (t) => {
const server = spawn(`node`, [pathToApp, "4006"], {
env: {
...process.env,
AIKIDO_DEBUG: "true",
AIKIDO_BLOCKING: "true",
AIKIDO_TOKEN: token,
AIKIDO_ENDPOINT: testServerUrl,
},
});

server.on("close", () => {
t.end();
});

server.on("error", (err) => {
t.fail(err);
});

let stdout = "";
server.stdout.on("data", (data) => {
stdout += data.toString();
});

let stderr = "";
server.stderr.on("data", (data) => {
stderr += data.toString();
});

// Wait for the server to start
timeout(2000)
.then(async () => {
// Test monitored user agent
const resp1 = await fetch("http://127.0.0.1:4006/", {
headers: {
"User-Agent": "monitored-bot",
},
signal: AbortSignal.timeout(5000),
});
t.same(resp1.status, 200);
})
.catch((error) => {
t.fail(error);
})
.finally(() => {
server.kill();
});
});
Loading