diff --git a/src/main/java/org/opensearch/securityanalytics/services/STIX2IOCFetchService.java b/src/main/java/org/opensearch/securityanalytics/services/STIX2IOCFetchService.java index 479df34b5..2a18297fc 100644 --- a/src/main/java/org/opensearch/securityanalytics/services/STIX2IOCFetchService.java +++ b/src/main/java/org/opensearch/securityanalytics/services/STIX2IOCFetchService.java @@ -329,7 +329,7 @@ private String getEndpoint() { public void downloadFromUrlAndIndexIOCs(SATIFSourceConfig saTifSourceConfig, ActionListener listener) { UrlDownloadSource source = (UrlDownloadSource) saTifSourceConfig.getSource(); - switch (source.getFeedFormat()) { // todo add check to stop user from creating url type config from rest api. only internal allowed + switch (source.getFeedFormat()) { case "csv": try (CSVParser reader = ThreatIntelFeedParser.getThreatIntelFeedReaderCSV(source.getUrl())) { CSVParser noHeaderReader = ThreatIntelFeedParser.getThreatIntelFeedReaderCSV(source.getUrl()); diff --git a/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestIndexTIFSourceConfigAction.java b/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestIndexTIFSourceConfigAction.java index cf3630588..5d059dd99 100644 --- a/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestIndexTIFSourceConfigAction.java +++ b/src/main/java/org/opensearch/securityanalytics/threatIntel/resthandler/RestIndexTIFSourceConfigAction.java @@ -21,6 +21,7 @@ import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigAction; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigRequest; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigResponse; +import org.opensearch.securityanalytics.threatIntel.common.SourceConfigType; import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; import java.io.IOException; @@ -55,6 +56,15 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli SATIFSourceConfigDto tifConfig = SATIFSourceConfigDto.parse(xcp, id, null); + // Block URL_DOWNLOAD creation via REST API — reserved for internal use only + if (RestRequest.Method.POST.equals(request.method()) + && SourceConfigType.URL_DOWNLOAD.equals(tifConfig.getType())) { + return channel -> channel.sendResponse(new BytesRestResponse( + RestStatus.BAD_REQUEST, + "URL_DOWNLOAD source type cannot be created via the REST API. It is reserved for internal use only." + )); + } + SAIndexTIFSourceConfigRequest indexTIFConfigRequest = new SAIndexTIFSourceConfigRequest(id, request.method(), tifConfig); return channel -> client.execute(SAIndexTIFSourceConfigAction.INSTANCE, indexTIFConfigRequest, indexTIFConfigResponse(channel, request.method())); } diff --git a/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigActionTests.java b/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigActionTests.java index c8b8b29bd..f98e7e5b9 100644 --- a/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigActionTests.java +++ b/src/test/java/org/opensearch/securityanalytics/action/IndexTIFSourceConfigActionTests.java @@ -5,12 +5,59 @@ package org.opensearch.securityanalytics.action; import org.junit.Assert; +import org.junit.Test; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.rest.RestRequest; import org.opensearch.securityanalytics.threatIntel.action.SAIndexTIFSourceConfigAction; +import org.opensearch.securityanalytics.threatIntel.resthandler.RestIndexTIFSourceConfigAction; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.rest.FakeRestChannel; +import org.opensearch.test.rest.FakeRestRequest; +import org.opensearch.transport.client.node.NodeClient; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.opensearch.securityanalytics.SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI; public class IndexTIFSourceConfigActionTests extends OpenSearchTestCase { public void testIndexTIFSourceConfigActionName() { Assert.assertNotNull(SAIndexTIFSourceConfigAction.INSTANCE.name()); Assert.assertEquals(SAIndexTIFSourceConfigAction.INSTANCE.name(), SAIndexTIFSourceConfigAction.NAME); } + + @Test + public void testPrepareRequest_blockUrlDownloadCreate() throws Exception { + RestIndexTIFSourceConfigAction action = new RestIndexTIFSourceConfigAction(); + NodeClient client = mock(NodeClient.class); + + String requestBody = "{\n" + + " \"type\": \"URL_DOWNLOAD\",\n" + + " \"name\": \"test\",\n" + + " \"format\": \"STIX2\",\n" + + " \"source\": {\n" + + " \"url_download\": {\n" + + " \"url\": \"http://127.0.0.1:9200\",\n" + + " \"feed_format\": \"csv\"\n" + + " }\n" + + " },\n" + + " \"enabled_for_scan\": true,\n" + + " \"ioc_types\": [\"ip\"]\n" + + "}"; + + FakeRestRequest request = new FakeRestRequest.Builder(xContentRegistry()) + .withMethod(RestRequest.Method.POST) + .withPath(THREAT_INTEL_SOURCE_URI) + .withContent(new org.opensearch.core.common.bytes.BytesArray(requestBody), + org.opensearch.common.xcontent.XContentType.JSON) + .build(); + + FakeRestChannel channel = new FakeRestChannel(request, true, 1); + + action.handleRequest(request, channel, client); + + assertEquals(RestStatus.BAD_REQUEST, channel.capturedResponse().status()); + assertTrue(channel.capturedResponse().content().utf8ToString().contains("URL_DOWNLOAD")); + + verifyNoInteractions(client); + } } \ No newline at end of file diff --git a/src/test/java/org/opensearch/securityanalytics/resthandler/SATIFSourceConfigRestApiIT.java b/src/test/java/org/opensearch/securityanalytics/resthandler/SATIFSourceConfigRestApiIT.java index 9b95b261c..7bdab2b53 100644 --- a/src/test/java/org/opensearch/securityanalytics/resthandler/SATIFSourceConfigRestApiIT.java +++ b/src/test/java/org/opensearch/securityanalytics/resthandler/SATIFSourceConfigRestApiIT.java @@ -27,6 +27,7 @@ import org.opensearch.securityanalytics.threatIntel.model.S3Source; import org.opensearch.securityanalytics.threatIntel.model.SATIFSourceConfigDto; import org.opensearch.securityanalytics.threatIntel.model.Source; +import org.opensearch.securityanalytics.threatIntel.model.UrlDownloadSource; import org.opensearch.securityanalytics.util.STIX2IOCGenerator; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.regions.Region; @@ -39,6 +40,7 @@ import software.amazon.awssdk.services.s3.model.PutObjectResponse; import java.io.IOException; +import java.net.URL; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -163,6 +165,52 @@ public void afterTest() { s3Client.close(); } + public void testCreateUrlDownloadSourceConfig_blocked() throws Exception { + UrlDownloadSource urlDownloadSource = new UrlDownloadSource( + new URL("https://reputation.alienvault.com/reputation.generic"), + "csv", + true, + 0 + ); + + String feedName = "test_url_download_feed"; + String feedFormat = "STIX2"; + SourceConfigType sourceConfigType = SourceConfigType.URL_DOWNLOAD; + IntervalSchedule schedule = new IntervalSchedule(Instant.now(), 1, ChronoUnit.DAYS); + List iocTypes = List.of(IOCType.IPV4_TYPE); + + SATIFSourceConfigDto saTifSourceConfigDto = new SATIFSourceConfigDto( + null, + null, + feedName, + feedFormat, + sourceConfigType, + null, + null, + Instant.now(), + urlDownloadSource, + null, + Instant.now(), + schedule, + null, + null, + Instant.now(), + null, + true, + iocTypes, + true, + null + ); + + ResponseException exception = assertThrows(ResponseException.class, + () -> makeRequest(client(), "POST", SecurityAnalyticsPlugin.THREAT_INTEL_SOURCE_URI, + Collections.emptyMap(), toHttpEntity(saTifSourceConfigDto)) + ); + + assertEquals(RestStatus.BAD_REQUEST, restStatus(exception.getResponse())); + assertTrue(exception.getMessage().contains("URL_DOWNLOAD source type cannot be created via the REST API. It is reserved for internal use only.")); + } + public void testCreateSATIFSourceConfigAndVerifyJobRan() throws IOException, InterruptedException { // Only run tests when required system params are provided if (!canRunTests) return;