From 5a78c92c9a898c7aad5f6bf8f04ee98483276bc9 Mon Sep 17 00:00:00 2001 From: BitterPanda Date: Tue, 25 Nov 2025 14:44:21 +0100 Subject: [PATCH] Move attack wave detection to the WebResponseCollector --- .../collectors/WebRequestCollector.java | 8 ---- .../collectors/WebResponseCollector.java | 21 ++++++++- .../collectors/WebRequestCollectorTest.java | 38 --------------- .../collectors/WebResponseCollectorTest.java | 47 +++++++++++++++++++ 4 files changed, 66 insertions(+), 48 deletions(-) diff --git a/agent_api/src/main/java/dev/aikido/agent_api/collectors/WebRequestCollector.java b/agent_api/src/main/java/dev/aikido/agent_api/collectors/WebRequestCollector.java index dae3a2376..d331965f2 100644 --- a/agent_api/src/main/java/dev/aikido/agent_api/collectors/WebRequestCollector.java +++ b/agent_api/src/main/java/dev/aikido/agent_api/collectors/WebRequestCollector.java @@ -60,14 +60,6 @@ public static Res report(ContextObject newContext) { if (blockedUARes != null) return blockedUARes; - // Check for attack waves - if (AttackWaveDetectorStore.check(newContext)) { - AttackQueue.add( - DetectedAttackWave.createAPIEvent(newContext) - ); - StatisticsStore.incrementAttackWavesDetected(); - } - return null; } diff --git a/agent_api/src/main/java/dev/aikido/agent_api/collectors/WebResponseCollector.java b/agent_api/src/main/java/dev/aikido/agent_api/collectors/WebResponseCollector.java index d8ccd8325..c48c1d66c 100644 --- a/agent_api/src/main/java/dev/aikido/agent_api/collectors/WebResponseCollector.java +++ b/agent_api/src/main/java/dev/aikido/agent_api/collectors/WebResponseCollector.java @@ -1,10 +1,14 @@ package dev.aikido.agent_api.collectors; import dev.aikido.agent_api.api_discovery.APISpec; +import dev.aikido.agent_api.background.cloud.api.events.DetectedAttackWave; import dev.aikido.agent_api.context.Context; import dev.aikido.agent_api.context.ContextObject; import dev.aikido.agent_api.context.RouteMetadata; +import dev.aikido.agent_api.storage.AttackQueue; +import dev.aikido.agent_api.storage.attack_wave_detector.AttackWaveDetectorStore; import dev.aikido.agent_api.storage.routes.RoutesStore; +import dev.aikido.agent_api.storage.statistics.StatisticsStore; import static dev.aikido.agent_api.api_discovery.GetApiInfo.getApiInfo; import static dev.aikido.agent_api.helpers.url.IsUsefulRoute.isUsefulRoute; @@ -20,9 +24,22 @@ private WebResponseCollector() {} */ public static void report(int statusCode) { ContextObject context = Context.get(); - if (statusCode <= 0 || context == null) { + if (context == null) { + return; + } + + // Check for attack waves + if (AttackWaveDetectorStore.check(context)) { + AttackQueue.add( + DetectedAttackWave.createAPIEvent(context) + ); + StatisticsStore.incrementAttackWavesDetected(); + } + + if (statusCode <= 0) { return; // Status code below or equal to zero: Invalid request } + RouteMetadata routeMetadata = context.getRouteMetadata(); if (routeMetadata == null || !isUsefulRoute(statusCode, context.getRoute(), context.getMethod())) { return; @@ -37,4 +54,4 @@ public static void report(int statusCode) { RoutesStore.updateApiSpec(routeMetadata, apiSpec); } } -} \ No newline at end of file +} diff --git a/agent_api/src/test/java/collectors/WebRequestCollectorTest.java b/agent_api/src/test/java/collectors/WebRequestCollectorTest.java index e4c56a7e1..75999d50a 100644 --- a/agent_api/src/test/java/collectors/WebRequestCollectorTest.java +++ b/agent_api/src/test/java/collectors/WebRequestCollectorTest.java @@ -260,42 +260,4 @@ void testReport_ipNotAllowedUsingLists_Ip_Bypassed() { assertNull(response); assertNull(Context.get()); } - - @Test - void testReport_WithAttackWaveContext() throws InterruptedException { - ContextObject attackWaveCtx = new EmptySampleContextObject("/wp-config.php", "BADMETHOD", Map.of()); - - WebRequestCollector.Res response = WebRequestCollector.report(attackWaveCtx); - assertNull(response); - assertEquals(0, AttackQueue.getSize()); - assertEquals(0, StatisticsStore.getStatsRecord().requests().attackWaves().total()); - - // 2...14 - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - WebRequestCollector.report(attackWaveCtx); - - WebRequestCollector.Res response2 = WebRequestCollector.report(attackWaveCtx); - assertNull(response2); - assertEquals(1, AttackQueue.getSize()); - DetectedAttackWave.DetectedAttackWaveEvent event = (DetectedAttackWave.DetectedAttackWaveEvent) AttackQueue.get(); - assertEquals("192.168.1.1", event.request().ipAddress()); - assertEquals("web", event.request().source()); - assertEquals(null, event.request().userAgent()); - assertEquals("detected_attack_wave", event.type()); - assertEquals(null, event.attack().user()); - assertEquals(0, event.attack().metadata().size()); - // check stats changed - assertEquals(1, StatisticsStore.getStatsRecord().requests().attackWaves().total()); - } } diff --git a/agent_api/src/test/java/collectors/WebResponseCollectorTest.java b/agent_api/src/test/java/collectors/WebResponseCollectorTest.java index 657b85145..119f4d8e5 100644 --- a/agent_api/src/test/java/collectors/WebResponseCollectorTest.java +++ b/agent_api/src/test/java/collectors/WebResponseCollectorTest.java @@ -1,15 +1,22 @@ package collectors; +import dev.aikido.agent_api.SetUser; +import dev.aikido.agent_api.background.cloud.api.events.DetectedAttackWave; +import dev.aikido.agent_api.collectors.WebRequestCollector; import dev.aikido.agent_api.collectors.WebResponseCollector; import dev.aikido.agent_api.context.Context; import dev.aikido.agent_api.context.ContextObject; import dev.aikido.agent_api.context.RouteMetadata; +import dev.aikido.agent_api.storage.AttackQueue; import dev.aikido.agent_api.storage.routes.RoutesStore; +import dev.aikido.agent_api.storage.statistics.StatisticsStore; import org.junit.jupiter.api.*; import org.junit.jupiter.api.Test; +import utils.EmptySampleContextObject; import java.sql.SQLException; import java.util.HashMap; +import java.util.Map; import static org.junit.jupiter.api.Assertions.*; @@ -92,5 +99,45 @@ public void testResponseCollectorWithInvalidMethodOrStatusCode() throws SQLExcep WebResponseCollector.report(-200); assertEquals(0, RoutesStore.getRoutesAsList().length); } + @Test + void testReport_WithAttackWaveContext() throws InterruptedException { + ContextObject attackWaveCtx = new EmptySampleContextObject("/wp-config.php", "BADMETHOD", Map.of()); + Context.set(attackWaveCtx); + + SetUser.setUser(new SetUser.UserObject("123", "Jane Doe")); + + WebResponseCollector.report(500); + assertEquals(0, AttackQueue.getSize()); + assertEquals(0, StatisticsStore.getStatsRecord().requests().attackWaves().total()); + + // 2...14 + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + WebResponseCollector.report(500); + + WebResponseCollector.report(500); + assertEquals(1, AttackQueue.getSize()); + DetectedAttackWave.DetectedAttackWaveEvent event = (DetectedAttackWave.DetectedAttackWaveEvent) AttackQueue.get(); + assertEquals("192.168.1.1", event.request().ipAddress()); + assertEquals("web", event.request().source()); + assertEquals(null, event.request().userAgent()); + assertEquals("detected_attack_wave", event.type()); + assertEquals("123", event.attack().user().id()); + assertEquals("Jane Doe", event.attack().user().name()); + + assertEquals(0, event.attack().metadata().size()); + // check stats changed + assertEquals(1, StatisticsStore.getStatsRecord().requests().attackWaves().total()); + } }