Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
import static dev.aikido.agent_api.helpers.env.Endpoints.getAikidoAPIEndpoint;

public class BackgroundProcess extends Thread {
private static final int API_TIMEOUT = 10; // 10 seconds
private static final int API_TIMEOUT = 30; // GET config and lists
private static final int EVENTS_API_TIMEOUT = 60; // POST events

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium - 60-second event timeout lets stalled reporting exhaust the attack queue

When the event endpoint accepts a connection but stalls, an attacker can keep generating detected attacks while the sole queue consumer waits up to 60 seconds on each POST. The queue is unbounded and producers continue accepting events, so sustained attack traffic can retain enough event payloads to exhaust the agent heap and degrade the instrumented application; the previous 10-second timeout drained six times faster.

Suggested change
private static final int EVENTS_API_TIMEOUT = 60; // POST events
private static final int EVENTS_API_TIMEOUT = 10; // POST events

More info - Reply on this comment to give feedback or ignore the issue.

private static final int POLLING_INTERVAL_SECONDS = 60; // Check for realtime config changes every 1 minute
private static final int FIRST_HEARTBEAT_INTERVAL_SECONDS = 30;
private static final int SECOND_HEARTBEAT_INTERVAL_SECONDS = 120;
Expand All @@ -41,7 +42,7 @@ public void run() {
}
logger.trace("Starting agent thread: %s", Thread.currentThread());

ReportingApiHTTP api = new ReportingApiHTTP(getAikidoAPIEndpoint(token), API_TIMEOUT, token);
ReportingApiHTTP api = new ReportingApiHTTP(getAikidoAPIEndpoint(token), API_TIMEOUT, EVENTS_API_TIMEOUT, token);
RealtimeAPI realtimeApi = new RealtimeAPI(token);

// make api calls on start
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

public class RealtimeAPI {
private static final Logger logger = LogManager.getLogger(RealtimeAPI.class);
private static final int timeoutInSec = 3; // 3 sec timeout for requests to the realtime endpoint
private static final int timeoutInSec = 30;
private final String endpoint;
private final Token token;
private HttpClient httpClient;
Expand Down Expand Up @@ -63,6 +63,7 @@ public Optional<ConfigResponse> toConfigResponse(HttpResponse<String> res) {
private static HttpRequest createConfigRequest(String token, URI uri) {
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.uri(uri) // Change to your target URL
.timeout(Duration.ofSeconds(timeoutInSec))
.header("Content-Type", "application/json") // Set Content-Type header
.header("Authorization", token); // Set Authorization header
AgentHeaders.get().forEach(requestBuilder::header);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ public class ReportingApiHTTP extends ReportingApi {
private final Token token;
private final Gson gson = new Gson();
private HttpClient httpClient;
public ReportingApiHTTP(String reportingUrl, int timeoutInSec, Token token) {
private final int eventsTimeoutInSec;
public ReportingApiHTTP(String reportingUrl, int timeoutInSec, int eventsTimeoutInSec, Token token) {
// Reporting URL should end with trailing slash for now.
super(timeoutInSec);
this.eventsTimeoutInSec = eventsTimeoutInSec;
this.reportingUrl = reportingUrl;
this.token = token;
}
Expand Down Expand Up @@ -125,9 +127,10 @@ public APIResponse toApiResponse(HttpResponse<String> res) {
return getUnsuccessfulAPIResponse("unknown_error");
}
private HttpRequest createHttpRequest(Optional<APIEvent> event, URI uri) {
int requestTimeoutInSec = event.isPresent() ? eventsTimeoutInSec : timeoutInSec;
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.uri(uri) // Change to your target URL
.timeout(Duration.ofSeconds(timeoutInSec))
.timeout(Duration.ofSeconds(requestTimeoutInSec))
.header("Content-Type", "application/json") // Set Content-Type header
.header("Authorization", token.get()); // Set Authorization header
AgentHeaders.get().forEach(requestBuilder::header);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void testReusesConnectionsAcrossCloudRequests() throws Exception {
});
server.start();
try {
ReportingApiHTTP api = new ReportingApiHTTP("http://127.0.0.1:" + server.getAddress().getPort() + "/", 2, new Token("token"));
ReportingApiHTTP api = new ReportingApiHTTP("http://127.0.0.1:" + server.getAddress().getPort() + "/", 2, 2, new Token("token"));
record Event(String type) implements APIEvent {}
for (int i = 0; i < 10; i++) {
assertTrue(api.fetchNewConfig().isPresent());
Expand Down Expand Up @@ -80,7 +80,7 @@ private void assertClosesUnfinishedResponse(String status, String headers, Strin
return socket.getInputStream().read();
}
});
ReportingApiHTTP api = new ReportingApiHTTP("http://localhost:" + server.getLocalPort() + "/", 1, new Token("token"));
ReportingApiHTTP api = new ReportingApiHTTP("http://localhost:" + server.getLocalPort() + "/", 1, 1, new Token("token"));
assertTrue(api.fetchBlockedLists().isEmpty());
assertEquals(-1, disconnected.get(4, TimeUnit.SECONDS));
} finally {
Expand All @@ -106,7 +106,7 @@ public void testReadsGzippedLists() throws Exception {
});
server.start();
try {
ReportingApiHTTP api = new ReportingApiHTTP("http://127.0.0.1:" + server.getAddress().getPort() + "/", 2, new Token("token"));
ReportingApiHTTP api = new ReportingApiHTTP("http://127.0.0.1:" + server.getAddress().getPort() + "/", 2, 2, new Token("token"));
assertEquals("bot", api.fetchBlockedLists().orElseThrow().blockedUserAgents());
} finally {
server.stop(0);
Expand Down
12 changes: 6 additions & 6 deletions agent_api/src/test/java/background/cloud/ReportingAPITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ public class ReportingAPITest {
ReportingApiHTTP api;
@BeforeEach
public void setup() {
api = new ReportingApiHTTP("http://localhost:5000/", 2, new Token("token"));
api = new ReportingApiHTTP("http://localhost:5000/", 2, 2, new Token("token"));
}

@Test
public void testTimeoutValid() {
api = new ReportingApiHTTP("http://localhost:5000/delayed/2/", 3, new Token("token")); // Allowed delay
api = new ReportingApiHTTP("http://localhost:5000/delayed/2/", 3, 3, new Token("token")); // Allowed delay
Optional<APIResponse> res = api.fetchNewConfig();
assertTrue(res.isPresent());
assertTrue(res.get().block());
assertEquals(3, res.get().endpoints().size());
}
@Test
public void testTimeoutInvalid() {
api = new ReportingApiHTTP("http://localhost:5000/delayed/4/", 3, new Token("token")); // Allowed delay
api = new ReportingApiHTTP("http://localhost:5000/delayed/4/", 3, 3, new Token("token")); // Allowed delay
Optional<APIResponse> res = api.fetchNewConfig();
assertFalse(res.isPresent());
}
Expand All @@ -53,7 +53,7 @@ public void testFetchNewConfig() {
@Test
@StdIo
public void testFetchNewConfigInvalidEndpoint(StdOut out) {
this.api = new ReportingApiHTTP("http://unknown.app.here:1234/", 2, new Token("token"));
this.api = new ReportingApiHTTP("http://unknown.app.here:1234/", 2, 2, new Token("token"));
Optional<APIResponse> res = api.fetchNewConfig();
assertEquals(Optional.empty(), res);
assertTrue(
Expand All @@ -65,7 +65,7 @@ public void testFetchNewConfigInvalidEndpoint(StdOut out) {
@Test
@StdIo
public void testListsResponseWithWrongEndpoint(StdOut out) {
this.api = new ReportingApiHTTP("http://unknown.app.here:1234/", 2, new Token("token"));
this.api = new ReportingApiHTTP("http://unknown.app.here:1234/", 2, 2, new Token("token"));
Optional<ReportingApiHTTP.APIListsResponse> res = api.fetchBlockedLists();
assertEquals(Optional.empty(), res);
assertTrue(
Expand Down Expand Up @@ -114,7 +114,7 @@ public void testSendsAgentHeaders() throws Exception {
});
server.start();
try {
api = new ReportingApiHTTP("http://localhost:" + server.getAddress().getPort() + "/", 2, new Token("token"));
api = new ReportingApiHTTP("http://localhost:" + server.getAddress().getPort() + "/", 2, 2, new Token("token"));
api.fetchNewConfig();
api.fetchBlockedLists();
} finally {
Expand Down
Loading