|
2 | 2 |
|
3 | 3 | import org.apache.hc.core5.http.ContentType; |
4 | 4 | import org.apache.hc.core5.http.io.entity.StringEntity; |
| 5 | +import org.apache.http.HttpStatus; |
5 | 6 | import org.junit.Assert; |
| 7 | +import org.opensearch.client.Request; |
6 | 8 | import org.opensearch.client.Response; |
7 | 9 | import org.opensearch.client.ResponseException; |
8 | 10 | import org.opensearch.common.settings.Settings; |
9 | 11 | import org.opensearch.common.xcontent.XContentFactory; |
10 | 12 | import org.opensearch.commons.alerting.model.IntervalSchedule; |
11 | 13 | import org.opensearch.commons.alerting.model.Monitor; |
| 14 | +import org.opensearch.core.rest.RestStatus; |
12 | 15 | import org.opensearch.core.xcontent.ToXContent; |
13 | 16 | import org.opensearch.search.SearchHit; |
14 | 17 | import org.opensearch.securityanalytics.SecurityAnalyticsPlugin; |
15 | 18 | import org.opensearch.securityanalytics.SecurityAnalyticsRestTestCase; |
16 | 19 | import org.opensearch.securityanalytics.action.ListIOCsActionRequest; |
17 | 20 | import org.opensearch.securityanalytics.commons.model.IOCType; |
| 21 | +import org.opensearch.securityanalytics.model.Detector; |
| 22 | +import org.opensearch.securityanalytics.model.DetectorTrigger; |
18 | 23 | import org.opensearch.securityanalytics.model.STIX2IOC; |
19 | 24 | import org.opensearch.securityanalytics.threatIntel.common.RefreshType; |
20 | 25 | import org.opensearch.securityanalytics.threatIntel.common.SourceConfigType; |
|
37 | 42 | import java.util.Map; |
38 | 43 |
|
39 | 44 | import static java.util.Collections.emptyList; |
| 45 | +import static org.opensearch.securityanalytics.TestHelpers.randomDetectorType; |
| 46 | +import static org.opensearch.securityanalytics.TestHelpers.randomDetectorWithTriggers; |
40 | 47 | import static org.opensearch.securityanalytics.TestHelpers.randomIndex; |
41 | 48 | import static org.opensearch.securityanalytics.TestHelpers.windowsIndexMapping; |
42 | 49 | import static org.opensearch.securityanalytics.threatIntel.resthandler.monitor.RestSearchThreatIntelMonitorAction.SEARCH_THREAT_INTEL_MONITOR_PATH; |
@@ -422,6 +429,154 @@ public void testCreateThreatIntelMonitor() throws IOException { |
422 | 429 | assertEquals(totalHitsVal.intValue(), 0); |
423 | 430 | } |
424 | 431 |
|
| 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 | + |
425 | 580 | public void testCreateThreatIntelMonitor_invalidMonitorJson() throws IOException { |
426 | 581 | ThreatIntelMonitorDto iocScanMonitor = randomIocScanMonitorDto("test-index"); |
427 | 582 |
|
|
0 commit comments