Skip to content

Commit a9423f8

Browse files
authored
make threat intel run with standard detectors (opensearch-project#1234)
Signed-off-by: Subhobrata Dey <sbcd90@gmail.com>
1 parent 1483883 commit a9423f8

File tree

3 files changed

+159
-4
lines changed

3 files changed

+159
-4
lines changed

src/main/java/org/opensearch/securityanalytics/threatIntel/transport/monitor/TransportGetThreatIntelAlertsAction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ protected void doExecute(Task task, GetThreatIntelAlertsRequest request, ActionL
108108
SearchRequest threatIntelMonitorsSearchRequest = new SearchRequest();
109109
threatIntelMonitorsSearchRequest.indices(".opendistro-alerting-config");
110110
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
111-
boolQueryBuilder.should().add(new BoolQueryBuilder().must(QueryBuilders.matchPhraseQuery("monitor.owner", PLUGIN_OWNER_FIELD)));
112-
boolQueryBuilder.should().add(new BoolQueryBuilder().must(QueryBuilders.matchPhraseQuery("monitor.monitor_type", ThreatIntelMonitorRunner.THREAT_INTEL_MONITOR_TYPE)));
111+
boolQueryBuilder.must().add(new BoolQueryBuilder().must(QueryBuilders.matchPhraseQuery("monitor.owner", PLUGIN_OWNER_FIELD)));
112+
boolQueryBuilder.must().add(new BoolQueryBuilder().must(QueryBuilders.matchPhraseQuery("monitor.monitor_type", ThreatIntelMonitorRunner.THREAT_INTEL_MONITOR_TYPE)));
113113
threatIntelMonitorsSearchRequest.source(new SearchSourceBuilder().query(boolQueryBuilder));
114114
transportSearchThreatIntelMonitorAction.execute(new SearchThreatIntelMonitorRequest(threatIntelMonitorsSearchRequest), ActionListener.wrap(
115115
searchResponse -> {

src/main/java/org/opensearch/securityanalytics/threatIntel/transport/monitor/TransportIndexThreatIntelMonitorAction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ protected void doExecute(Task task, IndexThreatIntelMonitorRequest request, Acti
118118
SearchRequest threatIntelMonitorsSearchRequest = new SearchRequest();
119119
threatIntelMonitorsSearchRequest.indices(".opendistro-alerting-config");
120120
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
121-
boolQueryBuilder.should().add(new BoolQueryBuilder().must(QueryBuilders.matchQuery("monitor.owner", PLUGIN_OWNER_FIELD)));
122-
boolQueryBuilder.should().add(new BoolQueryBuilder().must(QueryBuilders.matchQuery("monitor.monitor_type", ThreatIntelMonitorRunner.THREAT_INTEL_MONITOR_TYPE)));
121+
boolQueryBuilder.must().add(new BoolQueryBuilder().must(QueryBuilders.matchQuery("monitor.owner", PLUGIN_OWNER_FIELD)));
122+
boolQueryBuilder.must().add(new BoolQueryBuilder().must(QueryBuilders.matchQuery("monitor.monitor_type", ThreatIntelMonitorRunner.THREAT_INTEL_MONITOR_TYPE)));
123123
threatIntelMonitorsSearchRequest.source(new SearchSourceBuilder().query(boolQueryBuilder));
124124
transportSearchThreatIntelMonitorAction.execute(new SearchThreatIntelMonitorRequest(threatIntelMonitorsSearchRequest), ActionListener.wrap(
125125
searchResponse -> {

src/test/java/org/opensearch/securityanalytics/resthandler/ThreatIntelMonitorRestApiIT.java

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@
22

33
import org.apache.hc.core5.http.ContentType;
44
import org.apache.hc.core5.http.io.entity.StringEntity;
5+
import org.apache.http.HttpStatus;
56
import org.junit.Assert;
7+
import org.opensearch.client.Request;
68
import org.opensearch.client.Response;
79
import org.opensearch.client.ResponseException;
810
import org.opensearch.common.settings.Settings;
911
import org.opensearch.common.xcontent.XContentFactory;
1012
import org.opensearch.commons.alerting.model.IntervalSchedule;
1113
import org.opensearch.commons.alerting.model.Monitor;
14+
import org.opensearch.core.rest.RestStatus;
1215
import org.opensearch.core.xcontent.ToXContent;
1316
import org.opensearch.search.SearchHit;
1417
import org.opensearch.securityanalytics.SecurityAnalyticsPlugin;
1518
import org.opensearch.securityanalytics.SecurityAnalyticsRestTestCase;
1619
import org.opensearch.securityanalytics.action.ListIOCsActionRequest;
1720
import org.opensearch.securityanalytics.commons.model.IOCType;
21+
import org.opensearch.securityanalytics.model.Detector;
22+
import org.opensearch.securityanalytics.model.DetectorTrigger;
1823
import org.opensearch.securityanalytics.model.STIX2IOC;
1924
import org.opensearch.securityanalytics.threatIntel.common.RefreshType;
2025
import org.opensearch.securityanalytics.threatIntel.common.SourceConfigType;
@@ -37,6 +42,8 @@
3742
import java.util.Map;
3843

3944
import static java.util.Collections.emptyList;
45+
import static org.opensearch.securityanalytics.TestHelpers.randomDetectorType;
46+
import static org.opensearch.securityanalytics.TestHelpers.randomDetectorWithTriggers;
4047
import static org.opensearch.securityanalytics.TestHelpers.randomIndex;
4148
import static org.opensearch.securityanalytics.TestHelpers.windowsIndexMapping;
4249
import static org.opensearch.securityanalytics.threatIntel.resthandler.monitor.RestSearchThreatIntelMonitorAction.SEARCH_THREAT_INTEL_MONITOR_PATH;
@@ -422,6 +429,154 @@ public void testCreateThreatIntelMonitor() throws IOException {
422429
assertEquals(totalHitsVal.intValue(), 0);
423430
}
424431

432+
public void testCreateThreatIntelMonitorWithExistingDetector() throws IOException {
433+
String index = createTestIndex(randomIndex(), windowsIndexMapping());
434+
435+
// Execute CreateMappingsAction to add alias mapping for index
436+
Request createMappingRequest = new Request("POST", SecurityAnalyticsPlugin.MAPPER_BASE_URI);
437+
// both req params and req body are supported
438+
createMappingRequest.setJsonEntity(
439+
"{ \"index_name\":\"" + index + "\"," +
440+
" \"rule_topic\":\"" + randomDetectorType() + "\", " +
441+
" \"partial\":true" +
442+
"}"
443+
);
444+
445+
Response response = client().performRequest(createMappingRequest);
446+
assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
447+
448+
Detector detector = randomDetectorWithTriggers(getRandomPrePackagedRules(), List.of(new DetectorTrigger(null, "test-trigger", "1", List.of(randomDetectorType()), List.of(), List.of(), List.of(), List.of(), List.of())));
449+
450+
Response createResponse = makeRequest(client(), "POST", SecurityAnalyticsPlugin.DETECTOR_BASE_URI, Collections.emptyMap(), toHttpEntity(detector));
451+
Assert.assertEquals("Create detector failed", RestStatus.CREATED, restStatus(createResponse));
452+
453+
Response iocFindingsResponse = makeRequest(client(), "GET", SecurityAnalyticsPlugin.THREAT_INTEL_BASE_URI + "/findings/_search",
454+
Map.of(), null);
455+
Map<String, Object> responseAsMap = responseAsMap(iocFindingsResponse);
456+
Assert.assertEquals(0, ((List<Map<String, Object>>) responseAsMap.get("ioc_findings")).size());
457+
List<String> vals = List.of("ip1", "ip2");
458+
indexSourceConfigsAndIocs(1, vals);
459+
String monitorName = "test_monitor_name";
460+
461+
462+
/**create monitor */
463+
ThreatIntelMonitorDto iocScanMonitor = randomIocScanMonitorDto(index);
464+
response = makeRequest(client(), "POST", SecurityAnalyticsPlugin.THREAT_INTEL_MONITOR_URI, Collections.emptyMap(), toHttpEntity(iocScanMonitor));
465+
Assert.assertEquals(201, response.getStatusLine().getStatusCode());
466+
Map<String, Object> responseBody = asMap(response);
467+
468+
try {
469+
makeRequest(client(), "POST", SecurityAnalyticsPlugin.THREAT_INTEL_MONITOR_URI, Collections.emptyMap(), toHttpEntity(iocScanMonitor));
470+
fail();
471+
} catch (Exception e) {
472+
/** creating a second threat intel monitor should fail*/
473+
assertTrue(e.getMessage().contains("already exists"));
474+
}
475+
476+
final String monitorId = responseBody.get("id").toString();
477+
Assert.assertNotEquals("response is missing Id", Monitor.NO_ID, monitorId);
478+
479+
Response alertingMonitorResponse = getAlertingMonitor(client(), monitorId);
480+
Assert.assertEquals(200, alertingMonitorResponse.getStatusLine().getStatusCode());
481+
int i = 1;
482+
for (String val : vals) {
483+
String doc = String.format("{\"ip\":\"%s\", \"ip1\":\"%s\"}", val, val);
484+
try {
485+
indexDoc(index, "" + i++, doc);
486+
} catch (IOException e) {
487+
fail();
488+
}
489+
}
490+
491+
Response executeResponse = executeAlertingMonitor(monitorId, Collections.emptyMap());
492+
Map<String, Object> executeResults = entityAsMap(executeResponse);
493+
assertEquals(1, 1);
494+
495+
String matchAllRequest = getMatchAllRequest();
496+
Response searchMonitorResponse = makeRequest(client(), "POST", SEARCH_THREAT_INTEL_MONITOR_PATH, Collections.emptyMap(), new StringEntity(matchAllRequest, ContentType.APPLICATION_JSON));
497+
Assert.assertEquals(200, alertingMonitorResponse.getStatusLine().getStatusCode());
498+
HashMap<String, Object> hits = (HashMap<String, Object>) asMap(searchMonitorResponse).get("hits");
499+
HashMap<String, Object> totalHits = (HashMap<String, Object>) hits.get("total");
500+
Integer totalHitsVal = (Integer) totalHits.get("value");
501+
assertEquals(totalHitsVal.intValue(), 1);
502+
makeRequest(client(), "POST", SEARCH_THREAT_INTEL_MONITOR_PATH, Collections.emptyMap(), new StringEntity(matchAllRequest, ContentType.APPLICATION_JSON));
503+
504+
505+
iocFindingsResponse = makeRequest(client(), "GET", SecurityAnalyticsPlugin.THREAT_INTEL_BASE_URI + "/findings/_search",
506+
Map.of(), null);
507+
responseAsMap = responseAsMap(iocFindingsResponse);
508+
Assert.assertEquals(2, ((List<Map<String, Object>>) responseAsMap.get("ioc_findings")).size());
509+
510+
//alerts
511+
List<SearchHit> searchHits = executeSearch(ThreatIntelAlertService.THREAT_INTEL_ALERT_ALIAS_NAME, matchAllRequest);
512+
Assert.assertEquals(4, searchHits.size());
513+
514+
for (String val : vals) {
515+
String doc = String.format("{\"ip\":\"%s\", \"ip1\":\"%s\"}", val, val);
516+
try {
517+
indexDoc(index, "" + i++, doc);
518+
} catch (IOException e) {
519+
fail();
520+
}
521+
}
522+
executeAlertingMonitor(monitorId, Collections.emptyMap());
523+
iocFindingsResponse = makeRequest(client(), "GET", SecurityAnalyticsPlugin.THREAT_INTEL_BASE_URI + "/findings/_search",
524+
Map.of(), null);
525+
responseAsMap = responseAsMap(iocFindingsResponse);
526+
Assert.assertEquals(4, ((List<Map<String, Object>>) responseAsMap.get("ioc_findings")).size());
527+
528+
// Use ListIOCs API to confirm expected number of findings are returned
529+
String listIocsUri = String.format("?%s=%s", ListIOCsActionRequest.FEED_IDS_FIELD, "id0");
530+
Response listIocsResponse = makeRequest(client(), "GET", SecurityAnalyticsPlugin.LIST_IOCS_URI + listIocsUri, Collections.emptyMap(), null);
531+
Map<String, Object> listIocsResponseMap = responseAsMap(listIocsResponse);
532+
List<Map<String, Object>> iocsMap = (List<Map<String, Object>>) listIocsResponseMap.get("iocs");
533+
assertEquals(2, iocsMap.size());
534+
iocsMap.forEach((iocDetails) -> {
535+
String iocId = (String) iocDetails.get("id");
536+
int numFindings = (Integer) iocDetails.get("num_findings");
537+
assertTrue(testIocs.stream().anyMatch(ioc -> iocId.equals(ioc.getId())));
538+
assertEquals(2, numFindings);
539+
});
540+
541+
//alerts via system index search
542+
searchHits = executeSearch(ThreatIntelAlertService.THREAT_INTEL_ALERT_ALIAS_NAME, matchAllRequest);
543+
Assert.assertEquals(4, searchHits.size());
544+
545+
// alerts via API
546+
Map<String, String> params = new HashMap<>();
547+
Response getAlertsResponse = makeRequest(client(), "GET", SecurityAnalyticsPlugin.THREAT_INTEL_ALERTS_URI, params, null);
548+
Map<String, Object> getAlertsBody = asMap(getAlertsResponse);
549+
Assert.assertEquals(4, getAlertsBody.get("total_alerts"));
550+
551+
552+
ThreatIntelMonitorDto updateMonitorDto = new ThreatIntelMonitorDto(
553+
monitorId,
554+
iocScanMonitor.getName() + "update",
555+
iocScanMonitor.getPerIocTypeScanInputList(),
556+
new IntervalSchedule(5, ChronoUnit.MINUTES, Instant.now()),
557+
false,
558+
null,
559+
List.of(iocScanMonitor.getTriggers().get(0), iocScanMonitor.getTriggers().get(1))
560+
);
561+
//update monitor
562+
response = makeRequest(client(), "PUT", SecurityAnalyticsPlugin.THREAT_INTEL_MONITOR_URI + "/" + monitorId, Collections.emptyMap(), toHttpEntity(updateMonitorDto));
563+
Assert.assertEquals(200, response.getStatusLine().getStatusCode());
564+
responseBody = asMap(response);
565+
assertEquals(responseBody.get("id").toString(), monitorId);
566+
assertEquals(((HashMap<String, Object>) responseBody.get("monitor")).get("name").toString(), iocScanMonitor.getName() + "update");
567+
568+
//delete
569+
Response delete = makeRequest(client(), "DELETE", SecurityAnalyticsPlugin.THREAT_INTEL_MONITOR_URI + "/" + monitorId, Collections.emptyMap(), null);
570+
Assert.assertEquals(200, delete.getStatusLine().getStatusCode());
571+
572+
searchMonitorResponse = makeRequest(client(), "POST", SEARCH_THREAT_INTEL_MONITOR_PATH, Collections.emptyMap(), new StringEntity(matchAllRequest, ContentType.APPLICATION_JSON));
573+
Assert.assertEquals(200, alertingMonitorResponse.getStatusLine().getStatusCode());
574+
hits = (HashMap<String, Object>) asMap(searchMonitorResponse).get("hits");
575+
totalHits = (HashMap<String, Object>) hits.get("total");
576+
totalHitsVal = (Integer) totalHits.get("value");
577+
assertEquals(totalHitsVal.intValue(), 0);
578+
}
579+
425580
public void testCreateThreatIntelMonitor_invalidMonitorJson() throws IOException {
426581
ThreatIntelMonitorDto iocScanMonitor = randomIocScanMonitorDto("test-index");
427582

0 commit comments

Comments
 (0)