Skip to content

Commit 7855f5c

Browse files
committed
Add client side rate limiting for attack wave
1 parent 1c7d73b commit 7855f5c

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

library/agent/api/ReportingAPIRateLimitedClientSide.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,45 @@ function generateAttackEvent(): Event {
5757
};
5858
}
5959

60+
function generateAttackWaveEvent(): Event {
61+
return {
62+
type: "detected_attack_wave",
63+
time: Date.now(),
64+
request: {
65+
ipAddress: undefined,
66+
userAgent: undefined,
67+
source: "express",
68+
},
69+
attack: {
70+
metadata: {},
71+
user: undefined,
72+
},
73+
agent: {
74+
version: "1.0.0",
75+
library: "firewall-node",
76+
dryMode: false,
77+
hostname: "hostname",
78+
packages: {},
79+
ipAddress: "ipAddress",
80+
preventedPrototypePollution: false,
81+
nodeEnv: "",
82+
os: {
83+
name: "os",
84+
version: "version",
85+
},
86+
serverless: false,
87+
incompatiblePackages: {
88+
prototypePollution: {},
89+
},
90+
stack: [],
91+
platform: {
92+
version: "version",
93+
arch: "arch",
94+
},
95+
},
96+
};
97+
}
98+
6099
t.test("it throttles attack events", async () => {
61100
const api = new ReportingAPIForTesting();
62101
const token = new Token("123");
@@ -250,3 +289,59 @@ t.test("it does not blow memory", async () => {
250289
// @ts-expect-error Private field but we need to check the length
251290
t.same(throttled.events.length, 10);
252291
});
292+
293+
t.test("it throttles attack wave events", async () => {
294+
const api = new ReportingAPIForTesting();
295+
const token = new Token("123");
296+
297+
const throttled = new ReportingAPIRateLimitedClientSide(api, {
298+
maxEventsPerInterval: 5,
299+
intervalInMs: 1000,
300+
});
301+
302+
t.same(api.getEvents().length, 0);
303+
await throttled.report(token, generateAttackWaveEvent(), 5000);
304+
t.same(api.getEvents().length, 1);
305+
await throttled.report(token, generateAttackWaveEvent(), 5000);
306+
t.same(api.getEvents().length, 2);
307+
await throttled.report(token, generateAttackWaveEvent(), 5000);
308+
t.same(api.getEvents().length, 3);
309+
await throttled.report(token, generateAttackWaveEvent(), 5000);
310+
t.same(api.getEvents().length, 4);
311+
await throttled.report(token, generateAttackWaveEvent(), 5000);
312+
t.same(api.getEvents().length, 5);
313+
await throttled.report(token, generateAttackWaveEvent(), 5000);
314+
t.same(api.getEvents().length, 5);
315+
316+
await new Promise((resolve) => setTimeout(resolve, 1000));
317+
await throttled.report(token, generateAttackWaveEvent(), 5000);
318+
t.same(api.getEvents().length, 6);
319+
});
320+
321+
t.test("it throttles both event types at the same time", async () => {
322+
const api = new ReportingAPIForTesting();
323+
const token = new Token("123");
324+
325+
const throttled = new ReportingAPIRateLimitedClientSide(api, {
326+
maxEventsPerInterval: 5,
327+
intervalInMs: 1000,
328+
});
329+
330+
t.same(api.getEvents().length, 0);
331+
await throttled.report(token, generateAttackWaveEvent(), 5000);
332+
t.same(api.getEvents().length, 1);
333+
await throttled.report(token, generateAttackEvent(), 5000);
334+
t.same(api.getEvents().length, 2);
335+
await throttled.report(token, generateAttackWaveEvent(), 5000);
336+
t.same(api.getEvents().length, 3);
337+
await throttled.report(token, generateAttackEvent(), 5000);
338+
t.same(api.getEvents().length, 4);
339+
await throttled.report(token, generateAttackWaveEvent(), 5000);
340+
t.same(api.getEvents().length, 5);
341+
await throttled.report(token, generateAttackEvent(), 5000);
342+
t.same(api.getEvents().length, 5);
343+
344+
await new Promise((resolve) => setTimeout(resolve, 1000));
345+
await throttled.report(token, generateAttackEvent(), 5000);
346+
t.same(api.getEvents().length, 6);
347+
});

library/agent/api/ReportingAPIRateLimitedClientSide.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ export class ReportingAPIRateLimitedClientSide implements ReportingAPI {
2222
event: Event,
2323
timeoutInMS: number
2424
): Promise<ReportingAPIResponse> {
25-
if (event.type === "detected_attack") {
25+
if (
26+
event.type === "detected_attack" ||
27+
event.type === "detected_attack_wave"
28+
) {
2629
const currentTime = Date.now();
2730

2831
// Filter out events that are outside the current interval

0 commit comments

Comments
 (0)