Skip to content

Commit a8bc36a

Browse files
committed
Cleanup URLCollector & perform simplified check in DNSRecordCollector
also adds test cases for DNSRecordCollector
1 parent ee001fa commit a8bc36a

3 files changed

Lines changed: 48 additions & 34 deletions

File tree

agent_api/src/main/java/dev/aikido/agent_api/collectors/DNSRecordCollector.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import dev.aikido.agent_api.storage.statistics.StatisticsStore;
88
import dev.aikido.agent_api.vulnerabilities.Attack;
99
import dev.aikido.agent_api.vulnerabilities.ssrf.SSRFDetector;
10+
import dev.aikido.agent_api.vulnerabilities.outbound_blocking.BlockedOutboundException;
1011
import dev.aikido.agent_api.vulnerabilities.ssrf.SSRFException;
1112
import dev.aikido.agent_api.helpers.logging.LogManager;
1213
import dev.aikido.agent_api.helpers.logging.Logger;
@@ -31,6 +32,12 @@ public static void report(String hostname, InetAddress[] inetAddresses) {
3132
// store stats
3233
StatisticsStore.registerCall("java.net.InetAddress.getAllByName", OperationKind.OUTGOING_HTTP_OP);
3334

35+
// Block if the hostname is in the blocked domains list
36+
if (ServiceConfigStore.shouldBlockOutgoingRequest(hostname)) {
37+
logger.debug("Blocking DNS lookup for domain: %s", hostname);
38+
throw BlockedOutboundException.get();
39+
}
40+
3441
// Convert inetAddresses array to a List of IP strings :
3542
List<String> ipAddresses = new ArrayList<>();
3643
for (InetAddress inetAddress : inetAddresses) {
@@ -77,7 +84,7 @@ public static void report(String hostname, InetAddress[] inetAddresses) {
7784
}
7885
}
7986

80-
} catch (SSRFException | StoredSSRFException e) {
87+
} catch (BlockedOutboundException | SSRFException | StoredSSRFException e) {
8188
throw e;
8289
} catch (Throwable e) {
8390
logger.trace(e);

agent_api/src/main/java/dev/aikido/agent_api/collectors/URLCollector.java

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,15 @@
22

33
import dev.aikido.agent_api.context.Context;
44
import dev.aikido.agent_api.context.ContextObject;
5-
import dev.aikido.agent_api.context.User;
65
import dev.aikido.agent_api.storage.HostnamesStore;
76
import dev.aikido.agent_api.helpers.logging.LogManager;
87
import dev.aikido.agent_api.helpers.logging.Logger;
98
import dev.aikido.agent_api.storage.ServiceConfigStore;
10-
import dev.aikido.agent_api.vulnerabilities.Attack;
11-
import dev.aikido.agent_api.vulnerabilities.Vulnerabilities;
129
import dev.aikido.agent_api.vulnerabilities.outbound_blocking.BlockedOutboundException;
1310

1411
import java.net.URL;
15-
import java.util.Map;
1612

17-
import static dev.aikido.agent_api.helpers.ShouldBlockHelper.shouldBlock;
18-
import static dev.aikido.agent_api.helpers.StackTrace.getCurrentStackTrace;
1913
import static dev.aikido.agent_api.helpers.url.PortParser.getPortFromURL;
20-
import static dev.aikido.agent_api.storage.AttackQueue.attackDetected;
2114

2215
public final class URLCollector {
2316
private static final Logger logger = LogManager.getLogger(URLCollector.class);
@@ -34,33 +27,7 @@ public static void report(URL url, String operation) {
3427
// We store hostname and port in two places, HostnamesStore and Context. HostnamesStore is for reporting
3528
// outbound domains. Context is to have a map of hostnames with used port numbers to detect SSRF attacks.
3629

37-
// hostname blocking :
3830
String hostname = url.getHost();
39-
if (ServiceConfigStore.shouldBlockOutgoingRequest(hostname)) {
40-
ContextObject ctx = Context.get();
41-
42-
User currentUser = null;
43-
if (ctx != null) {
44-
currentUser = ctx.getUser();
45-
}
46-
47-
Attack attack = new Attack(
48-
operation,
49-
Vulnerabilities.SSRF,
50-
"",
51-
"",
52-
Map.of(),
53-
/* payload */ hostname,
54-
getCurrentStackTrace(),
55-
currentUser
56-
);
57-
58-
attackDetected(attack, ctx);
59-
if (shouldBlock()) {
60-
logger.debug("Blocking request to domain: %s", hostname);
61-
throw BlockedOutboundException.get();
62-
}
63-
};
6431

6532
// Store (new) hostname hits
6633
HostnamesStore.incrementHits(hostname, port);

agent_api/src/test/java/collectors/DNSRecordCollectorTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package collectors;
22

3+
import dev.aikido.agent_api.background.cloud.api.APIResponse;
34
import dev.aikido.agent_api.background.cloud.api.events.DetectedAttack;
45
import dev.aikido.agent_api.collectors.DNSRecordCollector;
56
import dev.aikido.agent_api.context.Context;
@@ -8,7 +9,9 @@
89
import dev.aikido.agent_api.storage.Hostnames;
910
import dev.aikido.agent_api.storage.HostnamesStore;
1011
import dev.aikido.agent_api.storage.ServiceConfigStore;
12+
import dev.aikido.agent_api.storage.service_configuration.Domain;
1113
import dev.aikido.agent_api.vulnerabilities.Attack;
14+
import dev.aikido.agent_api.vulnerabilities.outbound_blocking.BlockedOutboundException;
1215
import dev.aikido.agent_api.vulnerabilities.ssrf.SSRFException;
1316
import dev.aikido.agent_api.vulnerabilities.ssrf.StoredSSRFException;
1417
import org.junit.jupiter.api.*;
@@ -40,6 +43,10 @@ public void cleanup() {
4043
HostnamesStore.clear();
4144
Context.set(null);
4245
AttackQueue.clear();
46+
// Reset domain config
47+
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
48+
true, null, 0L, null, null, null, false, List.of(), true, false
49+
));
4350
}
4451

4552
@Test
@@ -135,6 +142,39 @@ public void testHostnameSameWithContextAsAStoredSSRFAttack() {
135142
});
136143
}
137144

