This is an automated email from the ASF dual-hosted git repository. tballison pushed a commit to branch TIKA-4764-more-granular-capability-selection in repository https://gitbox.apache.org/repos/asf/tika.git
commit 63fb013efc4829adc7a65ef61da51430a31a9cd2 Author: tallison <[email protected]> AuthorDate: Fri Jun 26 13:12:14 2026 -0400 TIKA-4764 -- more granular selection of features in tika-server and tika-grpc --- CHANGES.txt | 10 ++ .../integration-testing/run-uat-script.adoc | 6 +- .../advanced/integration-testing/tika-server.adoc | 23 ++-- docs/modules/ROOT/pages/advanced/robustness.adoc | 2 +- docs/modules/ROOT/pages/configuration/index.adoc | 2 +- .../migration-to-4x/migrating-tika-server-4x.adoc | 14 ++- docs/modules/ROOT/pages/pipes/index.adoc | 2 +- docs/modules/ROOT/pages/pipes/timeouts.adoc | 2 +- docs/modules/ROOT/pages/using-tika/grpc/index.adoc | 130 +++++++++++++++++++++ docs/modules/ROOT/pages/using-tika/index.adoc | 7 ++ .../ROOT/pages/using-tika/server/index.adoc | 50 ++++---- tika-grpc/README.md | 6 + .../org/apache/tika/pipes/grpc/TikaGrpcConfig.java | 102 ++++++++++++++++ .../apache/tika/pipes/grpc/TikaGrpcServerImpl.java | 77 +++++++++++- .../apache/tika/pipes/grpc/TikaGrpcServerTest.java | 84 ++++++++++++- .../apache/tika/config/loader/TikaJsonConfig.java | 1 + .../server/core/ConfigEndpointSecurityFilter.java | 14 +-- .../apache/tika/server/core/TikaServerConfig.java | 72 +++++++----- .../apache/tika/server/core/TikaServerProcess.java | 10 +- .../tika/server/core/resource/TikaResource.java | 26 ++--- .../org/apache/tika/server/core/CXFTestBase.java | 4 +- .../core/ConfigEndpointSecurityEnabledTest.java | 4 +- .../server/core/ConfigEndpointSecurityTest.java | 4 +- .../tika/server/core/TikaServerConfigTest.java | 44 +++++-- .../resources/config-examples/server-basic.json | 3 +- .../resources/configs/cxf-test-base-template.json | 3 +- .../configs/tika-config-server-basic.json | 3 +- .../configs/tika-config-server-emitter.json | 3 +- .../tika-config-server-fetcher-template.json | 3 +- .../tika-config-server-fetchers-emitters.json | 3 +- .../configs/tika-config-server-pipes-basic.json | 3 +- ...json => tika-config-server-pipes-no-flags.json} | 0 ...son => tika-config-server-status-no-flags.json} | 9 +- .../resources/configs/tika-config-server-tls.json | 3 +- .../test/resources/configs/tika-config-server.json | 3 +- .../configs/tika-config-timeout-100ms.json | 3 +- .../tika/server/standard/MetadataResourceTest.java | 2 +- .../standard/RecursiveMetadataResourceTest.java | 2 +- .../tika/server/standard/TikaResourceTest.java | 2 +- .../UnpackerResourceConfigDisabledTest.java | 4 +- .../tika/server/standard/UnpackerResourceTest.java | 2 +- .../standard/UnpackerResourceWithConfigTest.java | 2 +- .../resources/configs/cxf-test-base-template.json | 3 +- 43 files changed, 608 insertions(+), 144 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4acdb12197..f0109c0a8c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -18,6 +18,16 @@ Release 4.0.0-beta-1 - unreleased The extra jars are also forwarded onto forked pipes/server worker processes, so they are available where parsing actually happens (TIKA-4755). + * More granular, default-deny capability flags for tika-server and tika-grpc. + tika-server's enableUnsecureFeatures is split into allowPipes (gates the + /pipes and /async endpoints) and allowPerRequestConfig (gates the /config + endpoints and the multipart config part); the /status endpoint is no longer + gated and is enabled simply by listing it under endpoints. tika-grpc gains + the same allowPerRequestConfig flag plus allowComponentModifications (gates + runtime Save/Delete of fetchers and pipes iterators). All flags default to + false, so an out-of-the-box tika-grpc server no longer accepts per-request + configuration or runtime store mutations (TIKA-4764). + OTHER CHANGES * Release artifacts are now channel-specific. Maven Central gets slim 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 900cfedbc2..370480fc0e 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 @@ -51,9 +51,9 @@ Coverage includes: * `/unpack/all` (embedded extraction; verifies the response is a valid zip) * `/language/stream` * `/meta/form`, `/rmeta/form` (multipart variants) -* `enableUnsecureFeatures=false` gating: `/meta/config`, `/rmeta/config`, - `/tika/config` all return 403; and selecting the `/pipes`, `/async`, or `/status` - endpoints without `enableUnsecureFeatures` makes the server refuse to start +* `allowPerRequestConfig=false` gating: `/meta/config`, `/rmeta/config`, + `/tika/config` all return 403; and `allowPipes=false` gating: selecting the + `/pipes` or `/async` endpoints without `allowPipes` 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 d42d0a0644..69595447bd 100644 --- a/docs/modules/ROOT/pages/advanced/integration-testing/tika-server.adoc +++ b/docs/modules/ROOT/pages/advanced/integration-testing/tika-server.adoc @@ -212,13 +212,13 @@ curl -s -w "\nHTTP Status: %{http_code}\n" -X POST -F "[email protected]" http:/ curl -s -w "\nHTTP Status: %{http_code}\n" -X POST -F "[email protected]" http://localhost:9998/unpack/config ---- -*Expected:* All return HTTP 403 with message: "Config endpoints are disabled. Set enableUnsecureFeatures=true in server config." +*Expected:* All return HTTP 403 with message: "Config endpoints are disabled. Set allowPerRequestConfig=true in server config." -=== Test 18b: `/pipes`, `/async`, `/status` Require enableUnsecureFeatures +=== Test 18b: `/pipes` and `/async` Require allowPipes [source,bash] ---- -cat > tika-config-pipes-no-unsecure.json << 'EOF' +cat > tika-config-pipes-no-flags.json << 'EOF' { "server": {"port": 9998, "endpoints": ["tika", "pipes"]}, "pipes": {"numClients": 2}, @@ -226,12 +226,12 @@ cat > tika-config-pipes-no-unsecure.json << 'EOF' } EOF -java -jar tika-server-standard-4.0.0-SNAPSHOT.jar -c tika-config-pipes-no-unsecure.json +java -jar tika-server-standard-4.0.0-SNAPSHOT.jar -c tika-config-pipes-no-flags.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`. +*Expected:* The server refuses to start, failing with a `TikaConfigException` stating that the `pipes` endpoint requires `allowPipes` to be `true`. The same applies to `async`. (`/status` is no longer gated — it can be enabled simply by listing `status` under `endpoints`.) -== Part 2: Tests with enableUnsecureFeatures +== Part 2: Tests with allowPipes / allowPerRequestConfig Stop the default server and create a config file: @@ -239,12 +239,13 @@ Stop the default server and create a config file: ---- pkill -f "tika-server-standard-4.0.0-SNAPSHOT.jar" -cat > tika-config-unsecure.json << 'EOF' +cat > tika-config-capabilities.json << 'EOF' { "server": { "port": 9998, "host": "localhost", - "enableUnsecureFeatures": true + "allowPipes": true, + "allowPerRequestConfig": true }, "parsers": [ {"default-parser": {}} @@ -253,7 +254,7 @@ cat > tika-config-unsecure.json << 'EOF' } EOF -java -jar tika-server-standard-4.0.0-SNAPSHOT.jar -c tika-config-unsecure.json & +java -jar tika-server-standard-4.0.0-SNAPSHOT.jar -c tika-config-capabilities.json & sleep 10 curl -s http://localhost:9998/version ---- @@ -382,7 +383,7 @@ rm -rf /tmp/tika-server-test The following endpoints were tested and verified working: -=== Default Mode (enableUnsecureFeatures=false) +=== Default Mode (allowPipes=false, allowPerRequestConfig=false) [cols="1,1,1", options="header"] |=== @@ -412,7 +413,7 @@ The following endpoints were tested and verified working: |`/unpack/config` |POST |BLOCKED (403) - Expected |=== -=== With enableUnsecureFeatures=true +=== With allowPerRequestConfig=true [cols="1,1,1", options="header"] |=== diff --git a/docs/modules/ROOT/pages/advanced/robustness.adoc b/docs/modules/ROOT/pages/advanced/robustness.adoc index 170db7cf27..c6ad52948c 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 (the server endpoints require `enableUnsecureFeatures=true`). +and `/pipes` endpoints (the server endpoints require `allowPipes=true`). == Security Testing and Prevention diff --git a/docs/modules/ROOT/pages/configuration/index.adoc b/docs/modules/ROOT/pages/configuration/index.adoc index 0605793967..19bcbe165f 100644 --- a/docs/modules/ROOT/pages/configuration/index.adoc +++ b/docs/modules/ROOT/pages/configuration/index.adoc @@ -74,7 +74,7 @@ used wherever a section is missing. "unpack-config": { /* embedded-byte extraction */ } /* other SelfConfiguring components by component name */ }, - "server": { /* tika-server options: enableUnsecureFeatures, cors, ... */ }, + "server": { /* tika-server options: allowPipes, allowPerRequestConfig, cors, ... */ }, "pipes": { /* Pipes process management: numClients, parseMode, ... */ }, "fetchers": { /* named fetcher instances */ }, "emitters": { /* named emitter instances */ }, 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 59e8691e76..1321e0837d 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,17 +132,23 @@ 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` +=== `/pipes` and `/async` Require `allowPipes`; Per-Request Config Requires `allowPerRequestConfig` -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. +Previously these endpoints (and per-request parser configuration) were enabled simply by listing endpoints under `server.endpoints`. The capabilities are now split into two default-`false` flags in the `server` section: -**Migration:** if your config selects `pipes`, `async`, or `status`, add `"enableUnsecureFeatures": true` to the `server` section: +* `allowPipes` gates the `/pipes` and `/async` endpoints, which drive process-isolated batch parsing through your fetchers and emitters. Selecting either without `allowPipes` causes the server to refuse to start with a clear error. +* `allowPerRequestConfig` gates per-request parser configuration: the `/config` family of endpoints and the multipart `config` part. When off, such requests are rejected with 403. + +`/status` is no longer gated: it exposes only aggregate counters, so it is enabled simply by listing `status` under `endpoints`. + +**Migration:** if your config selects `pipes` or `async`, add `"allowPipes": true`; if you rely on per-request config, add `"allowPerRequestConfig": true`: [source,json] ---- { "server": { - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": ["tika", "rmeta", "pipes", "async", "status"] } } diff --git a/docs/modules/ROOT/pages/pipes/index.adoc b/docs/modules/ROOT/pages/pipes/index.adoc index 8037b8857f..9cb7c167e1 100644 --- a/docs/modules/ROOT/pages/pipes/index.adoc +++ b/docs/modules/ROOT/pages/pipes/index.adoc @@ -29,7 +29,7 @@ While Tika Pipes has a programmatic Java API, it is best used through: * xref:using-tika/cli/index.adoc[tika-app] — batch processing from the command line * xref:using-tika/server/index.adoc[tika-server] — REST API with pipes-based robustness built in -* xref:using-tika/grpc/index.adoc[tika-grpc] — gRPC API with pipes-based robustness built in +* xref:using-tika/grpc/index.adoc[tika-grpc] — gRPC API with pipes-based robustness built in. More exposed by default than tika-server; run only on a trusted network (see xref:using-tika/grpc/index.adoc#_security[Security]). See xref:advanced/robustness.adoc[Robustness] for details on how Tika Pipes protects against problematic files. diff --git a/docs/modules/ROOT/pages/pipes/timeouts.adoc b/docs/modules/ROOT/pages/pipes/timeouts.adoc index bbb94abfd5..1cdddd7746 100644 --- a/docs/modules/ROOT/pages/pipes/timeouts.adoc +++ b/docs/modules/ROOT/pages/pipes/timeouts.adoc @@ -68,7 +68,7 @@ This can be combined with other parse-context settings: == Per-Request Overrides -When using Tika Server with `enableUnsecureFeatures: true`, timeouts can be overridden per-request +When using Tika Server with `allowPerRequestConfig: true`, timeouts can be overridden per-request by including `TimeoutLimits` in the `ParseContext` of a `FetchEmitTuple`: [source,java] diff --git a/docs/modules/ROOT/pages/using-tika/grpc/index.adoc b/docs/modules/ROOT/pages/using-tika/grpc/index.adoc index 7782e1cd63..47ddb21e9f 100644 --- a/docs/modules/ROOT/pages/using-tika/grpc/index.adoc +++ b/docs/modules/ROOT/pages/using-tika/grpc/index.adoc @@ -29,6 +29,131 @@ register a fetcher (`SaveFetcher`) and then submit `FetchAndParseRequest` messages, each of which returns a `FetchAndParseReply` with extracted metadata and content. +== Security + +[WARNING] +==== +Treat tika-grpc as a privileged, trusted-network-only service — it is *more +exposed by default than tika-server*. By default it has no transport security and +no per-caller authorization, and its core `FetchAndParse` surface is always on: +anyone who can reach the gRPC port can fetch and parse whatever the server's +configured fetchers can reach. + +Its most dangerous capabilities — runtime mutation of the fetcher/iterator store +(for example `SaveFetcher`, which can read files and load code on the server +host) and per-request parse configuration — are *off by default*, behind the +flags in <<_capability_flags>>, mirroring tika-server's default-deny capability +flags (`allowPipes`, `allowPerRequestConfig`). Those flags are defense in depth, +not a substitute for network controls and mutual TLS. Run tika-grpc only behind +strict network controls. +==== + +=== Capability Flags + +Like tika-server, tika-grpc locks down its dangerous capabilities by default. +The two flags below live in the `grpc` section of your tika-config and both +default to `false`. Out of the box, the server only fetches and parses using the +fetchers and pipes iterators declared in the config file, using the server's own +parse configuration. + +[cols="1,3",options="header"] +|=== +|Setting |Description + +|`allowPerRequestConfig` +|When `true`, callers may attach per-request configuration to `FetchAndParse` +requests (`parse_context_json` and `additional_fetch_config_json`), overriding +the server's defaults for that request. Because this can reconfigure any pipeline +component (fetcher, parser, timeouts, ...), it is off by default. When `false`, a +request carrying either field is rejected with `PERMISSION_DENIED`. + +|`allowComponentModifications` +|When `true`, callers may add, modify, or delete fetchers and pipes iterators at +runtime (`SaveFetcher`, `DeleteFetcher`, `SavePipesIterator`, +`DeletePipesIterator`). Because this changes what the server can reach for all +subsequent requests (for example, adding a fetcher that escapes a configured base +path), it is off by default. When `false`, those RPCs are rejected with +`PERMISSION_DENIED`. +|=== + +Enable these only for trusted callers over a secured channel: + +[source,json] +---- +{ + "grpc": { + "allowPerRequestConfig": true, + "allowComponentModifications": true + } +} +---- + +=== Transport Security (TLS) + +By default the gRPC server runs *without TLS*: connections are plaintext and +unauthenticated. This includes the `apache/tika-grpc` Docker image, whose +entrypoint does not pass `--secure`. Only run in this mode on a trusted network. + +Transport security is configured entirely through command-line flags (there is no +JSON config for gRPC TLS), with three modes: + +*Insecure (default).* No `--secure` flag. Plaintext, no authentication. + +*Server (1-way) TLS.* Enable `-s`/`--secure` and supply the server certificate +and key. The server authenticates to clients; clients are not authenticated. + +[source,bash] +---- +java -jar tika-grpc-<version>.jar --secure \ + --cert-chain server.pem --private-key server.key +---- + +Add `--private-key-password` if the private key is encrypted. + +*Mutual (2-way) TLS.* Additionally supply the trust collection (the CA used to +verify client certificates) and require client authentication: + +[source,bash] +---- +java -jar tika-grpc-<version>.jar --secure \ + --cert-chain server.pem --private-key server.key \ + --trust-cert-collection ca.pem --client-auth-required +---- + +Mutual TLS is opt-in: `--client-auth-required` is off by default, so it has no +effect unless `--trust-cert-collection` is also given (a missing or non-existent +trust-collection path is silently ignored). The default port is `50052` +(`-p`/`--port`). + +When running the Docker image, append these flags to the container command — they +are forwarded to the server — and mount the certificate files into the container. + +=== Kubernetes and Service Meshes + +Running tika-grpc in Kubernetes does not make it safe on its own. By default, pod +networking is flat: any pod can reach any other pod's port, traffic is +unencrypted, and there is no authentication or authorization between pods. +Kubernetes gives you the tools to lock this down, but none of them are applied +automatically. Two distinct controls are involved, and both matter: + +* *Transport security.* A service mesh (for example Istio or Linkerd) with sidecar + mTLS encrypts and authenticates pod-to-pod traffic. If the mesh provides this, + you can run tika-grpc without `--secure` and let the mesh handle transport + security in place of the TLS flags above. +* *Reachability.* A `NetworkPolicy` that admits only your trusted client(s) to the + tika-grpc Service. This is the control that actually mitigates the exposure + described above: because tika-grpc has no per-caller authorization, the set of + pods that can reach the port is effectively the set of pods that can use its + enabled RPC surface. + +Mesh mTLS authenticates *who opened the connection*; it does not authorize *what +that caller may do*, so an authenticated-but-untrusted pod can still invoke +whatever RPC surface is enabled — at minimum `FetchAndParse` against your +configured fetchers, and the runtime-mutation RPCs too if you have set +`allowComponentModifications`. Restricting reachability with a `NetworkPolicy` is +therefore required, not optional — running in Kubernetes without one leaves +tika-grpc reachable by every pod in the cluster. + == Per-Request `ParseContext` `FetchAndParseRequest.parse_context_json` lets the caller override the @@ -46,6 +171,11 @@ parse-context component names; values are their JSON configs. See `META-INF/tika/parse-context.idx` (generated at build time from `@TikaComponent` annotations) for the available component names. +NOTE: Per-request configuration is disabled by default. A request that sets +`parse_context_json` (or `additional_fetch_config_json`) is rejected with +`PERMISSION_DENIED` unless `allowPerRequestConfig` is enabled. See +<<_capability_flags,Capability Flags>>. + == Topics // Add links to specific topics as they are created diff --git a/docs/modules/ROOT/pages/using-tika/index.adoc b/docs/modules/ROOT/pages/using-tika/index.adoc index a81c5ee6c5..dbd049bba5 100644 --- a/docs/modules/ROOT/pages/using-tika/index.adoc +++ b/docs/modules/ROOT/pages/using-tika/index.adoc @@ -36,6 +36,13 @@ and microservice architectures. xref:using-tika/grpc/index.adoc[gRPC]:: Use Tika via gRPC protocol. Best for high-performance, cross-language communication. ++ +NOTE: tika-grpc is more exposed by default than tika-server — by default it has no +transport security or per-caller authorization, and its core fetch-and-parse surface +is always on. (Its most dangerous capabilities — runtime fetcher/iterator changes and +per-request parse configuration — are off by default.) Run it only on a trusted, +access-controlled network. See the +xref:using-tika/grpc/index.adoc#_security[Security] section. == Which Should I Use? diff --git a/docs/modules/ROOT/pages/using-tika/server/index.adoc b/docs/modules/ROOT/pages/using-tika/server/index.adoc index 4d2fc800b4..f7e15e9906 100644 --- a/docs/modules/ROOT/pages/using-tika/server/index.adoc +++ b/docs/modules/ROOT/pages/using-tika/server/index.adoc @@ -67,8 +67,8 @@ The server starts on `localhost:9998` by default. |Print the usage message. |=== -NOTE: Other behavior — `enableUnsecureFeatures`, CORS, TLS, timeouts — is configured -in the JSON config file (see <<_configuration,Configuration>>), not via CLI flags. +NOTE: Other behavior — `allowPipes`, `allowPerRequestConfig`, CORS, TLS, timeouts — is +configured in the JSON config file (see <<_configuration,Configuration>>), not via CLI flags. == Endpoints @@ -156,10 +156,10 @@ 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. +NOTE: `/pipes` and `/async` require `allowPipes` (they drive process-isolated fetching +and parsing); selecting either without it causes the server to refuse to start. `/status` +is a plain opt-in endpoint — enable it simply by listing it under `endpoints`. See +<<_security_configuration,Security Configuration>>. == Error Responses @@ -210,9 +210,13 @@ Server behavior beyond host/port is controlled by a JSON config file passed via |=== |Field |Default |Description -|`enableUnsecureFeatures` +|`allowPipes` |`false` -|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>>). +|Opt-in for the `/pipes` and `/async` endpoints, which drive process-isolated fetching and parsing. The server refuses to start if either is selected without this flag (see <<_security_configuration,Security Configuration>>). + +|`allowPerRequestConfig` +|`false` +|Opt-in for per-request parser configuration: the `/config` family of endpoints and the multipart `config` part. When off, such requests are rejected with 403 (see <<_security_configuration,Security Configuration>>). |`cors` |`""` (off) @@ -265,49 +269,51 @@ Protected endpoints include: === Enabling Config Endpoints -The setting is JSON-only — there is no CLI flag. Set `enableUnsecureFeatures` in +The setting is JSON-only — there is no CLI flag. Set `allowPerRequestConfig` in your config file's `server` section: [source,json] ---- { "server": { - "enableUnsecureFeatures": true + "allowPerRequestConfig": true } } ---- -WARNING: Only enable `enableUnsecureFeatures` if you have secured access to Tika +WARNING: Only enable `allowPerRequestConfig` if you have secured access to Tika Server through network controls (firewalls, private subnets), a reverse proxy (nginx, Apache httpd), or xref:using-tika/server/tls.adoc[2-way TLS authentication]. Exposing config endpoints to untrusted networks can help attackers identify vulnerabilities and craft targeted attacks. -=== Pipes, Async, and Status Endpoints +=== Pipes and Async 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. +The `/pipes` and `/async` endpoints require `allowPipes`. They 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]). 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. +`server.endpoints`. You must now *also* set `allowPipes` to `true`; selecting either +without it causes the server to refuse to start. This is deliberate — it makes enabling +these powerful endpoints an explicit, considered choice. [source,json] ---- { "server": { - "enableUnsecureFeatures": true, + "allowPipes": true, "endpoints": ["tika", "rmeta", "pipes", "async", "status"] } } ---- +NOTE: `/status` exposes only aggregate counters (active task count, files processed, +time since last parse) and is *not* gated by `allowPipes` or `allowPerRequestConfig`. +Enable it by listing `status` under `endpoints`. + === Security Best Practices 1. **Keep config endpoints disabled** in production (default behavior). diff --git a/tika-grpc/README.md b/tika-grpc/README.md index fdb2cf275a..986529ca30 100644 --- a/tika-grpc/README.md +++ b/tika-grpc/README.md @@ -11,6 +11,12 @@ This server will manage a pool of Tika Pipes clients. * Delete * Fetch + Parse a given Fetch Item +> **Security note:** runtime fetcher/iterator mutations (Create/Update/Delete) and +> per-request parse configuration are **disabled by default**. Enable them +> explicitly via `allowComponentModifications` / `allowPerRequestConfig` in the +> `grpc` section of your tika-config. See the +> [Tika gRPC security configuration docs](../docs/modules/ROOT/pages/using-tika/grpc/index.adoc). + ## Distribution and Maven Artifact **tika-grpc is designed to be run via Docker — it is not a standalone runnable artifact published to Maven Central.** diff --git a/tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcConfig.java b/tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcConfig.java new file mode 100644 index 0000000000..3ef88c19c9 --- /dev/null +++ b/tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcConfig.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tika.pipes.grpc; + +import java.io.IOException; + +import org.apache.tika.config.loader.TikaJsonConfig; + +/** + * Configuration for security-sensitive tika-grpc features, loaded from the + * {@code "grpc"} section of the tika-config JSON. + * <p> + * Both flags default to {@code false} so an out-of-the-box server is locked + * down: clients may only fetch-and-parse using the fetchers and pipes iterators + * the operator declared in the config file, using the server's own parse + * configuration. The capabilities below are dangerous and must be opted into + * explicitly: + * <ul> + * <li>{@link #isAllowPerRequestConfig()} lets a client reconfigure any + * pipeline component for a single request.</li> + * <li>{@link #isAllowComponentModifications()} lets a client change which + * fetchers and iterators the server has at all.</li> + * </ul> + */ +public class TikaGrpcConfig { + + private boolean allowPerRequestConfig = false; + + private boolean allowComponentModifications = false; + + /** + * Loads {@link TikaGrpcConfig} from the {@code "grpc"} section of the JSON + * configuration, or returns a locked-down default instance (all flags + * {@code false}) if no {@code "grpc"} section is present. + * + * @param tikaJsonConfig the JSON configuration to load from + * @return the loaded config, or a default (locked-down) instance + * @throws IOException if deserialization fails + */ + public static TikaGrpcConfig load(TikaJsonConfig tikaJsonConfig) throws IOException { + TikaGrpcConfig config = tikaJsonConfig.deserialize("grpc", TikaGrpcConfig.class); + if (config == null) { + config = new TikaGrpcConfig(); + } + return config; + } + + /** + * Whether clients may attach per-request configuration to FetchAndParse + * requests (the {@code additional_fetch_config_json} and + * {@code parse_context_json} fields), overriding the server's defaults for + * that single request. + * <p> + * This is dangerous because the supplied JSON can reconfigure <em>any</em> + * pipeline component (fetcher, parser/handler, timeout limits, ...), not + * just parse behavior. Defaults to {@code false}; when {@code false}, a + * request carrying either field is rejected. + * + * @return true if per-request configuration is permitted + */ + public boolean isAllowPerRequestConfig() { + return allowPerRequestConfig; + } + + public void setAllowPerRequestConfig(boolean allowPerRequestConfig) { + this.allowPerRequestConfig = allowPerRequestConfig; + } + + /** + * Whether clients may add, modify, or delete fetchers and pipes iterators at + * runtime (the SaveFetcher, DeleteFetcher, SavePipesIterator and + * DeletePipesIterator RPCs). + * <p> + * This is dangerous because it changes what the server can reach for all + * subsequent requests and clients (for example, adding a fetcher that + * escapes a configured base path or points at an internal host). Defaults + * to {@code false}; when {@code false}, those RPCs are rejected. + * + * @return true if runtime component modifications are permitted + */ + public boolean isAllowComponentModifications() { + return allowComponentModifications; + } + + public void setAllowComponentModifications(boolean allowComponentModifications) { + this.allowComponentModifications = allowComponentModifications; + } +} diff --git a/tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcServerImpl.java b/tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcServerImpl.java index 9f80afb352..5b3ee9e719 100644 --- a/tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcServerImpl.java +++ b/tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcServerImpl.java @@ -85,6 +85,7 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { private static final String PIPES_ITERATOR_PREFIX = "pipesIterator:"; PipesConfig pipesConfig; + TikaGrpcConfig tikaGrpcConfig; PipesClient pipesClient; FetcherManager fetcherManager; ConfigStore configStore; @@ -113,6 +114,10 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { pipesConfig = new PipesConfig(); } + // Security-sensitive grpc features (per-request config, runtime component + // modifications) are off unless explicitly enabled in the "grpc" section. + tikaGrpcConfig = TikaGrpcConfig.load(tikaJsonConfig); + pipesClient = new PipesClient(pipesConfig, configPath); try { @@ -141,7 +146,8 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { this.configStore = createConfigStore(); - fetcherManager = FetcherManager.load(pluginManager, tikaJsonConfig, true, this.configStore); + fetcherManager = FetcherManager.load(pluginManager, tikaJsonConfig, + tikaGrpcConfig.isAllowComponentModifications(), this.configStore); } private ConfigStore createConfigStore() throws TikaConfigException { @@ -182,9 +188,60 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { } } + /** + * If the operator has not opted in to runtime component modifications, closes + * the call with {@code PERMISSION_DENIED} and returns {@code true}. Guards the + * Save/Delete fetcher and pipes-iterator RPCs. The caller must {@code return} + * immediately when this returns {@code true}. + * <p> + * We close the observer here rather than throwing, because a + * {@link io.grpc.StatusRuntimeException} thrown out of a service method is + * reported to the client as {@code UNKNOWN}; only {@code onError} transmits + * the intended status. + */ + private boolean denyComponentModifications(StreamObserver<?> responseObserver) { + if (tikaGrpcConfig.isAllowComponentModifications()) { + return false; + } + responseObserver.onError(io.grpc.Status.PERMISSION_DENIED + .withDescription("Runtime component modifications are disabled. Set " + + "'allowComponentModifications' to true in the 'grpc' section of your " + + "tika-config to allow SaveFetcher/DeleteFetcher/SavePipesIterator/" + + "DeletePipesIterator. Understand the security implications first.") + .asRuntimeException()); + return true; + } + + /** + * If the request carries per-request configuration + * ({@code additional_fetch_config_json} or {@code parse_context_json}) but the + * operator has not opted in, closes the call with {@code PERMISSION_DENIED} and + * returns {@code true}. A request with no per-request config is always allowed. + * The caller must {@code return} immediately when this returns {@code true}. + */ + private boolean denyPerRequestConfig(FetchAndParseRequest request, + StreamObserver<?> responseObserver) { + boolean hasPerRequestConfig = + StringUtils.isNotBlank(request.getAdditionalFetchConfigJson()) + || StringUtils.isNotBlank(request.getParseContextJson()); + if (!hasPerRequestConfig || tikaGrpcConfig.isAllowPerRequestConfig()) { + return false; + } + responseObserver.onError(io.grpc.Status.PERMISSION_DENIED + .withDescription("Per-request configuration is disabled. Set " + + "'allowPerRequestConfig' to true in the 'grpc' section of your " + + "tika-config to allow additional_fetch_config_json / " + + "parse_context_json. Understand the security implications first.") + .asRuntimeException()); + return true; + } + @Override public void fetchAndParseServerSideStreaming(FetchAndParseRequest request, StreamObserver<FetchAndParseReply> responseObserver) { + if (denyPerRequestConfig(request, responseObserver)) { + return; + } fetchAndParseImpl(request, responseObserver); } @@ -194,6 +251,9 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { return new StreamObserver<>() { @Override public void onNext(FetchAndParseRequest fetchAndParseRequest) { + if (denyPerRequestConfig(fetchAndParseRequest, responseObserver)) { + return; + } fetchAndParseImpl(fetchAndParseRequest, responseObserver); } @@ -212,6 +272,9 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { @Override public void fetchAndParse(FetchAndParseRequest request, StreamObserver<FetchAndParseReply> responseObserver) { + if (denyPerRequestConfig(request, responseObserver)) { + return; + } fetchAndParseImpl(request, responseObserver); responseObserver.onCompleted(); } @@ -269,6 +332,9 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { @Override public void saveFetcher(SaveFetcherRequest request, StreamObserver<SaveFetcherReply> responseObserver) { + if (denyComponentModifications(responseObserver)) { + return; + } SaveFetcherReply reply = SaveFetcherReply.newBuilder().setFetcherId(request.getFetcherId()).build(); try { @@ -363,6 +429,9 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { @Override public void deleteFetcher(DeleteFetcherRequest request, StreamObserver<DeleteFetcherReply> responseObserver) { + if (denyComponentModifications(responseObserver)) { + return; + } boolean successfulDelete = deleteFetcher(request.getFetcherId()); responseObserver.onNext(DeleteFetcherReply.newBuilder().setSuccess(successfulDelete).build()); responseObserver.onCompleted(); @@ -398,6 +467,9 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { @Override public void savePipesIterator(SavePipesIteratorRequest request, StreamObserver<SavePipesIteratorReply> responseObserver) { + if (denyComponentModifications(responseObserver)) { + return; + } try { String iteratorId = request.getIteratorId(); String iteratorClass = request.getIteratorClass(); @@ -466,6 +538,9 @@ class TikaGrpcServerImpl extends TikaGrpc.TikaImplBase { @Override public void deletePipesIterator(DeletePipesIteratorRequest request, StreamObserver<DeletePipesIteratorReply> responseObserver) { + if (denyComponentModifications(responseObserver)) { + return; + } try { String iteratorId = request.getIteratorId(); LOG.info("Deleting pipes iterator: {}", iteratorId); diff --git a/tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java b/tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java index ee909098fc..04d57defce 100644 --- a/tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java +++ b/tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java @@ -42,9 +42,12 @@ import java.util.concurrent.atomic.AtomicBoolean; import com.asarkar.grpc.test.GrpcCleanupExtension; import com.asarkar.grpc.test.Resources; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.ImmutableMap; import io.grpc.ManagedChannel; import io.grpc.Server; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import io.grpc.inprocess.InProcessChannelBuilder; import io.grpc.inprocess.InProcessServerBuilder; import io.grpc.stub.StreamObserver; @@ -76,7 +79,13 @@ public class TikaGrpcServerTest { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final Logger LOG = LoggerFactory.getLogger(TikaGrpcServerTest.class); public static final int NUM_TEST_DOCS = 2; + // Secure-default config: no "grpc" section, so per-request config and runtime + // component modifications are both disabled. Used by the default-deny tests. static Path tikaConfig = Paths.get("target", "tika-config-" + UUID.randomUUID() + ".json"); + // Same config but with the dangerous grpc features explicitly enabled. Used by + // the CRUD/streaming tests, which save fetchers at runtime. + static Path tikaConfigUnlocked = + Paths.get("target", "tika-config-unlocked-" + UUID.randomUUID() + ".json"); @BeforeAll @@ -102,12 +111,24 @@ public class TikaGrpcServerTest { TikaGrpcServerTest.class, replacements, tikaConfig); LOG.debug("Written config to: {}", tikaConfig.toAbsolutePath()); + + // Derive an "unlocked" variant that opts in to the dangerous grpc features, + // for the tests that add/modify fetchers at runtime. + ObjectNode root = (ObjectNode) OBJECT_MAPPER.readTree(tikaConfig.toFile()); + ObjectNode grpc = OBJECT_MAPPER.createObjectNode(); + grpc.put("allowComponentModifications", true); + grpc.put("allowPerRequestConfig", true); + root.set("grpc", grpc); + FileUtils.write(tikaConfigUnlocked.toFile(), + OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(root), + StandardCharsets.UTF_8); } @AfterAll static void clean() { try { Files.deleteIfExists(tikaConfig); + Files.deleteIfExists(tikaConfigUnlocked); } catch (Exception e) { LOG.warn("Failed to delete {}", tikaConfig, e); } @@ -122,7 +143,7 @@ public class TikaGrpcServerTest { Server server = InProcessServerBuilder .forName(serverName) .directExecutor() - .addService(new TikaGrpcServerImpl(tikaConfig.toAbsolutePath().toString())) + .addService(new TikaGrpcServerImpl(tikaConfigUnlocked.toAbsolutePath().toString())) .build() .start(); resources.register(server, Duration.ofSeconds(10)); @@ -201,11 +222,70 @@ public class TikaGrpcServerTest { return "nick" + i + ":is:cool:super/" + FileSystemFetcher.class; } + @Test + public void testComponentModificationsDeniedByDefault(Resources resources) throws Exception { + TikaGrpc.TikaBlockingStub blockingStub = startServer(resources, tikaConfig); + + String targetFolder = new File("target").getAbsolutePath(); + StatusRuntimeException saveEx = Assertions.assertThrows(StatusRuntimeException.class, () -> + blockingStub.saveFetcher(SaveFetcherRequest + .newBuilder() + .setFetcherId(createFetcherId(0)) + .setFetcherClass(FileSystemFetcher.class.getName()) + .setFetcherConfigJson(OBJECT_MAPPER.writeValueAsString(ImmutableMap + .builder() + .put("basePath", targetFolder) + .build())) + .build())); + assertEquals(Status.Code.PERMISSION_DENIED, saveEx.getStatus().getCode()); + + StatusRuntimeException deleteEx = Assertions.assertThrows(StatusRuntimeException.class, () -> + blockingStub.deleteFetcher(DeleteFetcherRequest + .newBuilder() + .setFetcherId(createFetcherId(0)) + .build())); + assertEquals(Status.Code.PERMISSION_DENIED, deleteEx.getStatus().getCode()); + } + + @Test + public void testPerRequestConfigDeniedByDefault(Resources resources) throws Exception { + TikaGrpc.TikaBlockingStub blockingStub = startServer(resources, tikaConfig); + + // The per-request config gate fires before any fetch, so this never reaches the network. + StatusRuntimeException ex = Assertions.assertThrows(StatusRuntimeException.class, () -> + blockingStub.fetchAndParse(FetchAndParseRequest + .newBuilder() + .setFetcherId("httpFetcherIdHere") + .setFetchKey("https://example.com") + .setParseContextJson("{\"basic-content-handler-factory\":{\"type\":\"HTML\"}}") + .build())); + assertEquals(Status.Code.PERMISSION_DENIED, ex.getStatus().getCode()); + } + + private static TikaGrpc.TikaBlockingStub startServer(Resources resources, Path config) + throws Exception { + String serverName = InProcessServerBuilder.generateName(); + Server server = InProcessServerBuilder + .forName(serverName) + .directExecutor() + .addService(new TikaGrpcServerImpl(config.toAbsolutePath().toString())) + .build() + .start(); + resources.register(server, Duration.ofSeconds(10)); + + ManagedChannel channel = InProcessChannelBuilder + .forName(serverName) + .directExecutor() + .build(); + resources.register(channel, Duration.ofSeconds(10)); + return TikaGrpc.newBlockingStub(channel); + } + @Test public void testBiStream(Resources resources) throws Exception { String serverName = InProcessServerBuilder.generateName(); - TikaGrpcServerImpl tikaGrpcServerImpl = new TikaGrpcServerImpl(tikaConfig.toAbsolutePath().toString()); + TikaGrpcServerImpl tikaGrpcServerImpl = new TikaGrpcServerImpl(tikaConfigUnlocked.toAbsolutePath().toString()); Server server = InProcessServerBuilder .forName(serverName) .directExecutor() diff --git a/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaJsonConfig.java b/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaJsonConfig.java index d645ef73be..d15815b4ac 100644 --- a/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaJsonConfig.java +++ b/tika-serialization/src/main/java/org/apache/tika/config/loader/TikaJsonConfig.java @@ -117,6 +117,7 @@ public class TikaJsonConfig { "auto-detect-parser", "parse-context", "server", + "grpc", // Pipes/plugin keys "fetchers", diff --git a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/ConfigEndpointSecurityFilter.java b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/ConfigEndpointSecurityFilter.java index 16787e441b..c047f737c3 100644 --- a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/ConfigEndpointSecurityFilter.java +++ b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/ConfigEndpointSecurityFilter.java @@ -23,24 +23,24 @@ import jakarta.ws.rs.core.Response; import jakarta.ws.rs.ext.Provider; /** - * JAX-RS filter that gates /config endpoints behind the enableUnsecureFeatures flag. - * When enableUnsecureFeatures is false, requests to paths containing "/config" will + * JAX-RS filter that gates /config endpoints behind the allowPerRequestConfig flag. + * When allowPerRequestConfig is false, requests to paths containing "/config" will * receive a 403 Forbidden response. */ @Provider public class ConfigEndpointSecurityFilter implements ContainerRequestFilter { - private final boolean enableUnsecureFeatures; + private final boolean allowPerRequestConfig; - public ConfigEndpointSecurityFilter(boolean enableUnsecureFeatures) { - this.enableUnsecureFeatures = enableUnsecureFeatures; + public ConfigEndpointSecurityFilter(boolean allowPerRequestConfig) { + this.allowPerRequestConfig = allowPerRequestConfig; } @Override public void filter(ContainerRequestContext requestContext) { - if (!enableUnsecureFeatures && requestContext.getUriInfo().getPath().contains("/config")) { + if (!allowPerRequestConfig && requestContext.getUriInfo().getPath().contains("/config")) { requestContext.abortWith(Response.status(Response.Status.FORBIDDEN) - .entity("Config endpoints are disabled. Set enableUnsecureFeatures=true in server config.") + .entity("Config endpoints are disabled. Set allowPerRequestConfig=true in server config.") .type(MediaType.TEXT_PLAIN) .build()); } 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 6eb1771af7..14fd249468 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 @@ -48,23 +48,16 @@ public class TikaServerConfig { //used in fork mode -- restart after processing this many files private static final long DEFAULT_MAX_FILES = 100000; private static final int DEFAULT_DIGEST_MARK_LIMIT = 20 * 1024 * 1024; - private static final String UNSECURE_WARNING = - "WARNING: You have chosen to run tika-server with unsecure features enabled.\n" + - "Whoever has access to your service now has the same read permissions\n" + - "as you've given your fetchers and the same write permissions as your emitters.\n" + - "Users could request and receive a sensitive file from your\n" + - "drive or a webpage from your intranet and/or send malicious content to\n" + - "your emitter endpoints. See CVE-2015-3271.\n" + - "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. + * Endpoints that expose the pipes/fetch machinery (process-isolated pipes + * parsing and async batch processing). Selecting any of these requires + * {@code allowPipes=true} as an explicit, deliberate opt-in. + * <p> + * {@code status} is intentionally not in this set: it exposes only aggregate + * counters and is enabled simply by listing it under {@code endpoints}. */ - private static final Set<String> ENDPOINTS_REQUIRING_UNSECURE_FEATURES = - new HashSet<>(Arrays.asList("pipes", "async", "status")); + private static final Set<String> ENDPOINTS_REQUIRING_PIPES = + new HashSet<>(Arrays.asList("pipes", "async")); private static final List<String> ONLY_IN_FORK_MODE = Arrays.asList( new String[]{"maxFiles", "javaPath", "maxRestarts", "numRestarts", "forkedStatusFile", "maxForkedStartupMillis", "tmpFilePrefix"}); @@ -83,7 +76,8 @@ private long forkedProcessStartupMillis = DEFAULT_FORKED_PROCESS_STARTUP_MILLIS; private long forkedProcessShutdownMillis = DEFAULT_FORKED_PROCESS_SHUTDOWN_MILLIS; */ - private boolean enableUnsecureFeatures = false; + private boolean allowPipes = false; + private boolean allowPerRequestConfig = false; private String cors = ""; private boolean returnStackTrace = false; private String idBase = UUID @@ -163,34 +157,52 @@ private long forkedProcessShutdownMillis = DEFAULT_FORKED_PROCESS_SHUTDOWN_MILLI return idBase; } - public boolean isEnableUnsecureFeatures() { - return enableUnsecureFeatures; + /** + * Whether the pipes/fetch endpoints ({@code pipes}, {@code async}) may be + * enabled. Off by default; selecting one of those endpoints without this set + * causes the server to refuse to start. + */ + public boolean isAllowPipes() { + return allowPipes; + } + + public void setAllowPipes(boolean allowPipes) { + this.allowPipes = allowPipes; + } + + /** + * Whether callers may supply per-request parser configuration (the + * {@code /config} endpoints and the multipart {@code config} part). Off by + * default; when off, such requests are rejected with 403. + */ + public boolean isAllowPerRequestConfig() { + return allowPerRequestConfig; } - public void setEnableUnsecureFeatures(boolean enableUnsecureFeatures) { - this.enableUnsecureFeatures = enableUnsecureFeatures; + public void setAllowPerRequestConfig(boolean allowPerRequestConfig) { + this.allowPerRequestConfig = allowPerRequestConfig; } private void validateConsistency(Set<String> settings) throws TikaConfigException { if (host == null) { throw new TikaConfigException("Must specify 'host'"); } - if (!enableUnsecureFeatures) { - List<String> requireUnsecure = new ArrayList<>(); + if (!allowPipes) { + List<String> requirePipes = new ArrayList<>(); for (String endpoint : endpoints) { - if (ENDPOINTS_REQUIRING_UNSECURE_FEATURES.contains(endpoint) - && !requireUnsecure.contains(endpoint)) { - requireUnsecure.add(endpoint); + if (ENDPOINTS_REQUIRING_PIPES.contains(endpoint) + && !requirePipes.contains(endpoint)) { + requirePipes.add(endpoint); } } - if (!requireUnsecure.isEmpty()) { + if (!requirePipes.isEmpty()) { throw new TikaConfigException( - "The following selected endpoint(s) require unsecure features to be " + - "enabled: " + requireUnsecure + ". Set 'enableUnsecureFeatures' to true " + + "The following selected endpoint(s) require the pipes machinery to be " + + "enabled: " + requirePipes + ". Set 'allowPipes' 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."); + "endpoints expose process-isolated fetching and parsing, which can read " + + "files and reach network resources."); } } } diff --git a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java index 1d1cddfacb..241e96bee2 100644 --- a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java +++ b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java @@ -192,7 +192,7 @@ public class TikaServerProcess { } TikaResource.init(tikaLoader, serverStatus, pipesParsingHelper, - tikaServerConfig.isEnableUnsecureFeatures()); + tikaServerConfig.isAllowPerRequestConfig()); JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); List<ResourceProvider> resourceProviders = new ArrayList<>(); @@ -308,7 +308,7 @@ public class TikaServerProcess { writers.add(new JSONObjWriter()); // Add ConfigEndpointSecurityFilter to gate /config endpoints - writers.add(new ConfigEndpointSecurityFilter(tikaServerConfig.isEnableUnsecureFeatures())); + writers.add(new ConfigEndpointSecurityFilter(tikaServerConfig.isAllowPerRequestConfig())); TikaLoggingFilter logFilter = null; if (!StringUtils.isBlank(tikaServerConfig.getLogLevel())) { @@ -354,11 +354,13 @@ public class TikaServerProcess { resourceProviders.add(new SingletonResourceProvider(new TikaDetectors())); resourceProviders.add(new SingletonResourceProvider(new TikaParsers())); resourceProviders.add(new SingletonResourceProvider(new TikaVersion())); - if (tikaServerConfig.isEnableUnsecureFeatures()) { + if (tikaServerConfig.isAllowPipes()) { addAsyncResource = true; addPipesResource = true; - resourceProviders.add(new SingletonResourceProvider(new TikaServerStatus(serverStatus))); } + // status is a plain opt-in endpoint: it is only served when explicitly + // listed under "endpoints" (handled in the else branch below), not in + // this default set. } else { for (String endPoint : tikaServerConfig.getEndpoints()) { if ("meta".equals(endPoint)) { diff --git a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/TikaResource.java b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/TikaResource.java index a0ea80b80e..4abedede71 100644 --- a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/TikaResource.java +++ b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/TikaResource.java @@ -90,7 +90,7 @@ public class TikaResource { private static MetadataWriteLimiterFactory DEFAULT_METADATA_WRITE_LIMITER_FACTORY = null; // Whether per-request config injection (multipart "config" parts) is permitted. // Enforced in setupMultipartConfig so every config-consuming endpoint honors it. - private static boolean ENABLE_UNSECURE_FEATURES = false; + private static boolean ALLOW_PER_REQUEST_CONFIG = false; /** * Initialize TikaResource with pipes-based parsing for process isolation. @@ -98,14 +98,14 @@ public class TikaResource { * @param tikaLoader the Tika loader * @param serverStatus server status tracker * @param pipesParsingHelper helper for pipes-based parsing, may be null if /tika endpoint is not enabled - * @param enableUnsecureFeatures whether per-request config injection is permitted + * @param allowPerRequestConfig whether per-request config injection is permitted */ public static void init(TikaLoader tikaLoader, ServerStatus serverStatus, - PipesParsingHelper pipesParsingHelper, boolean enableUnsecureFeatures) { + PipesParsingHelper pipesParsingHelper, boolean allowPerRequestConfig) { TIKA_LOADER = tikaLoader; SERVER_STATUS = serverStatus; PIPES_PARSING_HELPER = pipesParsingHelper; - ENABLE_UNSECURE_FEATURES = enableUnsecureFeatures; + ALLOW_PER_REQUEST_CONFIG = allowPerRequestConfig; // MetadataWriteLimiterFactory is now loaded dynamically via loadParseContext() } @@ -274,10 +274,10 @@ public class TikaResource { // Enforce the per-request config gate where the config part is actually // consumed, so every endpoint that accepts a config part honors - // enableUnsecureFeatures uniformly. - if (configAtt != null && !ENABLE_UNSECURE_FEATURES) { + // allowPerRequestConfig uniformly. + if (configAtt != null && !ALLOW_PER_REQUEST_CONFIG) { throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN) - .entity("Per-request configuration is disabled. Set enableUnsecureFeatures=true in server config.") + .entity("Per-request configuration is disabled. Set allowPerRequestConfig=true in server config.") .type(MediaType.TEXT_PLAIN) .build()); } @@ -626,7 +626,7 @@ public class TikaResource { * <p> * Returns XHTML by default. Use /tika/config/text, /tika/config/html, or /tika/config/xml for other formats. * <p> - * This endpoint is gated behind enableUnsecureFeatures=true because per-request + * This endpoint is gated behind allowPerRequestConfig=true because per-request * configuration could enable dangerous operations. */ @POST @@ -649,7 +649,7 @@ public class TikaResource { * - "file" part (required): the document to parse * - "config" part (optional): JSON configuration for parser settings * <p> - * This endpoint is gated behind enableUnsecureFeatures=true because per-request + * This endpoint is gated behind allowPerRequestConfig=true because per-request * configuration could enable dangerous operations. */ @POST @@ -671,7 +671,7 @@ public class TikaResource { * - "file" part (required): the document to parse * - "config" part (optional): JSON configuration for parser settings * <p> - * This endpoint is gated behind enableUnsecureFeatures=true because per-request + * This endpoint is gated behind allowPerRequestConfig=true because per-request * configuration could enable dangerous operations. */ @POST @@ -693,7 +693,7 @@ public class TikaResource { * - "file" part (required): the document to parse * - "config" part (optional): JSON configuration for parser settings * <p> - * This endpoint is gated behind enableUnsecureFeatures=true because per-request + * This endpoint is gated behind allowPerRequestConfig=true because per-request * configuration could enable dangerous operations. */ @POST @@ -715,7 +715,7 @@ public class TikaResource { * - "file" part (required): the document to parse * - "config" part (optional): JSON configuration for parser settings * <p> - * This endpoint is gated behind enableUnsecureFeatures=true because per-request + * This endpoint is gated behind allowPerRequestConfig=true because per-request * configuration could enable dangerous operations. */ @POST @@ -739,7 +739,7 @@ public class TikaResource { * <p> * Default handler is text. Use config to specify different handler type. * <p> - * This endpoint is gated behind enableUnsecureFeatures=true because per-request + * This endpoint is gated behind allowPerRequestConfig=true because per-request * configuration could enable dangerous operations. */ @POST diff --git a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/CXFTestBase.java b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/CXFTestBase.java index 888cfe22e5..f081a5fc01 100644 --- a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/CXFTestBase.java +++ b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/CXFTestBase.java @@ -216,7 +216,7 @@ public abstract class CXFTestBase { PipesParsingHelper pipesParsingHelper = new PipesParsingHelper(this.pipesParser, pipesConfig, inputTempDirectory, getUnpackEmitterBasePath(), false); - TikaResource.init(tika, new ServerStatus(), pipesParsingHelper, isEnableUnsecureFeatures()); + TikaResource.init(tika, new ServerStatus(), pipesParsingHelper, isAllowPerRequestConfig()); } finally { // Only delete tika config, keep pipes config for child processes Files.deleteIfExists(tmp); @@ -372,7 +372,7 @@ public abstract class CXFTestBase { * the production default. Tests that POST a multipart "config" part must override * this to return true, otherwise the config part is rejected with 403. */ - protected boolean isEnableUnsecureFeatures() { + protected boolean isAllowPerRequestConfig() { return false; } diff --git a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/ConfigEndpointSecurityEnabledTest.java b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/ConfigEndpointSecurityEnabledTest.java index 0646f75ecd..e7335bf7ea 100644 --- a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/ConfigEndpointSecurityEnabledTest.java +++ b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/ConfigEndpointSecurityEnabledTest.java @@ -37,7 +37,7 @@ import org.apache.tika.server.core.resource.TikaResource; import org.apache.tika.server.core.writer.JSONMessageBodyWriter; /** - * Tests that /config endpoints work when enableUnsecureFeatures=true. + * Tests that /config endpoints work when allowPerRequestConfig=true. */ public class ConfigEndpointSecurityEnabledTest extends CXFTestBase { @@ -55,7 +55,7 @@ public class ConfigEndpointSecurityEnabledTest extends CXFTestBase { List<Object> providers = new ArrayList<>(); providers.add(new TikaServerParseExceptionMapper(false)); providers.add(new JSONMessageBodyWriter()); - // Add security filter with enableUnsecureFeatures=true + // Add security filter with allowPerRequestConfig=true providers.add(new ConfigEndpointSecurityFilter(true)); sf.setProviders(providers); } diff --git a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/ConfigEndpointSecurityTest.java b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/ConfigEndpointSecurityTest.java index cbb7b5aa86..314574899a 100644 --- a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/ConfigEndpointSecurityTest.java +++ b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/ConfigEndpointSecurityTest.java @@ -38,7 +38,7 @@ import org.apache.tika.server.core.writer.JSONMessageBodyWriter; /** * Tests for ConfigEndpointSecurityFilter. - * Verifies that /config endpoints are gated behind enableUnsecureFeatures. + * Verifies that /config endpoints are gated behind allowPerRequestConfig. */ public class ConfigEndpointSecurityTest extends CXFTestBase { @@ -56,7 +56,7 @@ public class ConfigEndpointSecurityTest extends CXFTestBase { List<Object> providers = new ArrayList<>(); providers.add(new TikaServerParseExceptionMapper(false)); providers.add(new JSONMessageBodyWriter()); - // Add security filter with enableUnsecureFeatures=false + // Add security filter with allowPerRequestConfig=false providers.add(new ConfigEndpointSecurityFilter(false)); sf.setProviders(providers); } 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 d4920f04fe..56b5ffd0cb 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 @@ -45,7 +45,8 @@ public class TikaServerConfigTest extends TikaTest { CommandLine emptyCommandLine = parser.parse(new Options(), new String[]{}); Path path = getConfigPath(getClass(), "tika-config-server.json"); TikaServerConfig config = TikaServerConfig.load(path, emptyCommandLine, settings); - assertEquals(true, config.isEnableUnsecureFeatures()); + assertTrue(config.isAllowPipes()); + assertTrue(config.isAllowPerRequestConfig()); } @Test @@ -55,7 +56,8 @@ public class TikaServerConfigTest extends TikaTest { CommandLine emptyCommandLine = parser.parse(new Options(), new String[]{}); Path path = getConfigPath(getClass(), "tika-config-server-fetchers-emitters.json"); TikaServerConfig config = TikaServerConfig.load(path, emptyCommandLine, settings); - assertEquals(true, config.isEnableUnsecureFeatures()); + assertTrue(config.isAllowPipes()); + assertTrue(config.isAllowPerRequestConfig()); } @Test @@ -79,11 +81,11 @@ public class TikaServerConfigTest extends TikaTest { } @Test - public void testUnsecureEndpointRequiresEnableUnsecureFeatures() throws Exception { - // Selecting /pipes (or /async, /status) without enableUnsecureFeatures must fail - // at config load, forcing an explicit opt-in. + public void testPipesEndpointRequiresAllowPipes() throws Exception { + // Selecting /pipes (or /async) without allowPipes 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"); + Path path = getConfigPath(getClass(), "tika-config-server-pipes-no-flags.json"); CommandLine commandLine = parser.parse(new Options() .addOption(Option .builder("c") @@ -94,14 +96,14 @@ public class TikaServerConfigTest extends TikaTest { .toString())}); TikaConfigException ex = assertThrows(TikaConfigException.class, () -> TikaServerConfig.load(commandLine)); - assertContains("enableUnsecureFeatures", ex.getMessage()); + assertContains("allowPipes", 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. + public void testEndpointsWithCapabilitiesLoad() throws Exception { + // tika-config-server-basic.json selects rmeta/status/tika together with + // allowPipes + allowPerRequestConfig, 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() @@ -113,7 +115,27 @@ public class TikaServerConfigTest extends TikaTest { .toAbsolutePath() .toString())}); TikaServerConfig config = TikaServerConfig.load(commandLine); - assertTrue(config.isEnableUnsecureFeatures()); + assertTrue(config.isAllowPipes()); + assertTrue(config.isAllowPerRequestConfig()); + } + + @Test + public void testStatusEndpointDoesNotRequireAllowPipes() throws Exception { + // status is a plain opt-in endpoint: selecting it (without allowPipes) must + // load without error, unlike the pipes/async endpoints. + CommandLineParser parser = new DefaultParser(); + Path path = getConfigPath(getClass(), "tika-config-server-status-no-flags.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); + assertFalse(config.isAllowPipes()); + assertFalse(config.isAllowPerRequestConfig()); } @Test diff --git a/tika-server/tika-server-core/src/test/resources/config-examples/server-basic.json b/tika-server/tika-server-core/src/test/resources/config-examples/server-basic.json index d133c0deeb..af5f016a34 100644 --- a/tika-server/tika-server-core/src/test/resources/config-examples/server-basic.json +++ b/tika-server/tika-server-core/src/test/resources/config-examples/server-basic.json @@ -3,7 +3,8 @@ "port": 9998, "host": "localhost", "taskTimeoutMillis": 300000, - "enableUnsecureFeatures": false + "allowPipes": false, + "allowPerRequestConfig": false }, "parsers": [ { diff --git a/tika-server/tika-server-core/src/test/resources/configs/cxf-test-base-template.json b/tika-server/tika-server-core/src/test/resources/configs/cxf-test-base-template.json index b2c0ef4118..664b2599e6 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/cxf-test-base-template.json +++ b/tika-server/tika-server-core/src/test/resources/configs/cxf-test-base-template.json @@ -25,7 +25,8 @@ }, "server": { "port": 9999, - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": [ "rmeta", "status", 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 41d8acb90b..ed88d33f02 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,7 +8,8 @@ }, "server": { "port": 9999, - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": 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 60a672a866..affc5f9a2f 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,8 @@ }, "server": { "port": 9999, - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": [ "emit", "async", diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-fetcher-template.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-fetcher-template.json index 7348181239..dd42ae4f2c 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-fetcher-template.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-fetcher-template.json @@ -8,7 +8,8 @@ }, "server": { "port": "PORT", - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": [ "tika" ] diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-fetchers-emitters.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-fetchers-emitters.json index b500cdd21b..040398522d 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-fetchers-emitters.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-fetchers-emitters.json @@ -1,7 +1,8 @@ { "server": { "port": 9999, - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": [ "rmeta" ] 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 2b7ed5a22b..58a6a8e638 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,7 +8,8 @@ }, "server": { "port": 9999, - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": [ "rmeta", "status", diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-no-unsecure.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-no-flags.json similarity index 100% copy from tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-no-unsecure.json copy to tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-no-flags.json diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-no-unsecure.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-status-no-flags.json similarity index 51% rename from tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-no-unsecure.json rename to tika-server/tika-server-core/src/test/resources/configs/tika-config-server-status-no-flags.json index 262f29d7a8..0b94a296cc 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-pipes-no-unsecure.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-status-no-flags.json @@ -1,16 +1,9 @@ { - "fetchers": { - "file-system-fetcher": { - "file-system-fetcher": { - "allowAbsolutePaths": true - } - } - }, "server": { "port": 9999, "endpoints": [ "tika", - "pipes" + "status" ] }, "pipes": { diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls.json index 7c526aa906..02e866bf7d 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls.json @@ -1,7 +1,8 @@ { "server": { "port": 9999, - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": [ "rmeta" ], diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server.json index b500cdd21b..040398522d 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server.json @@ -1,7 +1,8 @@ { "server": { "port": 9999, - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": [ "rmeta" ] diff --git a/tika-server/tika-server-core/src/test/resources/configs/tika-config-timeout-100ms.json b/tika-server/tika-server-core/src/test/resources/configs/tika-config-timeout-100ms.json index 8cf3bdc5e5..c0534fe6d6 100644 --- a/tika-server/tika-server-core/src/test/resources/configs/tika-config-timeout-100ms.json +++ b/tika-server/tika-server-core/src/test/resources/configs/tika-config-timeout-100ms.json @@ -1,6 +1,7 @@ { "server": { - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "enableLogFile": true }, "parse-context": { diff --git a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MetadataResourceTest.java b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MetadataResourceTest.java index 8ae59274c3..0a0a25b761 100644 --- a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MetadataResourceTest.java +++ b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MetadataResourceTest.java @@ -59,7 +59,7 @@ public class MetadataResourceTest extends CXFTestBase { private static final String META_PATH = "/meta"; @Override - protected boolean isEnableUnsecureFeatures() { + protected boolean isAllowPerRequestConfig() { return true; // exercises per-request config injection } diff --git a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/RecursiveMetadataResourceTest.java b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/RecursiveMetadataResourceTest.java index 936e202f1a..3203950a34 100644 --- a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/RecursiveMetadataResourceTest.java +++ b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/RecursiveMetadataResourceTest.java @@ -64,7 +64,7 @@ public class RecursiveMetadataResourceTest extends CXFTestBase { private static final String TEST_RECURSIVE_DOC = "test-documents/test_recursive_embedded.docx"; @Override - protected boolean isEnableUnsecureFeatures() { + protected boolean isAllowPerRequestConfig() { return true; // exercises per-request config injection } diff --git a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java index 58ef99ecc6..b8ca2a7c3d 100644 --- a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java +++ b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java @@ -65,7 +65,7 @@ public class TikaResourceTest extends CXFTestBase { private static final int UNPROCESSEABLE = 422; @Override - protected boolean isEnableUnsecureFeatures() { + protected boolean isAllowPerRequestConfig() { return true; // exercises per-request config injection } diff --git a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceConfigDisabledTest.java b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceConfigDisabledTest.java index 5e96ff7604..4f6f73175f 100644 --- a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceConfigDisabledTest.java +++ b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceConfigDisabledTest.java @@ -42,7 +42,7 @@ import org.apache.tika.server.core.writer.TarWriter; import org.apache.tika.server.core.writer.ZipWriter; /** - * Verifies that /unpack and /unpack/all honor enableUnsecureFeatures: when per-request + * Verifies that /unpack and /unpack/all honor allowPerRequestConfig: when per-request * config injection is disabled (the default), a multipart "config" part is rejected * with 403. Counterpart to {@link UnpackerResourceWithConfigTest}, which covers the * enabled path. @@ -52,7 +52,7 @@ public class UnpackerResourceConfigDisabledTest extends CXFTestBase { private static final String BASE_PATH = "/unpack"; private static final String ALL_PATH = BASE_PATH + "/all"; - // isEnableUnsecureFeatures() is intentionally NOT overridden; it defaults to false + // isAllowPerRequestConfig() is intentionally NOT overridden; it defaults to false // so a config part must be rejected. @Override diff --git a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceTest.java b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceTest.java index 6d1c0149cc..ab458d0a9c 100644 --- a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceTest.java +++ b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceTest.java @@ -99,7 +99,7 @@ public class UnpackerResourceTest extends CXFTestBase { private Path unpackTempDir; @Override - protected boolean isEnableUnsecureFeatures() { + protected boolean isAllowPerRequestConfig() { return true; // exercises per-request config injection } diff --git a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceWithConfigTest.java b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceWithConfigTest.java index 6f35a72eae..291cd80adb 100644 --- a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceWithConfigTest.java +++ b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceWithConfigTest.java @@ -73,7 +73,7 @@ public class UnpackerResourceWithConfigTest extends CXFTestBase { private Path unpackTempDir; @Override - protected boolean isEnableUnsecureFeatures() { + protected boolean isAllowPerRequestConfig() { return true; // exercises per-request config injection } diff --git a/tika-server/tika-server-standard/src/test/resources/configs/cxf-test-base-template.json b/tika-server/tika-server-standard/src/test/resources/configs/cxf-test-base-template.json index afc750acab..f0bfba8eb0 100644 --- a/tika-server/tika-server-standard/src/test/resources/configs/cxf-test-base-template.json +++ b/tika-server/tika-server-standard/src/test/resources/configs/cxf-test-base-template.json @@ -27,7 +27,8 @@ "port": 9999, "taskTimeoutMillis": "TIMEOUT_MILLIS", "taskPulseMillis": 100, - "enableUnsecureFeatures": true, + "allowPipes": true, + "allowPerRequestConfig": true, "endpoints": [ "rmeta", "status",
