This is an automated email from the ASF dual-hosted git repository. tballison pushed a commit to branch update-serialization-docs in repository https://gitbox.apache.org/repos/asf/tika.git
commit 193d69cff0344fb73469994ec3f4047ceb4a391c Author: tallison <[email protected]> AuthorDate: Thu Jul 2 09:33:00 2026 -0400 Update serialization docs --- .../ROOT/pages/developers/serialization.adoc | 56 ++++++++++++++++++++++ .../pages/migration-to-4x/design-notes-4x.adoc | 18 ++++--- .../pages/migration-to-4x/serialization-4x.adoc | 24 ++++++++-- .../tika/serialization/ComponentNameResolver.java | 11 +++-- 4 files changed, 93 insertions(+), 16 deletions(-) diff --git a/docs/modules/ROOT/pages/developers/serialization.adoc b/docs/modules/ROOT/pages/developers/serialization.adoc index 238adf1552..84037c6a78 100644 --- a/docs/modules/ROOT/pages/developers/serialization.adoc +++ b/docs/modules/ROOT/pages/developers/serialization.adoc @@ -251,6 +251,62 @@ for instantiation. } ---- +=== Untrusted (Wire) Input: Restricted Mode + +Configuration files loaded at startup via `TikaLoader` are treated as trusted. +Per-request configuration arriving over the wire — tika-server request bodies +and pipes `FetchEmitTuple`s — is deserialized in *restricted mode* +(`ParseContextDeserializer.readParseContext(node, true)`), which adds a second, +fail-closed gate on top of the registry: + +* Only context-key types confined to shaping this request's metadata or output + may be instantiated from the wire: `MetadataFilter`, `ContentHandlerFactory`, + `ContentHandlerDecoratorFactory`, `DigesterFactory`, + `MetadataWriteLimiterFactory`, `UnpackSelector` +* Types with exec/IO/network capability or control over which components run + are blocked: `Parser`, `Detector`, `EncodingDetector`, `Renderer`, + `Translator`, `EmbeddedDocumentExtractorFactory` +* The check is fail-closed: a newly added context-key interface is blocked until + it is consciously allow-listed +* The whole tree is scanned *before* any component is constructed + +The allowlist/blocklist lives in `ComponentNameResolver` +(`WIRE_INSTANTIABLE_CONTEXT_KEYS` / `WIRE_BLOCKED_CONTEXT_KEYS`); an +exhaustiveness test asserts every context-key interface is classified as +exactly one of the two. Plain config DTOs (non-component keys) are never +blocked. + +== Framework Directives + +Some JSON keys are consumed by the loading framework itself rather than by the +component whose config object they appear in. When such a directive shares a +JSON object with a component's own properties, it carries a leading underscore +to avoid namespace collisions with legitimate component config keys: + +[source,json] +---- +{ + "parsers": [ + { + "pdf-parser": { + "_mime-include": ["application/pdf"], + "_mime-exclude": ["application/pdf+fdf"], + "extractInlineImages": true + } + } + ] +} +---- + +`_mime-include`/`_mime-exclude` are stripped before the component sees its +config and are applied by the framework as a MIME-filtering decorator around +the parser. New framework directives must follow the underscore convention. + +Marker entries that have no component-config namespace of their own are the +exception: `"exclude"` on `default-parser`/`default-detector`/ +`default-encoding-detector` needs no prefix because those markers carry only +framework keys. + == Creating a Custom Component Complete example of a custom metadata filter: 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 d8e027d496..7913fafefb 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 @@ -61,16 +61,22 @@ IMPORTANT: We tried to have as few Tika dependencies in the plugins as possible. === Security Model -Configuration files at initialization are treated as trusted sources. Runtime -serialization/deserialization uses an allowlist of permitted packages via -`PolymorphicObjectMapperFactory`. - -Custom components can add patterns to `META-INF/tika-serialization-allowlist.txt`. +Configuration files at initialization are treated as trusted sources. Component +instantiation from JSON is restricted to classes registered at compile time by +the `@TikaComponent` annotation processor (`META-INF/tika/*.idx` files); unknown +class names are rejected, and there is no Jackson default typing. + +Untrusted per-request configuration (tika-server requests, pipes +`FetchEmitTuple`s) is deserialized in a restricted mode that additionally applies +a fail-closed allowlist of context-key types: only metadata/output-shaping +components (e.g. `MetadataFilter`, `ContentHandlerFactory`, `DigesterFactory`) +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]. === Implementation Challenges * Converted code to true Java beans with matching getters/setters -* Used `ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE` for polymorphic typing * Replaced generic collections (`List`, `Set`) with concrete types (`ArrayList`, `HashSet`) * Converted `Path` fields to `String` due to Jackson constraints * Avoided Java records to enable `readerForUpdating` functionality diff --git a/docs/modules/ROOT/pages/migration-to-4x/serialization-4x.adoc b/docs/modules/ROOT/pages/migration-to-4x/serialization-4x.adoc index 8afafbe55e..4981f706bc 100644 --- a/docs/modules/ROOT/pages/migration-to-4x/serialization-4x.adoc +++ b/docs/modules/ROOT/pages/migration-to-4x/serialization-4x.adoc @@ -75,10 +75,18 @@ indented). Use the `--list-*-names` variants when you want a machine-readable ma === Custom Class Support -The design permits users to add custom classes through Jackson's polymorphic handling: +Custom classes are supported through the `@TikaComponent` annotation rather than +Jackson polymorphic typing: -* `org.apache.tika` patterns are allowed by default -* Users can define additional inclusion patterns for security +* Annotate the class with `@TikaComponent`; the annotation processor generates a + `META-INF/tika/*.idx` registry entry at compile time +* Any class whose `.idx` entry is on the classpath can be referenced by its + friendly name in JSON configuration +* Class names or packages that are not registered cannot be instantiated from + JSON — there is no package-pattern allowlist and no `Class.forName` fallback + +See xref:developers/serialization.adoc[Serialization and Configuration] for the +full mechanism. === Configuration Consistency @@ -137,7 +145,13 @@ dependencies on components like `PDFParser`. == Security Considerations * Configuration files at initialization are treated as trusted sources -* Runtime serialization/deserialization uses an allowlist of permitted packages -* Custom components can register patterns in `META-INF/tika-serialization-allowlist.txt` +* Only classes registered via `@TikaComponent` (compile-time-generated + `META-INF/tika/*.idx` files) can be instantiated from JSON; unregistered class + names are rejected and there is no Jackson default typing +* Untrusted per-request configuration (tika-server requests, pipes + `FetchEmitTuple`s) is deserialized in restricted mode: a fail-closed allowlist + permits only metadata/output-shaping context keys (e.g. `MetadataFilter`, + `ContentHandlerFactory`, `DigesterFactory`) and blocks `Parser`, `Detector`, + `Renderer`, and similar before anything is constructed See link:design-notes-4x.html[Design Notes for 4.x] for additional architectural context. diff --git a/tika-serialization/src/main/java/org/apache/tika/serialization/ComponentNameResolver.java b/tika-serialization/src/main/java/org/apache/tika/serialization/ComponentNameResolver.java index 977dd895a4..7b3db573c3 100644 --- a/tika-serialization/src/main/java/org/apache/tika/serialization/ComponentNameResolver.java +++ b/tika-serialization/src/main/java/org/apache/tika/serialization/ComponentNameResolver.java @@ -128,13 +128,14 @@ public final class ComponentNameResolver { } /** - * Resolves a friendly name or FQCN to a Class. - * Searches all registered component registries, falling back to Class.forName. + * Resolves a friendly name (or registered FQCN) to a Class by searching the + * registered component registries only. There is deliberately no + * Class.forName fallback: unregistered names are rejected for security. * - * @param name friendly name or fully qualified class name - * @param classLoader the class loader to use for FQCN fallback + * @param name friendly name or registered fully qualified class name + * @param classLoader unused for resolution; retained for API stability * @return the resolved class - * @throws ClassNotFoundException if not found in any registry and not a valid FQCN + * @throws ClassNotFoundException if the name is not in any registry */ public static Class<?> resolveClass(String name, ClassLoader classLoader) throws ClassNotFoundException {
