Skip to content

Commit 053e8d0

Browse files
committed
Merge branch 'main' into AIK-4345-Add-a-way-to-only-allow-IP-traffic-from-certain-countries-Zen-for-Node
2 parents 621a685 + 689e46d commit 053e8d0

File tree

4 files changed

+137
-34
lines changed

4 files changed

+137
-34
lines changed

end2end/tests/hono-xml-blocklists.test.js

Lines changed: 105 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,41 @@ t.beforeEach(async () => {
1414
const body = await response.json();
1515
token = body.token;
1616

17-
// Apply rate limiting
18-
const updateConfigResponse = await fetch(
19-
`${testServerUrl}/api/runtime/firewall/lists`,
20-
{
21-
method: "POST",
22-
headers: {
23-
"Content-Type": "application/json",
24-
Authorization: token,
25-
},
26-
body: JSON.stringify({
27-
blockedIPAddresses: ["1.3.2.0/24", "fe80::1234:5678:abcd:ef12/64"],
28-
blockedUserAgents: "hacker|attacker|GPTBot",
29-
}),
30-
}
31-
);
32-
t.same(updateConfigResponse.status, 200);
17+
const config = await fetch(`${testServerUrl}/api/runtime/config`, {
18+
method: "POST",
19+
headers: {
20+
"Content-Type": "application/json",
21+
Authorization: token,
22+
},
23+
body: JSON.stringify({
24+
allowedIPAddresses: ["1.3.2.1", "1.3.2.2"],
25+
endpoints: [
26+
{
27+
route: "/admin",
28+
method: "GET",
29+
forceProtectionOff: false,
30+
allowedIPAddresses: ["1.3.2.1"],
31+
rateLimiting: {
32+
enabled: false,
33+
},
34+
},
35+
],
36+
}),
37+
});
38+
t.same(config.status, 200);
39+
40+
const lists = await fetch(`${testServerUrl}/api/runtime/firewall/lists`, {
41+
method: "POST",
42+
headers: {
43+
"Content-Type": "application/json",
44+
Authorization: token,
45+
},
46+
body: JSON.stringify({
47+
blockedIPAddresses: ["1.3.2.0/24", "fe80::1234:5678:abcd:ef12/64"],
48+
blockedUserAgents: "hacker|attacker|GPTBot",
49+
}),
50+
});
51+
t.same(lists.status, 200);
3352
});
3453