145+
@Test
146+
public void testBlockedDomain() {
147+
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
148+
true, null, 0L, null, null, null,
149+
false, List.of(new Domain("blocked.example.com", "block")), true, true
150+
));
151+
assertThrows(BlockedOutboundException.class, () ->
152+
DNSRecordCollector.report("blocked.example.com", new InetAddress[]{inetAddress1})
153+
);
154+
}
155+
156+
@Test
157+
public void testAllowedDomainNotBlocked() {
158+
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
159+
true, null, 0L, null, null, null,
160+
false, List.of(new Domain("allowed.example.com", "allow")), true, true
161+
));
162+
assertDoesNotThrow(() ->
163+
DNSRecordCollector.report("allowed.example.com", new InetAddress[]{inetAddress1})
164+
);
165+
}
166+
167+
@Test
168+
public void testUnknownDomainBlockedWhenBlockNewOutgoingRequests() {
169+
ServiceConfigStore.updateFromAPIResponse(new APIResponse(
170+
true, null, 0L, null, null, null,
171+
true, List.of(), true, true
172+
));
173+
assertThrows(BlockedOutboundException.class, () ->
174+
DNSRecordCollector.report("unknown.example.com", new InetAddress[]{inetAddress1})
175+
);
176+
}
177+
138178
@Test
139179
public void testStoredSSRFWithNoContext() throws InterruptedException {
140180
ServiceConfigStore.updateBlocking(true);

0 commit comments

Comments
 (0)