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 <[email protected]> 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.
