This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch TIKA-4809-stage-9
in repository https://gitbox.apache.org/repos/asf/tika.git

commit 508e9021890f6b20293c89a0ff5a3f59156d91c8
Merge: 9c9e675553 0b84d33943
Author: tallison <[email protected]>
AuthorDate: Mon Aug 10 21:16:33 2026 -0400

    Merge remote-tracking branch 'origin/main' into TIKA-4809-stage-9

 .../migration-to-4x/migrating-tika-server-4x.adoc  |  69 ++++++++
 .../pages/migration-to-4x/migrating-to-4x.adoc     |   2 +-
 docs/modules/ROOT/pages/pipes/cpu-sizing.adoc      |   2 +-
 .../ROOT/pages/using-tika/server/index.adoc        |  33 +---
 .../java/org/apache/tika/config/ServiceLoader.java |   9 +-
 .../charsoup/CharSoupMetadataFilter.java           |   8 +
 .../metadatafilter/OpenNLPMetadataFilter.java      |   8 +
 .../metadatafilter/OptimaizeMetadataFilter.java    |   8 +
 .../org/apache/tika/parser/mp3/ID3v2Frame.java     |  16 +-
 .../java/org/apache/tika/parser/mp4/MP4Parser.java |  30 +++-
 .../org/apache/tika/parser/mp4/TikaMp4Reader.java  | 149 +++++++++++++++++
 .../tika/parser/mp4/TikaMp4SoundHandler.java       |  15 +-
 .../tika/parser/mp4/boxes/TikaUserDataBox.java     | 167 +++++++++----------
 .../org/apache/tika/parser/video/FLVParser.java    |  53 ++++--
 .../org/apache/tika/parser/mp3/ID3v2FrameTest.java |  13 ++
 .../org/apache/tika/parser/mp3/Mp3ParserTest.java  |  22 +++
 .../org/apache/tika/parser/mp4/MP4ParserTest.java  | 109 ++++++++++++
 .../tika/parser/mp4/boxes/TikaUserDataBoxTest.java |  61 +++++++
 .../apache/tika/parser/video/FLVParserTest.java    |  22 +++
 .../tika/serialization/ParseContextUtils.java      |  10 +-
 .../TestParseContextSerialization.java             |  16 ++
 .../org/apache/tika/server/core/TikaServerCli.java |   4 +-
 .../apache/tika/server/core/TikaServerConfig.java  |  64 +++----
 .../apache/tika/server/core/TikaServerProcess.java | 107 +++++++-----
 .../core/resource/RecursiveMetadataResource.java   |  43 +----
 .../server/core/resource/ServerHandlerConfig.java  |  37 -----
 .../tika/server/core/resource/TikaResource.java    | 115 +++----------
 .../main/resources/tika-server-config-default.xml  |  55 -------
 .../server/core/RecursiveMetadataResourceTest.java |  61 -------
 .../apache/tika/server/core/TikaResourceTest.java  |  30 ----
 .../server/core/benchmark/TikaServerBenchmark.java |   1 -
 .../configs/cxf-unpack-test-template.json          |  10 +-
 .../standard/RecursiveMetadataResourceTest.java    | 183 +++++----------------
 .../tika/server/standard/TikaResourceTest.java     |  59 -------
 34 files changed, 836 insertions(+), 755 deletions(-)

diff --cc docs/modules/ROOT/pages/using-tika/server/index.adoc
index ee7502c34c,8d054258f8..0329241559
--- a/docs/modules/ROOT/pages/using-tika/server/index.adoc
+++ b/docs/modules/ROOT/pages/using-tika/server/index.adoc
@@@ -343,14 -304,10 +329,6 @@@ Server behavior beyond host/port is con
  |`""` (off)
  |`*` to allow any origin, or an explicit origin string. Empty disables CORS.
  
- |`digest`
- |`""` (off)
- |Compute a digest of the parsed bytes. Comma-separated algorithm names: 
`md5`, `sha1`, `sha256`, `sha384`, `sha512`.
- 
- |`digestMarkLimit`
- |`20971520` (20 MiB)
- |Max bytes buffered for digest computation.
 -|`returnStackTrace`
 -|`false`
 -|Include parser stack traces in error responses. Useful in dev, dangerous in 
production (leaks internals).
--
  |`logLevel`
  |_inherited_
  |`debug` or `info` to override the runtime log level.
diff --cc 
tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java
index d334359007,f17dbaa0e4..57fe4cef23
--- 
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
@@@ -70,7 -68,8 +68,7 @@@ private long forkedProcessShutdownMilli
      private boolean allowPipes = false;
      private boolean allowPerRequestConfig = false;
      private String cors = "";
-     private String idBase = UUID
 -    private boolean returnStackTrace = false;