3554
t.test("it blocks geo restricted IPs", (t) => {
@@ -48,7 +67,7 @@ t.test("it blocks geo restricted IPs", (t) => {
4867
});
4968

5069
server.on("error", (err) => {
51-
t.fail(err.message);
70+
t.fail(err);
5271
});
5372

5473
let stdout = "";
@@ -107,7 +126,7 @@ t.test("it blocks geo restricted IPs", (t) => {
107126
t.same(await resp3.text(), JSON.stringify({ success: true }));
108127
})
109128
.catch((error) => {
110-
t.fail(error.message);
129+
t.fail(error);
111130
})
112131
.finally(() => {
113132
server.kill();
@@ -130,7 +149,7 @@ t.test("it blocks bots", (t) => {
130149
});
131150

132151
server.on("error", (err) => {
133-
t.fail(err.message);
152+
t.fail(err);
134153
});
135154

136155
let stdout = "";
@@ -190,7 +209,73 @@ t.test("it blocks bots", (t) => {
190209
}
191210
})
192211
.catch((error) => {
193-
t.fail(error.message);
212+
t.fail(error);
213+
})
214+
.finally(() => {
215+
server.kill();
216+
});
217+
});
218+
219+
t.test("it does not block bypass IP if in blocklist", (t) => {
220+
const server = spawn(`node`, [pathToApp, "4004"], {
221+
env: {
222+
...process.env,
223+
AIKIDO_DEBUG: "true",
224+
AIKIDO_BLOCKING: "true",
225+
AIKIDO_TOKEN: token,
226+
AIKIDO_URL: testServerUrl,
227+
},
228+
});
229+
230+
server.on("close", () => {
231+
t.end();
232+
});
233+
234+
server.on("error", (err) => {
235+
t.fail(err);
236+
});
237+
238+
let stdout = "";
239+
server.stdout.on("data", (data) => {
240+
stdout += data.toString();
241+
});
242+
243+
let stderr = "";
244+
server.stderr.on("data", (data) => {
245+
stderr += data.toString();
246+
});
247+
248+
// Wait for the server to start
249+
timeout(2000)
250+
.then(async () => {
251+
const resp1 = await fetch("http://127.0.0.1:4004/", {
252+
headers: {
253+
"X-Forwarded-For": "1.3.2.1",
254+
},
255+
signal: AbortSignal.timeout(5000),
256+
});
257+
t.same(resp1.status, 200);
258+
259+
const resp2 = await fetch("http://127.0.0.1:4004/admin", {
260+
headers: {
261+
"X-Forwarded-For": "1.3.2.1",
262+
},
263+
});
264+
t.same(resp2.status, 200);
265+
266+
const resp3 = await fetch("http://127.0.0.1:4004/admin", {
267+
headers: {
268+
"X-Forwarded-For": "1.3.2.2",
269+
},
270+
});
271+
t.same(resp3.status, 403);
272+
t.same(
273+
await resp3.text(),
274+
`Your IP address is not allowed to access this resource. (Your IP: 1.3.2.2)`
275+
);
276+
})
277+
.catch((error) => {
278+
t.fail(error);
194279
})
195280
.finally(() => {
196281
server.kill();

library/sources/http-server/checkIfRequestIsBlocked.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@ export function checkIfRequestIsBlocked(
2626
return false;
2727
}
2828

29-
if (
30-
context.remoteAddress &&
31-
agent.getConfig().shouldOnlyAllowSomeIPAddresses() &&
32-
!agent.getConfig().isOnlyAllowedIPAddress(context.remoteAddress)
33-
) {
29+
if (!ipAllowedToAccessRoute(context, agent)) {
3430
res.statusCode = 403;
3531
res.setHeader("Content-Type", "text/plain");
3632

@@ -44,15 +40,15 @@ export function checkIfRequestIsBlocked(
4440
return true;
4541
}
4642

47-
const result = context.remoteAddress
48-
? agent.getConfig().isIPAddressBlocked(context.remoteAddress)
49-
: ({ blocked: false } as const);
50-
51-
if (result.blocked) {
43+
if (
44+
context.remoteAddress &&
45+
agent.getConfig().shouldOnlyAllowSomeIPAddresses() &&
46+
!agent.getConfig().isOnlyAllowedIPAddress(context.remoteAddress)
47+
) {
5248
res.statusCode = 403;
5349
res.setHeader("Content-Type", "text/plain");
5450

55-
let message = `Your IP address is blocked due to ${escapeHTML(result.reason)}.`;
51+
let message = "Your IP address is not allowed to access this resource.";
5652
if (context.remoteAddress) {
5753
message += ` (Your IP: ${escapeHTML(context.remoteAddress)})`;
5854
}
@@ -62,11 +58,23 @@ export function checkIfRequestIsBlocked(
6258
return true;
6359
}
6460

65-
if (!ipAllowedToAccessRoute(context, agent)) {
61+
const isAllowedIP =
62+
context.remoteAddress &&
63+
agent.getConfig().isAllowedIP(context.remoteAddress);
64+
65+
if (isAllowedIP) {
66+
return false;
67+
}
68+
69+
const result = context.remoteAddress
70+
? agent.getConfig().isIPAddressBlocked(context.remoteAddress)
71+
: ({ blocked: false } as const);
72+
73+
if (result.blocked) {
6674
res.statusCode = 403;
6775
res.setHeader("Content-Type", "text/plain");
6876

69-
let message = "Your IP address is not allowed to access this resource.";
77+
let message = `Your IP address is blocked due to ${escapeHTML(result.reason)}.`;
7078
if (context.remoteAddress) {
7179
message += ` (Your IP: ${escapeHTML(context.remoteAddress)})`;
7280
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"scripts": {
99
"install": "node scripts/install.js",
1010
"install-lib-only": "node scripts/install.js --lib-only",
11-
"containers": "cd sample-apps && docker compose up -d --remove-orphans",
11+
"containers": "cd sample-apps && docker compose up -d --remove-orphans --build",
1212
"build": "node scripts/build.js",
1313
"watch": "cd library && npm run build:watch",
1414
"test": "cd library && npm run test",

sample-apps/hono-xml/app.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ async function main() {
100100
return c.json({ success: true });
101101
});
102102

103+
app.get("/admin", async (c) => {
104+
return c.html(
105+
`<html lang="en">
106+
<body>
107+
<h1>Admin panel</h1>
108+
</body>
109+
</html>`
110+
);
111+
});
112+
103113
app.post("/add-fast", async (c) => {
104114
const body = await c.req.text();
105115

0 commit comments

Comments
 (0)