diff --git a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/AsyncResource.java b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/AsyncResource.java index a5de82d0e6..3fc243bbff 100644 --- a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/AsyncResource.java +++ b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/AsyncResource.java @@ -26,6 +26,7 @@ import java.util.concurrent.ArrayBlockingQueue; import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.Consumes; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; @@ -109,6 +110,7 @@ public ArrayBlockingQueue getEmitDataQueue(int size) { * @throws Exception */ @POST + @Consumes("application/json") @Produces("application/json") public Response post(InputStream is, @Context HttpHeaders httpHeaders, @Context UriInfo info) throws Exception { diff --git a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/PipesResource.java b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/PipesResource.java index b7f8081a3d..9a6bc79060 100644 --- a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/PipesResource.java +++ b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/PipesResource.java @@ -25,6 +25,7 @@ import java.util.Map; import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.Consumes; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; @@ -84,6 +85,7 @@ public PipesResource(PipesParser pipesParser, PipesConfig pipesConfig) { * @throws Exception */ @POST + @Consumes("application/json") @Produces("application/json") public Response postRmeta(InputStream is, @Context HttpHeaders httpHeaders, @Context UriInfo info) throws Exception { FetchEmitTuple t = null; diff --git a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/AsyncResourceTest.java b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/AsyncResourceTest.java index aa5d2a8b7f..8e8dd373a8 100644 --- a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/AsyncResourceTest.java +++ b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/AsyncResourceTest.java @@ -135,6 +135,7 @@ private Response post(String body) { return WebClient .create(endPoint + ASYNC_PATH) .accept("application/json") + .type("application/json") .post(body); } diff --git a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/PipesRequestLimitsTest.java b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/PipesRequestLimitsTest.java index 8a9690eac3..1053eabe2f 100644 --- a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/PipesRequestLimitsTest.java +++ b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/PipesRequestLimitsTest.java @@ -141,6 +141,7 @@ public void testRequestMayNotRaiseWriteLimit() throws Exception { Response response = WebClient .create(endPoint + "/pipes") .accept("application/json") + .type("application/json") .post(tuple); assertEquals(200, response.getStatus()); diff --git a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaPipesTest.java b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaPipesTest.java index 8a6657358d..d45b687253 100644 --- a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaPipesTest.java +++ b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaPipesTest.java @@ -26,6 +26,10 @@ import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -194,6 +198,7 @@ public void testPost() throws Exception { Response response = WebClient .create(getUrl) .accept("application/json") + .type("application/json") .post(writer.toString()); assertEquals(200, response.getStatus()); @@ -234,6 +239,7 @@ public void testPostXML() throws Exception { Response response = WebClient .create(getUrl) .accept("application/json") + .type("application/json") .post(writer.toString()); assertEquals(200, response.getStatus()); @@ -265,6 +271,7 @@ public void testPostNPE() throws Exception { Response response = WebClient .create(getUrl) .accept("application/json") + .type("application/json") .post(writer.toString()); assertEquals(200, response.getStatus()); @@ -299,6 +306,7 @@ public void testPostNPENoEmit() throws Exception { Response response = WebClient .create(getUrl) .accept("application/json") + .type("application/json") .post(writer.toString()); assertEquals(200, response.getStatus()); @@ -334,6 +342,7 @@ private Response postTuple(FetchEmitTuple t) throws Exception { return WebClient .create(endPoint + PIPES_PATH) .accept("application/json") + .type("application/json") .post(writer.toString()); } @@ -370,6 +379,7 @@ public void testMalformedBodyIs400() throws Exception { Response response = WebClient .create(endPoint + PIPES_PATH) .accept("application/json") + .type("application/json") .post("this is not json"); assertEquals(400, response.getStatus()); assertContains("/pipes request body", @@ -383,10 +393,36 @@ public void testUnknownTupleFieldIs400() throws Exception { Response response = WebClient .create(endPoint + PIPES_PATH) .accept("application/json") + .type("application/json") .post(body); assertEquals(400, response.getStatus()); assertContains("bogusField", getStringFromInputStream((InputStream) response.getEntity())); } + /** + * A web page can POST to /pipes without a CORS preflight only with a text/plain, + * form-urlencoded or multipart content type, or with none at all; refusing those + * keeps a browser on the server's network from running tuples cross-site. + */ + @Test + public void testCrossSiteSimpleRequestIsRefused() throws Exception { + FetchEmitTuple t = new FetchEmitTuple("myId", + new FetchKey(FETCHER_ID, HELLO_WORLD), new EmitKey(EMITTER_JSON_ID, ""), new Metadata()); + StringWriter writer = new StringWriter(); + JsonFetchEmitTuple.toJson(t, writer); + HttpClient client = HttpClient.newHttpClient(); + for (String contentType : new String[]{"text/plain", "application/x-www-form-urlencoded", null}) { + HttpRequest.Builder request = HttpRequest + .newBuilder(URI.create(endPoint + PIPES_PATH)) + .POST(HttpRequest.BodyPublishers.ofString(writer.toString())); + if (contentType != null) { + request.header("Content-Type", contentType); + } + HttpResponse response = client.send(request.build(), HttpResponse.BodyHandlers.ofString()); + assertEquals(415, response.statusCode(), String.valueOf(contentType)); + assertFalse(Files.exists(tmpOutputFile), String.valueOf(contentType)); + } + } + } diff --git a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerPipesIntegrationTest.java b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerPipesIntegrationTest.java index 7c4cdedebc..b96e352b91 100644 --- a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerPipesIntegrationTest.java +++ b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerPipesIntegrationTest.java @@ -125,6 +125,7 @@ private JsonNode testOneWithPerRequestTimeout(String fileName, long timeoutMilli Response response = WebClient .create(endPoint + "/pipes") .accept("application/json") + .type("application/json") .post(getJsonStringWithTimeout(fileName, timeoutMillis)); assertEquals(expectedStatus, response.getStatus()); // a crash emits nothing, even with ON_PARSE_EXCEPTION.EMIT @@ -160,6 +161,7 @@ private JsonNode testOne(String fileName, boolean shouldFileExist, Response response = WebClient .create(endPoint + "/pipes") .accept("application/json") + .type("application/json") .post(getJsonString(fileName, onParseException)); assertEquals(expectedStatus, response.getStatus()); Path targFile = TEMP_OUTPUT_DIR.resolve(fileName + ".json"); @@ -189,6 +191,7 @@ public void testPipesRejectsParserInjection() throws Exception { Response response = WebClient .create(endPoint + "/pipes") .accept("application/json") + .type("application/json") .post(requestJson); assertTrue(response.getStatus() != 200, "parser injection must be rejected, got HTTP " + response.getStatus()); diff --git a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaPipesTest.java b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaPipesTest.java index 8a28798698..07cf61c096 100644 --- a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaPipesTest.java +++ b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaPipesTest.java @@ -204,6 +204,7 @@ public void testBasic() throws Exception { Response response = WebClient .create(getUrl) .accept("application/json") + .type("application/json") .post(writer.toString()); assertEquals(200, response.getStatus()); @@ -233,6 +234,7 @@ public void testConcatenated() throws Exception { Response response = WebClient .create(getUrl) .accept("application/json") + .type("application/json") .post(writer.toString()); assertEquals(200, response.getStatus()); @@ -262,6 +264,7 @@ public void testPDFConfig() throws Exception { Response response = WebClient .create(getUrl) .accept("application/json") + .type("application/json") .post(writer.toString()); assertEquals(200, response.getStatus()); @@ -315,6 +318,7 @@ public void testBytes() throws Exception { Response response = WebClient .create(getUrl) .accept("application/json") + .type("application/json") .post(writer.toString()); assertEquals(200, response.getStatus());