Skip to content

Commit 9c68f1f

Browse files
authored
Merge pull request #1030 from AikidoSec/update-firewall-tester-action
Update QA tests to v1.0.16
2 parents 924235c + 7999b60 commit 9c68f1f

11 files changed

Lines changed: 69 additions & 106 deletions

File tree

.github/workflows/qa-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
cp firewall-node/.github/workflows/Dockerfile.qa zen-demo-nodejs/Dockerfile
5252
5353
- name: Run Firewall QA Tests
54-
uses: AikidoSec/firewall-tester-action@d6ce69ab0d6b52cac12d01be9b25603de492bfc5 # v1 branch
54+
uses: AikidoSec/firewall-tester-action@af36cba78b7a99542b3f1e011c30322a7d47ad7a # v1.0.16
5555
with:
5656
dockerfile_path: ./zen-demo-nodejs/Dockerfile
5757
app_port: 3000

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,8 @@ t.test("it does not block bypass IP if in blocklist", (t) => {
316316
"X-Forwarded-For": "1.3.2.2",
317317
},
318318
});
319-
t.same(resp3.status, 403);
320-
t.same(
321-
await resp3.text(),
322-
`Your IP address is not allowed to access this resource. (Your IP: 1.3.2.2)`
323-
);
319+
t.same(resp3.status, 200);
320+
t.match(await resp3.text(), "Admin panel");
324321

