This is an automated email from the ASF dual-hosted git repository. tballison pushed a commit to branch improve-pipes-in-server in repository https://gitbox.apache.org/repos/asf/tika.git
commit 780153d3addd275b3ac27ec98df95de387df888a Author: tallison <[email protected]> AuthorDate: Wed Jun 24 16:38:52 2026 -0400 require enableUnsecureFeatures in /pipes and /async handlers --- .../integration-testing/run-uat-script.adoc | 3 +- .../advanced/integration-testing/tika-server.adoc | 17 +++++++++ docs/modules/ROOT/pages/advanced/robustness.adoc | 2 +- .../migration-to-4x/migrating-tika-server-4x.adoc | 16 +++++++++ .../ROOT/pages/using-tika/server/index.adoc | 21 +++++++++++- .../apache/tika/server/core/TikaServerConfig.java | 25 ++++++++++++++ .../tika/server/core/TikaServerConfigTest.java | 40 ++++++++++++++++++++++ .../configs/tika-config-server-basic.json | 1 + .../configs/tika-config-server-emitter.json | 2 +- .../configs/tika-config-server-pipes-basic.json | 1 + 10 files changed, 124 insertions(+), 4 deletions(-) diff --git a/docs/modules/ROOT/pages/advanced/integration-testing/run-uat-script.adoc b/docs/modules/ROOT/pages/advanced/integration-testing/run-uat-script.adoc index 594e9e91ff..900cfedbc2 100644 --- a/docs/modules/ROOT/pages/advanced/integration-testing/run-uat-script.adoc +++ b/docs/modules/ROOT/pages/advanced/integration-testing/run-uat-script.adoc @@ -52,7 +52,8 @@ Coverage includes: * `/language/stream` * `/meta/form`, `/rmeta/form` (multipart variants) * `enableUnsecureFeatures=false` gating: `/meta/config`, `/rmeta/config`, - `/tika/config` all return 403 + `/tika/config` all return 403; and selecting the `/pipes`, `/async`, or `/status` + endpoints without `enableUnsecureFeatures` makes the server refuse to start * `X-Tika-OCRskipOcr` header, `Content-Disposition` filename * 404 / 405 error handling diff --git a/docs/modules/ROOT/pages/advanced/integration-testing/tika-server.adoc b/docs/modules/ROOT/pages/advanced/integration-testing/tika-server.adoc index 2608a6b490..d42d0a0644 100644 --- a/docs/modules/ROOT/pages/advanced/integration-testing/tika-server.adoc +++ b/docs/modules/ROOT/pages/advanced/integration-testing/tika-server.adoc @@ -214,6 +214,23 @@ curl -s -w "\nHTTP Status: %{http_code}\n" -X POST -F "[email protected]" http:/ *Expected:* All return HTTP 403 with message: "Config endpoints are disabled. Set enableUnsecureFeatures=true in server config." +=== Test 18b: `/pipes`, `/async`, `/status` Require enableUnsecureFeatures + +[source,bash] +---- +cat > tika-config-pipes-no-unsecure.json << 'EOF' +{ + "server": {"port": 9998, "endpoints": ["tika", "pipes"]}, + "pipes": {"numClients": 2}, + "plugin-roots": "/tmp/tika-server-test/plugins" +} +EOF + +java -jar tika-server-standard-4.0.0-SNAPSHOT.jar -c tika-config-pipes-no-unsecure.json +---- + +*Expected:* The server refuses to start, failing with a `TikaConfigException` stating that the `pipes` endpoint requires `enableUnsecureFeatures` to be `true`. The same applies to `async` and `status`. + == Part 2: Tests with enableUnsecureFeatures Stop the default server and create a config file: diff --git a/docs/modules/ROOT/pages/advanced/robustness.adoc b/docs/modules/ROOT/pages/advanced/robustness.adoc index 784ba02463..170db7cf27 100644 --- a/docs/modules/ROOT/pages/advanced/robustness.adoc +++ b/docs/modules/ROOT/pages/advanced/robustness.adoc @@ -97,7 +97,7 @@ tika-server restarts gracefully. tika-pipes:: Available through programmatic use, tika-app `-a` option, or tika-server's `/async` -and `/pipes` endpoints. +and `/pipes` endpoints (the server endpoints require `enableUnsecureFeatures=true`). == Security Testing and Prevention diff --git a/docs/modules/ROOT/pages/migration-to-4x/migrating-tika-server-4x.adoc b/docs/modules/ROOT/pages/migration-to-4x/migrating-tika-server-4x.adoc index 4a18868ec4..59e8691e76 100644 --- a/docs/modules/ROOT/pages/migration-to-4x/migrating-tika-server-4x.adoc +++ b/docs/modules/ROOT/pages/migration-to-4x/migrating-tika-server-4x.adoc @@ -132,6 +132,22 @@ The following `TikaServerConfig` options have been removed: * **Fetcher-based streaming** - The `InputStreamFactory` pattern for fetching documents via HTTP headers (`fetcherName`, `fetchKey`) has been removed. All documents are now processed via temp files through the pipes infrastructure. +=== `/pipes`, `/async`, and `/status` Require `enableUnsecureFeatures` + +Previously these endpoints were enabled simply by listing them under `server.endpoints`. They now *also* require `enableUnsecureFeatures` to be `true`; selecting any of `pipes`, `async`, or `status` without it causes the server to refuse to start with a clear error. `/pipes` and `/async` drive process-isolated batch parsing through your fetchers and emitters, and `/status` exposes server information, so this makes enabling them an explicit, deliberate opt-in. + +**Migration:** if your config selects `pipes`, `async`, or `status`, add `"enableUnsecureFeatures": true` to the `server` section: + +[source,json] +---- +{ + "server": { + "enableUnsecureFeatures": true, + "endpoints": ["tika", "rmeta", "pipes", "async", "status"] + } +} +---- + == Configuration Changes === Required: Pipes Configuration diff --git a/docs/modules/ROOT/pages/using-tika/server/index.adoc b/docs/modules/ROOT/pages/using-tika/server/index.adoc index 0c92d3e60f..ecec41df26 100644 --- a/docs/modules/ROOT/pages/using-tika/server/index.adoc +++ b/docs/modules/ROOT/pages/using-tika/server/index.adoc @@ -156,6 +156,11 @@ curl -T document.pdf http://localhost:9998/meta/Content-Type # single field * `/translate/all/\{translator}/\{src}/\{dest}` — translation * `/pipes`, `/async` — Pipes-based bulk processing +NOTE: `/pipes`, `/async`, and `/status` expose unsecure features and are only available +when `enableUnsecureFeatures` is `true` (see <<_security_configuration,Security +Configuration>>). Selecting any of them without that flag causes the server to refuse +to start. + == Error Responses When parsing fails due to a process-level problem — the forked child process timed out, @@ -207,7 +212,7 @@ Server behavior beyond host/port is controlled by a JSON config file passed via |`enableUnsecureFeatures` |`false` -|Enable the `/config` family of endpoints (see <<_security_configuration,Security Configuration>>). +|Required opt-in for unsecure features: the `/config` family of endpoints and the `/pipes`, `/async`, and `/status` endpoints. The server refuses to start if any of those endpoints are selected without this flag (see <<_security_configuration,Security Configuration>>). |`cors` |`""` (off) @@ -279,6 +284,20 @@ xref:using-tika/server/tls.adoc[2-way TLS authentication]. Exposing config endpo to untrusted networks can help attackers identify vulnerabilities and craft targeted attacks. +=== Pipes, Async, and Status Endpoints + +The `/pipes`, `/async`, and `/status` endpoints also require `enableUnsecureFeatures`. +`/pipes` and `/async` drive process-isolated batch parsing through your configured +fetchers and emitters — whoever can reach them gains the read access of your fetchers +and the write access of your emitters (see +https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3271[CVE-2015-3271]); `/status` +exposes server information. + +In earlier releases these endpoints were enabled simply by listing them under +`server.endpoints`. You must now *also* set `enableUnsecureFeatures` to `true`; +selecting any of them without it causes the server to refuse to start. This is +deliberate — it makes enabling these powerful endpoints an explicit, considered choice. + === Security Best Practices 1. **Keep config endpoints disabled** in production (default behavior). diff --git a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java index fa7bb3f93d..6eb1771af7 100644 --- a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java +++ b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java @@ -58,6 +58,13 @@ public class TikaServerConfig { "Additionally, /config endpoints allow per-request parser configuration\n" + "which could enable dangerous operations.\n" + "Please make sure you know what you are doing."; + /** + * Endpoints that expose unsecure features (process-isolated pipes parsing, async + * batch processing, and server status). Selecting any of these now requires + * {@code enableUnsecureFeatures=true} as an explicit, deliberate opt-in. + */ + private static final Set<String> ENDPOINTS_REQUIRING_UNSECURE_FEATURES = + new HashSet<>(Arrays.asList("pipes", "async", "status")); private static final List<String> ONLY_IN_FORK_MODE = Arrays.asList( new String[]{"maxFiles", "javaPath", "maxRestarts", "numRestarts", "forkedStatusFile", "maxForkedStartupMillis", "tmpFilePrefix"}); @@ -168,6 +175,24 @@ private long forkedProcessShutdownMillis = DEFAULT_FORKED_PROCESS_SHUTDOWN_MILLI if (host == null) { throw new TikaConfigException("Must specify 'host'"); } + if (!enableUnsecureFeatures) { + List<String> requireUnsecure = new ArrayList<>(); + for (String endpoint : endpoints) { + if (ENDPOINTS_REQUIRING_UNSECURE_FEATURES.contains(endpoint) + && !requireUnsecure.contains(endpoint)) { + requireUnsecure.add(endpoint); + } + } + if (!requireUnsecure.isEmpty()) { + throw new TikaConfigException( + "The following selected endpoint(s) require unsecure features to be " + + "enabled: " + requireUnsecure + ". Set 'enableUnsecureFeatures' to true " + + "in the 'server' section of your tika-config and confirm you understand " + + "the security implications (see the tika-server documentation). These " + + "endpoints were previously enabled simply by selecting them; the explicit " + + "opt-in is now required."); + } + } } public String getHost() { diff --git a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerConfigTest.java b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerConfigTest.java index 411317d6ef..d4920f04fe 100644 --- a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerConfigTest.java +++ b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerConfigTest.java @@ -18,6 +18,7 @@ package org.apache.tika.server.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Path; @@ -32,6 +33,7 @@ import org.apache.commons.cli.Options; import org.junit.jupiter.api.Test; import org.apache.tika.TikaTest; +import org.apache.tika.exception.TikaConfigException; import org.apache.tika.utils.ProcessUtils; public class TikaServerConfigTest extends TikaTest { @@ -76,6 +78,44 @@ public class TikaServerConfigTest extends TikaTest { TikaServerConfig config = TikaServerConfig.load(commandLine); } + @Test + public void testUnsecureEndpointRequiresEnableUnsecureFeatures() throws Exception { + // Selecting /pipes (or /async, /status) without enableUnsecureFeatures must fail + // at config load, forcing an explicit opt-in. + CommandLineParser parser = new DefaultParser(); + Path path = getConfigPath(getClass(), "tika-config-server-pipes-no-unsecure.json"); + CommandLine commandLine = parser.parse(new Options() + .addOption(Option + .builder("c") + .longOpt("config") + .hasArg() + .get()), new String[]{"-c", ProcessUtils.escapeCommandLine(path + .toAbsolutePath() + .toString())}); + TikaConfigException ex = assertThrows(TikaConfigException.class, + () -> TikaServerConfig.load(commandLine)); + assertContains("enableUnsecureFeatures", ex.getMessage()); + assertContains("pipes", ex.getMessage()); + } + + @Test + public void testUnsecureEndpointAllowedWithEnableUnsecureFeatures() throws Exception { + // tika-config-server-basic.json selects the 'status' endpoint together with + // enableUnsecureFeatures=true, so it must load without error. + CommandLineParser parser = new DefaultParser(); + Path path = getConfigPath(getClass(), "tika-config-server-basic.json"); + CommandLine commandLine = parser.parse(new Options() + .addOption(Option + .builder("c") + .longOpt("config") + .hasArg() + .get()), new String[]{"-c", ProcessUtils.escapeCommandLine(path + .toAbsolutePath() + .toString())}); + TikaServerConfig config = TikaServerConfig.load(commandLine); + assertTrue(config.isEnableUnsecureFeatures()); + } + @Test public void testTlsConfig() throws Exception { Set<String> settings = new HashSet<>(); diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-basic.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-basic.json index a14eda47ae..41d8acb90b 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-basic.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-basic.json @@ -8,6 +8,7 @@ }, "server": { "port": 9999, + "enableUnsecureFeatures": true, "endpoints": [ "rmeta", "status", diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-emitter.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-emitter.json index ce5f4a6f51..60a672a866 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-emitter.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-emitter.json @@ -8,7 +8,7 @@ }, "server": { "port": 9999, - "enableUnsecure": true, + "enableUnsecureFeatures": true, "endpoints": [ "emit", "async", diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-basic.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-basic.json index 86a24fa4c3..2b7ed5a22b 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-basic.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-basic.json @@ -8,6 +8,7 @@ }, "server": { "port": 9999, + "enableUnsecureFeatures": true, "endpoints": [ "rmeta", "status",
