Skip to content

Commit b405311

Browse files
committed
Collect samples for attack wave detection
1 parent 6ca5f29 commit b405311

5 files changed

Lines changed: 48 additions & 11 deletions

File tree

library/agent/Agent.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ import { AttackLogger } from "./AttackLogger";
3030
import { Packages } from "./Packages";
3131
import { AIStatistics } from "./AIStatistics";
3232
import { isNewInstrumentationUnitTest } from "../helpers/isNewInstrumentationUnitTest";
33-
import { AttackWaveDetector } from "../vulnerabilities/attack-wave-detection/AttackWaveDetector";
33+
import {
34+
AttackWaveDetector,
35+
SuspiciousRequest,
36+
} from "../vulnerabilities/attack-wave-detection/AttackWaveDetector";
3437
import type { FetchListsAPI } from "./api/FetchListsAPI";
3538
import { PendingEvents } from "./PendingEvents";
3639

@@ -649,18 +652,19 @@ export class Agent {
649652
*/
650653
onDetectedAttackWave({
651654
request,
652-
metadata,
655+
samples,
653656
}: {
654657
request: Context;
655-
metadata: Record<string, string>;
658+
samples: SuspiciousRequest[];
656659
}) {
657660
const attack: DetectedAttackWave = {
658661
type: "detected_attack_wave",
659662
time: Date.now(),
660663
attack: {
661-
metadata: limitLengthMetadata(metadata, 4096),
664+
metadata: {},
662665
user: request.user,
663666
},
667+
samples: samples,
664668
request: {
665669
ipAddress: request.remoteAddress,
666670
userAgent:

library/agent/api/Event.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ export type DetectedAttackWave = {
166166
metadata: Record<string, string>;
167167
user: User | undefined;
168168
};
169+
samples: {
170+
method: string;
171+
url: string;
172+
}[];
169173
agent: AgentInfo;
170174
time: number;
171175
};

library/sources/http-server/createRequestListener.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,13 @@ function onFinishRequestHandler(
113113
agent.onRouteRateLimited(context.rateLimitedEndpoint);
114114
}
115115

116-
if (agent.getAttackWaveDetector().check(context)) {
117-
agent.onDetectedAttackWave({ request: context, metadata: {} });
116+
const attackWaveDetector = agent.getAttackWaveDetector();
117+
118+
if (attackWaveDetector.check(context) && context.remoteAddress) {
119+
agent.onDetectedAttackWave({
120+
request: context,
121+
samples: attackWaveDetector.getSamplesForIP(context.remoteAddress),
122+
});
118123
agent.getInspectionStatistics().onAttackWaveDetected();
119124
}
120125
}

library/sources/http-server/http2/createStreamListener.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,13 @@ function discoverRouteFromStream(
7676
agent.onRouteRateLimited(context.rateLimitedEndpoint);
7777
}
7878

79-
if (agent.getAttackWaveDetector().check(context)) {
80-
agent.onDetectedAttackWave({ request: context, metadata: {} });
79+
const attackWaveDetector = agent.getAttackWaveDetector();
80+
81+
if (attackWaveDetector.check(context) && context.remoteAddress) {
82+
agent.onDetectedAttackWave({
83+
request: context,
84+
samples: attackWaveDetector.getSamplesForIP(context.remoteAddress),
85+
});
8186
agent.getInspectionStatistics().onAttackWaveDetected();
8287
}
8388
}

library/vulnerabilities/attack-wave-detection/AttackWaveDetector.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ import { LRUMap } from "../../ratelimiting/LRUMap";
22
import type { Context } from "../../agent/Context";
33
import { isWebScanner } from "./isWebScanner";
44

5+
export type SuspiciousRequest = {
6+
method: string;
7+
url: string;
8+
};
9+
510
export class AttackWaveDetector {
6-
private suspiciousRequestsMap: LRUMap<string, number>;
11+
private suspiciousRequestsMap: LRUMap<string, SuspiciousRequest[]>;
712
private sentEventsMap: LRUMap<string, number>;
813

914
// How many suspicious requests are allowed before triggering an alert
@@ -57,19 +62,33 @@ export class AttackWaveDetector {
5762
return false;
5863
}
5964

65+
// In isWebScanner we use `context.route`, `context.route` is always created from `context.url`
66+
if (!context.method || !context.url) {
67+
return false;
68+
}
69+
6070
if (!isWebScanner(context)) {
6171
return false;
6272
}
6373

64-
const suspiciousRequests = (this.suspiciousRequestsMap.get(ip) || 0) + 1;
74+
const suspiciousRequests = this.suspiciousRequestsMap.get(ip) || [];
75+
suspiciousRequests.push({
76+
method: context.method,
77+
url: context.url,
78+
});
79+
6580
this.suspiciousRequestsMap.set(ip, suspiciousRequests);
6681

67-
if (suspiciousRequests < this.attackWaveThreshold) {
82+
if (suspiciousRequests.length < this.attackWaveThreshold) {
6883
return false;
6984
}
7085

7186
this.sentEventsMap.set(ip, performance.now());
7287

7388
return true;
7489
}
90+
91+
getSamplesForIP(ip: string) {
92+
return this.suspiciousRequestsMap.get(ip) || [];
93+
}
7594
}

0 commit comments

Comments
 (0)