+     private String id = UUID
              .randomUUID()
              .toString();
      private int port = DEFAULT_PORT;
@@@ -244,29 -240,13 +239,6 @@@
          this.configPath = Paths.get(path);
      }
  
-     public int getDigestMarkLimit() {
-         return digestMarkLimit;
-     }
- 
-     public void setDigestMarkLimit(int digestMarkLimit) {
-         this.digestMarkLimit = digestMarkLimit;
-     }
- 
-     /**
-      * digest configuration string, e.g. md5 or sha256, alternately w 16 or 
32 encoding,
-      * e.g. md5:32,sha256:16 would result in two digests per file
-      *
-      * @return
-      */
-     public String getDigest() {
-         return digest;
 -    public boolean isReturnStackTrace() {
 -        return returnStackTrace;
--    }
--
-     public void setDigest(String digest) {
-         LOG.info("As of Tika 2.5.0, you can set the digester via the 
AutoDetectParserConfig in " + "tika-config.xml. We plan to remove this 
commandline option in 2.8.0");
-         this.digest = digest;
 -    public void setReturnStackTrace(boolean returnStackTrace) {
 -        this.returnStackTrace = returnStackTrace;
--    }
- 
  
      public TlsConfig getTlsConfig() {
          return tlsConfig;
diff --cc 
tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java
index 5cf042ae90,b5c39f2ad3..ae60613222
--- 
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
@@@ -105,6 -101,10 +101,10 @@@ public class TikaServerProcess 
      private static final Logger LOG = 
LoggerFactory.getLogger(TikaServerProcess.class);
      public static int DO_NOT_RESTART_EXIT_VALUE = -100;
  
+     private static final List<String> VALID_ENDPOINTS = List.of("tika", 
"rmeta", "meta",
 -            "unpack", "detect", "language", "translate", "mime", 
"mime-types", "detectors",
++            "unpack", "detect", "language", "mime", "mime-types", "detectors",
+             "parsers", "version", "status", "pipes", "async");
+ 
      private static Options getOptions() {
          Options options = new Options();
          options.addOption("h", "host", true, "host name, use * for all)");
@@@ -163,6 -162,35 +162,31 @@@
          LOG.info("Started Apache Tika server {} at {}", 
serverDetails.serverId, serverDetails.url);
      }
  
+     /**
+      * Warns for settings that widen what a caller can reach. The security 
prose already
+      * exists, but only in the message shown when a flag is *not* set -- i.e. 
only to the
+      * operators who did not enable the risky thing.
+      */
+     private static void warnOnPermissiveConfig(TikaServerConfig config) {
+         if (config.isAllowPipes()) {
+             LOG.warn("allowPipes is enabled: /pipes and /async can fetch and 
emit through "
+                     + "configured fetchers/emitters, reaching files and 
network resources at "
+                     + "the server's privilege level. Expose only to trusted 
callers.");
+         }
+         if (config.isAllowPerRequestConfig()) {
+             LOG.warn("allowPerRequestConfig is enabled: callers may set any 
parser option, "
+                     + "including options that spawn external processes such 
as OCR.");
+         }
 -        if (config.isReturnStackTrace()) {
 -            LOG.warn("returnStackTrace is enabled: error responses include 
server-side stack "
 -                    + "traces. Intended for development, not production.");
 -        }
+         if ("*".equals(config.getCors())) {
+             LOG.warn("cors is '*': any origin may call this server from a 
browser.");
+         }
+         if ("0.0.0.0".equals(config.getHost()) || 
"*".equals(config.getHost())) {
+             LOG.warn("host is {}: the server is reachable on every interface. 
tika-server "
+                     + "performs no authentication; restrict access at the 
network layer.",
+                     config.getHost());
+         }
+     }
+ 
      //This returns the server, configured and ready to be started.
      private static ServerDetails initServer(TikaServerConfig 
tikaServerConfig) throws Exception {
          String host = tikaServerConfig.getHost();
@@@ -369,32 -397,38 +392,36 @@@
              // this default set.
          } else {
              for (String endPoint : tikaServerConfig.getEndpoints()) {
-                 if ("meta".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
MetadataResource(tikaResource)));
-                 } else if ("rmeta".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
RecursiveMetadataResource(tikaResource)));
-                 } else if ("detect".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
DetectorResource(serverStatus, tikaResource)));
-                 } else if ("language".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
LanguageResource()));
-                 } else if ("tika".equals(endPoint)) {
-                     resourceProviders.add(new 
SingletonResourceProvider(tikaResource));
-                 } else if ("unpack".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
UnpackerResource(tikaResource)));
-                 } else if ("mime".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
TikaMimeTypes(tikaResource)));
-                 } else if ("detectors".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
TikaDetectors(tikaResource)));
-                 } else if ("parsers".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
TikaParsers(tikaResource)));
-                 } else if ("version".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
TikaVersion()));
-                 } else if ("pipes".equals(endPoint)) {
-                     addPipesResource = true;
-                 } else if ("async".equals(endPoint)) {
-                     addAsyncResource = true;
-                 } else if ("status".equals(endPoint)) {
-                     resourceProviders.add(new SingletonResourceProvider(new 
TikaServerStatus(serverStatus)));
+                 switch (endPoint) {
+                     case "meta" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
MetadataResource(tikaResource)));
+                     case "rmeta" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
RecursiveMetadataResource(tikaResource)));
+                     case "detect" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
DetectorResource(serverStatus, tikaResource)));
+                     case "language" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
LanguageResource()));
 -                    case "translate" -> resourceProviders.add(
 -                            new SingletonResourceProvider(new 
TranslateResource(serverStatus)));
+                     case "tika" -> resourceProviders.add(
+                             new SingletonResourceProvider(tikaResource));
+                     case "unpack" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
UnpackerResource(tikaResource)));
+                     // "mime-types" accepted too: that is the path, and it is 
the natural guess.
+                     case "mime", "mime-types" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
TikaMimeTypes(tikaResource)));
+                     case "detectors" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
TikaDetectors(tikaResource)));
+                     case "parsers" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
TikaParsers(tikaResource)));
+                     case "version" -> resourceProviders.add(
+                             new SingletonResourceProvider(new TikaVersion()));
+                     case "status" -> resourceProviders.add(
+                             new SingletonResourceProvider(new 
TikaServerStatus(serverStatus)));
+                     case "pipes" -> addPipesResource = true;
+                     case "async" -> addAsyncResource = true;
+                     // Refuse rather than skip: a dropped name is a missing 
endpoint at
+                     // runtime with nothing in the log pointing at the config.
+                     default -> throw new TikaConfigException("Unrecognized 
endpoint '" + endPoint
+                             + "'. Valid endpoints: " + VALID_ENDPOINTS);
                  }
              }
          }
