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 72f4df7cb1 TIKA-4848: server, step 4 (#3106)
72f4df7cb1 is described below
commit 72f4df7cb1038516e976496d42b9ceaf82325efe
Author: Tim Allison <[email protected]>
AuthorDate: Mon Aug 31 10:34:08 2026 -0400
TIKA-4848: server, step 4 (#3106)
---
CHANGES.txt | 5 +
.../ROOT/pages/advanced/setting-limits.adoc | 6 +-
docs/modules/ROOT/pages/configuration/index.adoc | 1 +
docs/modules/ROOT/pages/security.adoc | 5 +-
.../ROOT/pages/using-tika/server/index.adoc | 29 ++--
.../core/TikaServerParseExceptionMapper.java | 42 +++---
.../apache/tika/server/core/TikaServerProcess.java | 2 +-
.../server/core/resource/MetadataResource.java | 12 +-
.../tika/server/core/resource/TikaResource.java | 8 ++
.../tika/server/core/RedactedStackTraceTest.java | 149 +++++++++++++++++++++
.../core/TikaServerParseExceptionMapperTest.java | 60 +++++++++
.../resources/config-examples/server-basic.json | 4 +
12 files changed, 280 insertions(+), 43 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 7061f1e087..3880a79543 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,10 @@
Release 4.1.0 - unreleased
+ * tika-server error bodies (the 422/500 exception mapper, /meta/{field})
+ now honor the exception-reporting policy; /meta/{field} returns the
+ already-formatted container exception instead of re-wrapping it with
+ server frames. Completes TIKA-4848 (TIKA-4848).
+
* The exception-reporting policy now also governs the messages a pipes
worker returns (fetch/emit/crash) and the container exception it
records; part of TIKA-4848 step 3 (TIKA-4848).
diff --git a/docs/modules/ROOT/pages/advanced/setting-limits.adoc
b/docs/modules/ROOT/pages/advanced/setting-limits.adoc
index d1f4fe0848..98588bf65f 100644
--- a/docs/modules/ROOT/pages/advanced/setting-limits.adoc
+++ b/docs/modules/ROOT/pages/advanced/setting-limits.adoc
@@ -383,8 +383,10 @@ chain are kept). `REDACTED`: exception class names only,
no frames.
}
----
-The same policy applies to the container and to every embedded document. It is
-loaded from the config only and is rejected in a per-request `parse-context`
+The same policy applies to the container and to every embedded document, to
+parser-level warnings (`tk:exception:warn`, `tk:exception:embedded-stream`), to
+the messages a pipes worker returns on fetch/emit/crash, and to tika-server's
+`422` error bodies. It is loaded from the config only and is rejected in a
per-request `parse-context`
(tika-server `/rmeta/config` and friends, `/pipes`, `/async`), so a caller
cannot
turn redaction back off.
diff --git a/docs/modules/ROOT/pages/configuration/index.adoc
b/docs/modules/ROOT/pages/configuration/index.adoc
index 9d1407a603..c525061962 100644
--- a/docs/modules/ROOT/pages/configuration/index.adoc
+++ b/docs/modules/ROOT/pages/configuration/index.adoc
@@ -43,6 +43,7 @@ optional; anything you omit uses its defaults.
"auto-detect-parser": { /* AutoDetectParser options */ },
"parse-context": {
"timeout-limits": { /* progress + total task timeouts */ },
+ "exception-reporting": { /* how much exception detail is reported */ },
"unpack-config": { /* embedded-byte extraction */ }
/* other SelfConfiguring components by component name */
},
diff --git a/docs/modules/ROOT/pages/security.adoc
b/docs/modules/ROOT/pages/security.adoc
index 24c2c080fa..b7f7f936d7 100644
--- a/docs/modules/ROOT/pages/security.adoc
+++ b/docs/modules/ROOT/pages/security.adoc
@@ -34,7 +34,10 @@ process, and containing that is the caller's responsibility:
the project does no
of service as a security issue when you opt out of the built-in sandboxing and
parse files
directly in your application. Tika's limits (`output-limits.writeLimit`,
`embedded-limits.maxCount`, the metadata limiter) bound what a parse
*produces*, not the work
-it does to produce it.
+it does to produce it. Exception detail (stack traces, messages that may quote
paths or document
+text) is reported in full by default; `exception-reporting` in the
`parse-context` section
+redacts and bounds it for callers less trusted than the operator
+(xref:advanced/setting-limits.adoc#_exception_reporting[details]).
Concretely: an uncaught `RuntimeException`, `StackOverflowError`, or
`OutOfMemoryError` from an
in-process parse of a malformed or malicious file is a bug — please report it
in
diff --git a/docs/modules/ROOT/pages/using-tika/server/index.adoc
b/docs/modules/ROOT/pages/using-tika/server/index.adoc
index 3328c0824e..12f3c6df55 100644
--- a/docs/modules/ROOT/pages/using-tika/server/index.adoc
+++ b/docs/modules/ROOT/pages/using-tika/server/index.adoc
@@ -446,15 +446,26 @@ before the exception are discarded. Known gap.
[IMPORTANT]
====
-Exception detail is returned in full, and Tika does not redact it. Stack
traces and their messages
-can contain the spooled file's path, the source filename, and fragments of the
document. That
-detail appears in `tk:exception:*` metadata on **successful** parses as well
as in error bodies —
-including inside the `*.metadata.json` entries of an `/unpack` zip — so no
single switch keeps it
-inside the server.
-
-If Tika's output is forwarded somewhere less trusted than the server itself,
filter it on the way
-out: configure a `MetadataFilter` to drop the fields you do not want, and
treat the whole response
-as potentially containing document-derived text.
+By default exception detail is returned in full. Stack traces and their
messages can contain the
+spooled file's path, the source filename, and fragments of the document. That
detail appears in
+`tk:exception:*` metadata on **successful** parses as well as in error bodies
— including inside
+the `*.metadata.json` entries of an `/unpack` zip.
+
+One config setting governs all of those channels — the metadata values, the
`422` bodies above,
+and the `message` of a `/pipes` or `/async` result:
+
+[source,json]
+----
+"parse-context": {
+ "exception-reporting": { "level": "MESSAGE_REDACTED", "maxLength": 10000 }
+}
+----
+
+`MESSAGE_REDACTED` keeps class names and frames but strips every message;
`REDACTED` keeps class
+names only. It is refused in a per-request config so callers cannot turn it
back off. See
+xref:advanced/setting-limits.adoc#_exception_reporting[Exception reporting].
Set it whenever the
+server's callers are less trusted than its operator; a `MetadataFilter` can
additionally drop the
+`tk:exception:*` fields outright.
====
=== Truncated results (`PARTIAL_TIMEOUT`)
diff --git
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerParseExceptionMapper.java
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerParseExceptionMapper.java
index 76ceac24fd..8456d72e1e 100644
---
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerParseExceptionMapper.java
+++
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerParseExceptionMapper.java
@@ -17,25 +17,28 @@
package org.apache.tika.server.core;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.Writer;
-
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
+import org.apache.tika.config.ExceptionReporting;
import org.apache.tika.exception.EncryptedDocumentException;
import org.apache.tika.exception.TikaException;
import org.apache.tika.exception.UnsupportedFormatException;
+import org.apache.tika.utils.ExceptionUtils;
@Provider
public class TikaServerParseExceptionMapper implements
ExceptionMapper<TikaServerParseException> {
+ private final ExceptionReporting exceptionReporting;
public TikaServerParseExceptionMapper() {
+ this(ExceptionReporting.DEFAULT);
+ }
+
+ public TikaServerParseExceptionMapper(ExceptionReporting
exceptionReporting) {
+ this.exceptionReporting = exceptionReporting;
}
public Response toResponse(TikaServerParseException e) {
@@ -70,28 +73,13 @@ public class TikaServerParseExceptionMapper implements
ExceptionMapper<TikaServe
}
private Response buildResponse(Throwable cause, int i) {
- if (cause != null) {
- Writer result = new StringWriter();
- PrintWriter writer = new PrintWriter(result);
- cause.printStackTrace(writer);
- writer.flush();
- try {
- result.flush();
- } catch (IOException e) {
- //something went seriously wrong
- return Response
- .status(500)
- .build();
- }
- return Response
- .status(i)
- .entity(result.toString())
- .type("text/plain")
- .build();
- } else {
- return Response
- .status(i)
- .build();
+ if (cause == null) {
+ return Response.status(i).build();
}
+ return Response
+ .status(i)
+ .entity(ExceptionUtils.format(cause, exceptionReporting))
+ .type("text/plain")
+ .build();
}
}
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 e83b8a67cb..0de1e88ceb 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
@@ -431,7 +431,7 @@ public class TikaServerProcess {
writers.add(new JSONMessageBodyWriter());
writers.add(new TextMessageBodyWriter());
writers.addAll(loadWriterServices());
- writers.add(new TikaServerParseExceptionMapper());
+ writers.add(new
TikaServerParseExceptionMapper(tikaResource.getExceptionReporting()));
writers.add(new BadRequestExceptionMapper());
writers.add(new JSONObjWriter());
diff --git
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/MetadataResource.java
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/MetadataResource.java
index eaecf7aa8b..fe6ece30c2 100644
---
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/MetadataResource.java
+++
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/MetadataResource.java
@@ -27,8 +27,10 @@ import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
@@ -36,7 +38,6 @@ import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.tika.exception.TikaException;
import org.apache.tika.extractor.DocumentSelector;
import org.apache.tika.extractor.SkipEmbeddedDocumentSelector;
import org.apache.tika.io.TikaInputStream;
@@ -46,7 +47,6 @@ import org.apache.tika.parser.ParseContext;
import org.apache.tika.pipes.api.ParseMode;
import org.apache.tika.sax.BasicContentHandlerFactory;
import org.apache.tika.sax.ContentHandlerFactory;
-import org.apache.tika.server.core.TikaServerParseException;
@Path("/meta")
@@ -153,7 +153,13 @@ public class MetadataResource {
String containerException =
metadata.get(TikaCoreProperties.CONTAINER_EXCEPTION);
if (containerException != null && !containerException.isEmpty()) {
- throw new TikaServerParseException(new
TikaException(containerException));
+ // Already policy-formatted; wrapping it as an exception message
would let the
+ // mapper redact it away and append this server's own frames.
+ throw new WebApplicationException(Response
+ .status(422)
+ .entity(containerException)
+ .type(MediaType.TEXT_PLAIN)
+ .build());
}
if (metadata.get(field) == null) {
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 03f1354895..aa57ed9ef6 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
@@ -51,6 +51,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.tika.Tika;
+import org.apache.tika.config.ExceptionReporting;
import org.apache.tika.config.JsonConfig;
import org.apache.tika.config.OutputLimits;
import org.apache.tika.config.loader.TikaLoader;
@@ -92,6 +93,7 @@ public class TikaResource {
// createRequestContext); the forked worker loads these same defaults from
the same config.
private final MetadataWriteLimiterFactory
configMetadataWriteLimiterFactory;
private final OutputLimits configOutputLimits;
+ private final ExceptionReporting configExceptionReporting;
private final boolean configSuppliesContentHandlerFactory;
/**
@@ -110,6 +112,7 @@ public class TikaResource {
ParseContext configDefaults = loadConfigDefaults();
this.configMetadataWriteLimiterFactory =
configDefaults.get(MetadataWriteLimiterFactory.class);
this.configOutputLimits = OutputLimits.get(configDefaults);
+ this.configExceptionReporting = ExceptionReporting.get(configDefaults);
this.configSuppliesContentHandlerFactory =
configDefaults.get(ContentHandlerFactory.class) != null;
}
@@ -123,6 +126,11 @@ public class TikaResource {
return pipesParsingHelper;
}
+ /** The config's exception-reporting policy, for channels that have no
ParseContext. */
+ public ExceptionReporting getExceptionReporting() {
+ return configExceptionReporting;
+ }
+
/**
* Reads the config's {@code parse-context} section. Private and called
once: the values we
* need are cached above, and a request must not carry these defaults (see
diff --git
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/RedactedStackTraceTest.java
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/RedactedStackTraceTest.java
new file mode 100644
index 0000000000..0e699a4d1b
--- /dev/null
+++
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/RedactedStackTraceTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.server.core;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import jakarta.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.metadata.TikaCoreProperties;
+import org.apache.tika.serialization.JsonMetadataList;
+import org.apache.tika.serialization.config.JsonConfigHelper;
+import org.apache.tika.server.core.resource.MetadataResource;
+import org.apache.tika.server.core.resource.RecursiveMetadataResource;
+import org.apache.tika.server.core.resource.UnpackerResource;
+import org.apache.tika.server.core.writer.JSONMessageBodyWriter;
+import org.apache.tika.server.core.writer.MetadataListMessageBodyWriter;
+import org.apache.tika.server.core.writer.TextMessageBodyWriter;
+
+/**
+ * With exception-reporting set to MESSAGE_REDACTED in the server config, no
channel may leak
+ * an exception message: /rmeta metadata, /unpack 422 body, /meta/{field} 422
body.
+ * {@link StackTraceTest} pins the FULL default.
+ */
+public class RedactedStackTraceTest extends CXFTestBase {
+
+ private static final String TEST_NULL =
"test-documents/mock/null_pointer.xml";
+ private static final String MESSAGE = "null pointer message";
+ private static final String CLASS = "java.lang.NullPointerException";
+ private static final String UNPACK_CONFIG_TEMPLATE =
"/configs/cxf-unpack-test-template.json";
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ @TempDir
+ private static Path unpackTempDir;
+
+ @Override
+ protected void setUpResources(JAXRSServerFactoryBean sf) {
+ List<ResourceProvider> providers = new ArrayList<>();
+ providers.add(new SingletonResourceProvider(new
MetadataResource(tikaResource)));
+ providers.add(new SingletonResourceProvider(new
RecursiveMetadataResource(tikaResource)));
+ providers.add(new SingletonResourceProvider(tikaResource));
+ providers.add(new SingletonResourceProvider(new
UnpackerResource(tikaResource)));
+ sf.setResourceProviders(providers);
+ }
+
+ @Override
+ protected void setUpProviders(JAXRSServerFactoryBean sf) {
+ List<Object> providers = new ArrayList<>();
+ providers.add(new
TikaServerParseExceptionMapper(tikaResource.getExceptionReporting()));
+ providers.add(new JSONMessageBodyWriter());
+ providers.add(new TextMessageBodyWriter());
+ providers.add(new MetadataListMessageBodyWriter());
+ sf.setProviders(providers);
+ }
+
+ @Override
+ protected InputStream getPipesConfigInputStream() throws IOException {
+ Map<String, Object> replacements = new HashMap<>();
+ replacements.put("UNPACK_EMITTER_BASE_PATH",
unpackTempDir.toAbsolutePath().toString());
+ replacements.put("PLUGINS_PATHS",
+
Paths.get("target/plugins").toAbsolutePath().toString().replace("\\", "/"));
+ replacements.put("TIMEOUT_MILLIS", 60000L);
+ JsonNode config =
JsonConfigHelper.loadFromResource(UNPACK_CONFIG_TEMPLATE,
+ CXFTestBase.class, replacements);
+ ((ObjectNode)
config.get("parse-context")).putObject("exception-reporting")
+ .put("level", "MESSAGE_REDACTED").put("maxLength", 10000);
+ return new ByteArrayInputStream(
+
MAPPER.writeValueAsString(config).getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ protected Path getUnpackEmitterBasePath() {
+ return unpackTempDir;
+ }
+
+ private static void assertRedacted(String s) {
+ assertTrue(s.contains(CLASS), s);
+ assertTrue(s.contains("\tat "), s);
+ assertFalse(s.contains(MESSAGE), s);
+ }
+
+ @Test
+ public void rmeta() throws Exception {
+ Response response = WebClient.create(endPoint + "/rmeta")
+ .accept("application/json")
+ .put(ClassLoader.getSystemResourceAsStream(TEST_NULL));
+ assertEquals(200, response.getStatus());
+ List<Metadata> list = JsonMetadataList.fromJson(
+ new java.io.InputStreamReader((InputStream)
response.getEntity(),
+ StandardCharsets.UTF_8));
+
assertRedacted(list.get(0).get(TikaCoreProperties.CONTAINER_EXCEPTION));
+ }
+
+ @Test
+ public void unpack() throws Exception {
+ Response response = WebClient.create(endPoint + "/unpack")
+ .put(ClassLoader.getSystemResourceAsStream(TEST_NULL));
+ assertEquals(422, response.getStatus());
+ assertRedacted(getStringFromInputStream((InputStream)
response.getEntity()));
+ }
+
+ @Test
+ public void metaField() throws Exception {
+ Response response = WebClient.create(endPoint + "/meta/Content-Type")
+ .accept("text/plain")
+ .put(ClassLoader.getSystemResourceAsStream(TEST_NULL));
+ assertEquals(422, response.getStatus());
+ String body = getStringFromInputStream((InputStream)
response.getEntity());
+ assertRedacted(body);
+ // the body is the container exception itself, not re-wrapped with
server frames
+ assertFalse(body.contains("MetadataResource"), body);
+ }
+}
diff --git
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerParseExceptionMapperTest.java
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerParseExceptionMapperTest.java
new file mode 100644
index 0000000000..9d9883d94d
--- /dev/null
+++
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerParseExceptionMapperTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.server.core;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import jakarta.ws.rs.core.Response;
+import org.junit.jupiter.api.Test;
+
+import org.apache.tika.config.ExceptionReporting;
+import org.apache.tika.exception.TikaException;
+
+public class TikaServerParseExceptionMapperTest {
+
+ private static final String SECRET = "secret /path/to/file";
+
+ private static String body(ExceptionReporting reporting) {
+ Response r = new TikaServerParseExceptionMapper(reporting)
+ .toResponse(new TikaServerParseException(new
TikaException(SECRET)));
+ assertEquals(422, r.getStatus());
+ return (String) r.getEntity();
+ }
+
+ @Test
+ public void fullByDefault() {
+ String body = body(null);
+ assertTrue(body.contains("org.apache.tika.exception.TikaException: " +
SECRET), body);
+ }
+
+ @Test
+ public void messageRedacted() {
+ String body = body(new
ExceptionReporting(ExceptionReporting.Level.MESSAGE_REDACTED, -1));
+ assertTrue(body.contains("org.apache.tika.exception.TikaException"),
body);
+ assertTrue(body.contains("\tat "), body);
+ assertFalse(body.contains(SECRET), body);
+ }
+
+ @Test
+ public void maxLength() {
+ String body = body(new
ExceptionReporting(ExceptionReporting.Level.FULL, 30));
+ assertTrue(body.endsWith("...[truncated]"), body);
+ assertFalse(body.contains(SECRET), body);
+ }
+}
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 044303b17c..fe35775d2e 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
@@ -9,6 +9,10 @@
"timeout-limits": {
"totalTaskTimeoutMillis": 3600000,
"progressTimeoutMillis": 120000
+ },
+ "exception-reporting": {
+ "level": "MESSAGE_REDACTED",
+ "maxLength": 10000
}
},
"parsers": [