You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2026/08/10 00:02:49 UTC
(tika) 05/09: TIKA-4809: Correct security claims in serialization, design-notes and filesystem docs
This is an automated email from the ASF dual-hosted git repository.
tballison pushed a commit to branch TIKA-4809-stage-3
in repository https://gitbox.apache.org/repos/asf/tika.git
commit 559dd58f291b169c3d5b707c52495daf4f533944
Author: tallison <ta...@apache.org>
AuthorDate: Sun Aug 9 16:18:22 2026 -0400
TIKA-4809: Correct security claims in serialization, design-notes and filesystem docs
---
.../modules/ROOT/pages/developers/serialization.adoc | 20 ++++++++++++++++++++
.../ROOT/pages/migration-to-4x/design-notes-4x.adoc | 8 ++++++++
.../modules/ROOT/pages/pipes/plugins/filesystem.adoc | 11 ++++++-----
3 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/docs/modules/ROOT/pages/developers/serialization.adoc b/docs/modules/ROOT/pages/developers/serialization.adoc
index 122589731d..d8477966b3 100644
--- a/docs/modules/ROOT/pages/developers/serialization.adoc
+++ b/docs/modules/ROOT/pages/developers/serialization.adoc
@@ -241,6 +241,26 @@ The serialization system implements a security allowlist:
This prevents attacks where malicious JSON specifies dangerous classes
for instantiation.
+[IMPORTANT]
+====
+The allowlist governs *which components may be instantiated* from JSON. It does
+not restrict *how an already-loaded component may be configured*.
+
+Self-configuring components — which includes every `Parser`, since `Parser`
+extends `SelfConfiguring` — are skipped by the wire-block scan
+(`ParseContextDeserializer.assertNoBlockedComponents`): their config subtree is
+passed through to the component unexamined. So while a request cannot bind a new
+`Parser` from the wire, a request carrying
+`{"parse-context": {"pdf-parser": {"ocr": {"strategy": "OCR_AND_TEXT_EXTRACTION"}}}}`
+will reach `PDFParser` and take effect.
+
+That is why per-request configuration is gated separately by
+`allowPerRequestConfig`, which is off by default. Treat "the caller may supply
+per-request config" as equivalent to "the caller may set any parser option,
+including options that spawn external processes such as OCR" — not as something
+the allowlist constrains.
+====
+
[source,java]
----
// This will FAIL - class not registered
diff --git a/docs/modules/ROOT/pages/migration-to-4x/design-notes-4x.adoc b/docs/modules/ROOT/pages/migration-to-4x/design-notes-4x.adoc
index 7913fafefb..f6053f808d 100644
--- a/docs/modules/ROOT/pages/migration-to-4x/design-notes-4x.adoc
+++ b/docs/modules/ROOT/pages/migration-to-4x/design-notes-4x.adoc
@@ -74,6 +74,14 @@ may be bound from the wire; `Parser`, `Detector`, `Renderer`, and similar are
blocked before anything is constructed. See
xref:developers/serialization.adoc[Serialization and Configuration].
+Note the boundary: the allowlist blocks *binding a component* from the wire, not
+*configuring one that is already loaded*. Self-configuring components — every
+`Parser` among them — have their config subtree passed through unscanned, so a
+per-request config can still set parser options (including ones that spawn
+external processes, such as OCR). This is why `allowPerRequestConfig` is a
+separate gate and is off by default; the allowlist alone does not make
+per-request configuration safe to expose.
+
=== Implementation Challenges
* Converted code to true Java beans with matching getters/setters
diff --git a/docs/modules/ROOT/pages/pipes/plugins/filesystem.adoc b/docs/modules/ROOT/pages/pipes/plugins/filesystem.adoc
index 034a5d7b93..2a13b5d068 100644
--- a/docs/modules/ROOT/pages/pipes/plugins/filesystem.adoc
+++ b/docs/modules/ROOT/pages/pipes/plugins/filesystem.adoc
@@ -81,8 +81,8 @@ The outer key (`fsf`) is the fetcher ID — referenced by `pipesIterator.fetcher
|Field |Default |Description
|`basePath`
-|_required_
-|Base directory for fetch operations. Fetch keys are resolved relative to this path.
+|_none_
+|Base directory for fetch operations. Fetch keys are resolved relative to this path and must stay inside it. Not technically required, but omitting it disables containment entirely — see `allowAbsolutePaths` below and <<security-notes>>.
|`extractFileSystemMetadata`
|`false`
@@ -90,7 +90,7 @@ The outer key (`fsf`) is the fetcher ID — referenced by `pipesIterator.fetcher
|`allowAbsolutePaths`
|`false`
-|When `true`, fetch keys may be absolute paths and `basePath` may be omitted. Use sparingly — see <<security-notes>>.
+|Permission to run *without* a `basePath`. It is not a relaxation of `basePath` — see <<security-notes>>.
|===
[#file-system-emitter]
@@ -253,6 +253,7 @@ Tradeoffs:
[#security-notes]
== Security Notes
-* **`basePath` is a sandbox boundary.** The fetcher and emitter reject fetch/emit keys that resolve outside `basePath`. Do not set `allowAbsolutePaths=true` unless the source of fetch keys is fully trusted — an attacker-controlled fetch key could otherwise read arbitrary files.
-* **Symlinks are followed.** A symlink under `basePath` pointing outside `basePath` may still be readable. If you need strict containment, do not allow symlinks in your input tree.
+* **`basePath` is the sandbox boundary, and it is the only one.** With `basePath` set, the fetcher and emitter reject any key that resolves outside it, including absolute paths and `../` traversal. `allowAbsolutePaths` has no effect in this state.
+* **Without `basePath` there is no containment at all.** The key is used as a raw absolute path, and the containment checks are skipped entirely. `allowAbsolutePaths=true` is how you assert that you intend this; it is a switch between two states, not a dial that loosens `basePath`. For the fetcher this means any file the process can read; for the emitter, any file it can write. Use it only when fetch/emit keys come from a fully trusted source and access to the service is restricted by ot [...]
+* **Symlink containment differs between fetcher and emitter.** The fetcher re-checks with `toRealPath()`, so a symlink under `basePath` pointing outside it is rejected. The emitter does not: it checks only the normalized path, so a symlink already present under its `basePath` can be written through. Do not rely on symlinks being contained on the emit side.
* **Output directories are created automatically.** The emitter creates intermediate directories as needed. Make sure the process's umask is appropriate for the data being written.