This is an automated email from the ASF dual-hosted git repository.
tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git
The following commit(s) were added to refs/heads/main by this push:
new a1a582ac28 TIKA-4764 -- more granular selection of features in
tika-server and tika-grpc (#2909)
a1a582ac28 is described below
commit a1a582ac284cc2d8efd8ebc8d21ae27874921354
Author: Tim Allison <[email protected]>
AuthorDate: Fri Jun 26 14:04:30 2026 -0400
TIKA-4764 -- more granular selection of features in tika-server and
tika-grpc (#2909)
---
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 3c707d31df..7803e0a519 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
@@ -193,7 +193,7 @@ public class TikaServerProcess {
}
TikaResource.init(tikaLoader, serverStatus, pipesParsingHelper,
- tikaServerConfig.isEnableUnsecureFeatures());
+ tikaServerConfig.isAllowPerRequestConfig());
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
List<ResourceProvider> resourceProviders = new ArrayList<>();
@@ -309,7 +309,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())) {
@@ -356,11 +356,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 b48cfcfadd..31265d99ae 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 093a79bf73..e6dc35e56e 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
@@ -1,7 +1,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 11b7998805..ca30d36833 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
@@ -68,7 +68,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",