diff --cc 
tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaResourceTest.java
index d41cd8143b,8e89a38980..d6f7f581a9
--- 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaResourceTest.java
+++ 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaResourceTest.java
@@@ -110,32 -109,13 +110,14 @@@ public class TikaResourceTest extends C
          assertEquals("Nikolai Lobachevsky", metadata.get("author"));
          assertEquals("application/mock+xml", 
metadata.get(Metadata.CONTENT_TYPE));
          assertContains("some content", 
metadata.get(TikaCoreProperties.TIKA_CONTENT));
 -        // returnStackTrace defaults to false here, so CONTAINER_EXCEPTION is 
trimmed to
 -        // the caught exception's own class + message -- the NPE detail 
underneath it is
 -        // intentionally not exposed by default.
 +        // Exception detail is reported in full. Tika does not redact it: any 
scheme that
 +        // strips the message has to parse the rendered trace, and it would 
in any case be
 +        // undone by the same detail travelling in tk:exception:* on other 
responses. Filter
 +        // metadata before forwarding it somewhere less trusted.
          assertContains("TikaException", 
metadata.get(TikaCoreProperties.CONTAINER_EXCEPTION));
 -        assertNotFound("null pointer message", 
metadata.get(TikaCoreProperties.CONTAINER_EXCEPTION));
 +        assertContains("null pointer message", 
metadata.get(TikaCoreProperties.CONTAINER_EXCEPTION));
      }
  
-     @Test
-     public void testJsonWriteLimit() throws Exception {
-         Response response = WebClient
-                 .create(endPoint + TIKA_PATH + "/json")
-                 .header("writeLimit", "100")
-                 
.put(ClassLoader.getSystemResourceAsStream(TEST_HELLO_WORLD_LONG));
-         Metadata metadata = JsonMetadata.fromJson(new 
InputStreamReader(((InputStream) response.getEntity()), 
StandardCharsets.UTF_8));
- 
-         assertEquals("Nikolai Lobachevsky", metadata.get("author"));
-         assertEquals("application/mock+xml", 
metadata.get(Metadata.CONTENT_TYPE));
-         assertContains("Hello world", 
metadata.get(TikaCoreProperties.TIKA_CONTENT));
-         assertNotFound("dissolve", 
metadata.get(TikaCoreProperties.TIKA_CONTENT));
-         assertTrue(metadata
-                 .get(TikaCoreProperties.CONTAINER_EXCEPTION)
-                 
.startsWith("org.apache.tika.exception.WriteLimitReachedException"));
-         assertEquals("true", 
metadata.get(TikaCoreProperties.WRITE_LIMIT_REACHED));
-     }
- 
      @Test
      public void testJsonHandlerType() throws Exception {
          // Default /tika/json uses text handler

Reply via email to