325322
// IPv4-mapped IPv6 address should also bypass (matches bypass list 1.3.2.1)
326323
const resp4 = await fetch("http://127.0.0.1:4004/", {

library/agent/Hostnames.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { hostnameToUnicode } from "../helpers/hostnameToUnicode";
12
import { normalizeHostname } from "../helpers/normalizeHostname";
23

34
type Ports = Map<number, number>;
@@ -12,7 +13,7 @@ export class Hostnames {
1213
return;
1314
}
1415

15-
hostname = normalizeHostname(hostname);
16+
hostname = hostnameToUnicode(normalizeHostname(hostname));
1617

1718
if (!this.map.has(hostname)) {
1819
this.map.set(hostname, new Map([[port, 1]]));

library/agent/ServiceConfig.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { addIPv4MappedAddresses } from "../helpers/addIPv4MappedAddresses";
2+
import { hostnameToUnicode } from "../helpers/hostnameToUnicode";
23
import { IPMatcher } from "../helpers/ip-matcher/IPMatcher";
34
import { LimitedContext, matchEndpoints } from "../helpers/matchEndpoints";
45
import { normalizeHostname } from "../helpers/normalizeHostname";
@@ -297,7 +298,9 @@ export class ServiceConfig {
297298
}
298299

299300
shouldBlockOutgoingRequest(hostname: string): boolean {
300-
const mode = this.domains.get(normalizeHostname(hostname));
301+
const mode = this.domains.get(
302+
hostnameToUnicode(normalizeHostname(hostname))
303+
);
301304

302305
if (this.blockNewOutgoingRequests) {
303306
// Only allow outgoing requests if the mode is "allow"

library/agent/hooks/onInspectionInterceptorResult.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,35 +28,39 @@ export function onInspectionInterceptorResult(
2828
) {
2929
const end = performance.now();
3030

31+
const isBypassedIP =
32+
context &&
33+
context.remoteAddress &&
34+
agent.getConfig().isBypassedIP(context.remoteAddress);
35+
3136
if (kind) {
3237
agent.getInspectionStatistics().onInspectedCall({
3338
operation: operation,
3439
kind: kind,
35-
attackDetected: !!result,
40+
attackDetected: !isBypassedIP && !!result,
3641
blocked: agent.shouldBlock(),
3742
durationInMs: end - start,
3843
withoutContext: !context,
3944
});
4045
}
4146

42-
const isBypassedIP =
43-
context &&
44-
context.remoteAddress &&
45-
agent.getConfig().isBypassedIP(context.remoteAddress);
47+
if (isBypassedIP) {
48+
return;
49+
}
4650

47-
if (isIdorViolationResult(result) && !isBypassedIP) {
51+
if (isIdorViolationResult(result)) {
4852
throw cleanError(new Error(result.message));
4953
}
5054

51-
if (isBlockOutboundConnectionResult(result) && !isBypassedIP) {
55+
if (isBlockOutboundConnectionResult(result)) {
5256
throw cleanError(
5357
new Error(
5458
`Zen has blocked an outbound connection: ${result.operation}(...) to ${escapeHTML(result.hostname)}`
5559
)
5660
);
5761
}
5862

59-
if (isAttackResult(result) && context && !isBypassedIP) {
63+
if (isAttackResult(result) && context) {
6064
// Flag request as having an attack detected
6165
updateContext(context, "attackDetected", true);
6266

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { domainToUnicode } from "url";
2+
3+
/**
4+
* Converts a punycode hostname to its unicode form.
5+
* e.g. "xn--mnchen-3ya.example.com" -> "münchen.example.com"
6+
*
7+
* Returns the original hostname if conversion fails or produces an empty result.
8+
*/
9+
export function hostnameToUnicode(hostname: string): string {
10+
try {
11+
const unicode = domainToUnicode(hostname);
12+
if (unicode) {
13+
return unicode;
14+
}
15+
} catch {
16+
// Ignore
17+
}
18+
19+
return hostname;
20+
}

library/middleware/shouldBlockRequest.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ export function shouldBlockRequest(): Result {
4343
updateContext(context, "executedMiddleware", true);
4444
agent.onMiddlewareExecuted();
4545

46+
const isBypassedIP =
47+
context.remoteAddress &&
48+
agent.getConfig().isBypassedIP(context.remoteAddress);
49+
50+
if (isBypassedIP) {
51+
return { block: false };
52+
}
53+
4654
if (context.user && agent.getConfig().isUserBlocked(context.user.id)) {
4755
return { block: true, type: "blocked", trigger: "user" };
4856
}

library/ratelimiting/shouldRateLimitRequest.test.ts

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -184,37 +184,6 @@ t.test("it rate limits localhost when not in production mode", async (t) => {
184184
});
185185
});
186186

187-
t.test("it does not rate limit when the IP is allowed", async (t) => {
188-
const agent = await createAgent(
189-
[
190-
{
191-
method: "POST",
192-
route: "/login",
193-
forceProtectionOff: false,
194-
rateLimiting: {
195-
enabled: true,
196-
maxRequests: 3,
197-
windowSizeInMS: 1000,
198-
},
199-
},
200-
],
201-
["1.2.3.4"]
202-
);
203-
204-
t.same(shouldRateLimitRequest(createContext("1.2.3.4"), agent), {
205-
block: false,
206-
});
207-
t.same(shouldRateLimitRequest(createContext("1.2.3.4"), agent), {
208-
block: false,
209-
});
210-
t.same(shouldRateLimitRequest(createContext("1.2.3.4"), agent), {
211-
block: false,
212-
});
213-
t.same(shouldRateLimitRequest(createContext("1.2.3.4"), agent), {
214-
block: false,
215-
});
216-
});
217-
218187
t.test("it rate limits by user", async (t) => {
219188
const agent = await createAgent([
220189
{
@@ -439,40 +408,6 @@ t.test(
439408
}
440409
);
441410

442-
t.test(
443-
"it does not rate limit requests from allowed ip with user",
444-
async (t) => {
445-
const agent = await createAgent(
446-
[
447-
{
448-
method: "POST",
449-
route: "/login",
450-
forceProtectionOff: false,
451-
rateLimiting: {
452-
enabled: true,
453-
maxRequests: 3,
454-
windowSizeInMS: 1000,
455-
},
456-
},
457-
],
458-
["1.2.3.4"]
459-
);
460-
461-
t.same(shouldRateLimitRequest(createContext("1.2.3.4", "123"), agent), {
462-
block: false,
463-
});
464-
t.same(shouldRateLimitRequest(createContext("1.2.3.4", "123"), agent), {
465-
block: false,
466-
});
467-
t.same(shouldRateLimitRequest(createContext("1.2.3.4", "123"), agent), {
468-
block: false,
469-
});
470-
t.same(shouldRateLimitRequest(createContext("1.2.3.4", "123"), agent), {
471-
block: false,
472-
});
473-
}
474-
);
475-
476411
t.test(
477412
"it does not consume rate limit for user a second time (same request)",
478413
async (t) => {

library/ratelimiting/shouldRateLimitRequest.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,7 @@ export function shouldRateLimitRequest(
5555
isLocalhostIP(context.remoteAddress) &&
5656
isProduction;
5757

58-
// Allow requests from allowed IPs, e.g. never rate limit office IPs
59-
const isBypassedIP =
60-
context.remoteAddress &&
61-
agent.getConfig().isBypassedIP(context.remoteAddress);
62-
63-
if (isFromLocalhostInProduction || isBypassedIP) {
58+
if (isFromLocalhostInProduction) {
6459
return { block: false };
6560
}
6661

library/sources/http-server/blockIPsAndBots.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ export function blockIPsAndBots(
3939
// Also ensures that the statistics are only counted once
4040
res[checkedBlocks] = true;
4141

42+
const isBypassedIP =
43+
context.remoteAddress &&
44+
agent.getConfig().isBypassedIP(context.remoteAddress);
45+
46+
if (isBypassedIP) {
47+
return false;
48+
}
49+
4250
if (!ipAllowedToAccessRoute(context, agent)) {
4351
res.statusCode = 403;
4452
res.setHeader("Content-Type", "text/plain");
@@ -53,14 +61,6 @@ export function blockIPsAndBots(
5361
return true;
5462
}
5563

56-
const isBypassedIP =
57-
context.remoteAddress &&
58-
agent.getConfig().isBypassedIP(context.remoteAddress);
59-
60-
if (isBypassedIP) {
61-
return false;
62-
}
63-
6464
if (
6565
context.remoteAddress &&
6666
!agent.getConfig().isAllowedIPAddress(context.remoteAddress).allowed

0 commit comments

Comments
 (0)