Skip to content

Commit 89de688

Browse files
authored
improve unpack endpoint (#2851)
1 parent 7478ffd commit 89de688

9 files changed

Lines changed: 156 additions & 3 deletions

File tree

tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ private static ServerDetails initServer(TikaServerConfig tikaServerConfig) throw
185185
LOG.info("Pipes-based parsing enabled for /tika and /rmeta endpoints");
186186
}
187187

188-
TikaResource.init(tikaLoader, serverStatus, pipesParsingHelper);
188+
TikaResource.init(tikaLoader, serverStatus, pipesParsingHelper,
189+
tikaServerConfig.isEnableUnsecureFeatures());
189190
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
190191

191192
List<ResourceProvider> resourceProviders = new ArrayList<>();

tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/resource/TikaResource.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@
3737
import jakarta.ws.rs.Path;
3838
import jakarta.ws.rs.PathParam;
3939
import jakarta.ws.rs.Produces;
40+
import jakarta.ws.rs.WebApplicationException;
4041
import jakarta.ws.rs.core.Context;
4142
import jakarta.ws.rs.core.HttpHeaders;
43+
import jakarta.ws.rs.core.MediaType;
4244
import jakarta.ws.rs.core.MultivaluedMap;
45+
import jakarta.ws.rs.core.Response;
4346
import jakarta.ws.rs.core.StreamingOutput;
4447
import org.apache.cxf.attachment.ContentDisposition;
4548
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
@@ -85,19 +88,24 @@ public class TikaResource {
8588
private static ServerStatus SERVER_STATUS = null;
8689
private static PipesParsingHelper PIPES_PARSING_HELPER = null;
8790
private static MetadataWriteLimiterFactory DEFAULT_METADATA_WRITE_LIMITER_FACTORY = null;
91+
// Whether per-request config injection (multipart "config" parts) is permitted.
92+
// Enforced in setupMultipartConfig so every config-consuming endpoint honors it.
93+
private static boolean ENABLE_UNSECURE_FEATURES = false;
8894

8995
/**
9096
* Initialize TikaResource with pipes-based parsing for process isolation.
9197
*
9298
* @param tikaLoader the Tika loader
9399
* @param serverStatus server status tracker
94100
* @param pipesParsingHelper helper for pipes-based parsing, may be null if /tika endpoint is not enabled
101+
* @param enableUnsecureFeatures whether per-request config injection is permitted
95102
*/
96103
public static void init(TikaLoader tikaLoader, ServerStatus serverStatus,
97-
PipesParsingHelper pipesParsingHelper) {
104+
PipesParsingHelper pipesParsingHelper, boolean enableUnsecureFeatures) {
98105
TIKA_LOADER = tikaLoader;
99106
SERVER_STATUS = serverStatus;
100107
PIPES_PARSING_HELPER = pipesParsingHelper;
108+
ENABLE_UNSECURE_FEATURES = enableUnsecureFeatures;
101109
// MetadataWriteLimiterFactory is now loaded dynamically via loadParseContext()
102110
}
103111

@@ -264,6 +272,16 @@ public static TikaInputStream setupMultipartConfig(List<Attachment> attachments,
264272
}
265273
}
266274

275+
// Enforce the per-request config gate where the config part is actually
276+
// consumed, so every endpoint that accepts a config part honors
277+
// enableUnsecureFeatures uniformly.
278+
if (configAtt != null && !ENABLE_UNSECURE_FEATURES) {
279+
throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN)
280+
.entity("Per-request configuration is disabled. Set enableUnsecureFeatures=true in server config.")
281+
.type(MediaType.TEXT_PLAIN)
282+
.build());
283+
}
284+
267285
if (fileAtt == null) {
268286
throw new IOException("Missing file attachment (use name='file' or send single unnamed attachment)");
269287
}

tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/CXFTestBase.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public void setUp() throws Exception {
216216
PipesParsingHelper pipesParsingHelper = new PipesParsingHelper(this.pipesParser, pipesConfig,
217217
inputTempDirectory, getUnpackEmitterBasePath());
218218

219-
TikaResource.init(tika, new ServerStatus(), pipesParsingHelper);
219+
TikaResource.init(tika, new ServerStatus(), pipesParsingHelper, isEnableUnsecureFeatures());
220220
} finally {
221221
// Only delete tika config, keep pipes config for child processes
222222
Files.deleteIfExists(tmp);
@@ -367,6 +367,15 @@ protected Path getUnpackEmitterBasePath() throws IOException {
367367
return null;
368368
}
369369

370+
/**
371+
* Whether per-request config injection is permitted. Defaults to false, matching
372+
* the production default. Tests that POST a multipart "config" part must override
373+
* this to return true, otherwise the config part is rejected with 403.
374+
*/
375+
protected boolean isEnableUnsecureFeatures() {
376+
return false;
377+
}
378+
370379
protected InputStream getPipesConfigInputStream() throws IOException {
371380
if (getPipesInputPath() == null) {
372381
return null;

tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/MetadataResourceTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ public class MetadataResourceTest extends CXFTestBase {
5858

5959
private static final String META_PATH = "/meta";
6060

61+
@Override
62+
protected boolean isEnableUnsecureFeatures() {
63+
return true; // exercises per-request config injection
64+
}
65+
6166
@Override
6267
protected void setUpResources(JAXRSServerFactoryBean sf) {
6368
sf.setResourceClasses(MetadataResource.class, XMPMetadataResource.class);

tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/RecursiveMetadataResourceTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public class RecursiveMetadataResourceTest extends CXFTestBase {
6363

6464
private static final String TEST_RECURSIVE_DOC = "test-documents/test_recursive_embedded.docx";
6565

66+
@Override
67+
protected boolean isEnableUnsecureFeatures() {
68+
return true; // exercises per-request config injection
69+
}
70+
6671
@Override
6772
protected void setUpResources(JAXRSServerFactoryBean sf) {
6873
sf.setResourceClasses(RecursiveMetadataResource.class);

tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ public class TikaResourceTest extends CXFTestBase {
6464
private static final String TIKA_PATH = "/tika";
6565
private static final int UNPROCESSEABLE = 422;
6666

67+
@Override
68+
protected boolean isEnableUnsecureFeatures() {
69+
return true; // exercises per-request config injection
70+
}
71+
6772
@Override
6873
protected void setUpResources(JAXRSServerFactoryBean sf) {
6974
sf.setResourceClasses(TikaResource.class);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.tika.server.standard;
18+
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
22+
import java.io.ByteArrayInputStream;
23+
import java.io.InputStream;
24+
import java.nio.charset.StandardCharsets;
25+
import java.util.ArrayList;
26+
import java.util.Arrays;
27+
import java.util.List;
28+
29+
import jakarta.ws.rs.core.Response;
30+
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
31+
import org.apache.cxf.jaxrs.client.WebClient;
32+
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
33+
import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
34+
import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
35+
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
36+
import org.junit.jupiter.api.Test;
37+
38+
import org.apache.tika.server.core.CXFTestBase;
39+
import org.apache.tika.server.core.TikaServerParseExceptionMapper;
40+
import org.apache.tika.server.core.resource.UnpackerResource;
41+
import org.apache.tika.server.core.writer.TarWriter;
42+
import org.apache.tika.server.core.writer.ZipWriter;
43+
44+
/**
45+
* Verifies that /unpack and /unpack/all honor enableUnsecureFeatures: when per-request
46+
* config injection is disabled (the default), a multipart "config" part is rejected
47+
* with 403. Counterpart to {@link UnpackerResourceWithConfigTest}, which covers the
48+
* enabled path.
49+
*/
50+
public class UnpackerResourceConfigDisabledTest extends CXFTestBase {
51+
52+
private static final String BASE_PATH = "/unpack";
53+
private static final String ALL_PATH = BASE_PATH + "/all";
54+
55+
// isEnableUnsecureFeatures() is intentionally NOT overridden; it defaults to false
56+
// so a config part must be rejected.
57+
58+
@Override
59+
protected void setUpResources(JAXRSServerFactoryBean sf) {
60+
sf.setResourceClasses(UnpackerResource.class);
61+
sf.setResourceProvider(UnpackerResource.class, new SingletonResourceProvider(new UnpackerResource()));
62+
}
63+
64+
@Override
65+
protected void setUpProviders(JAXRSServerFactoryBean sf) {
66+
List<Object> providers = new ArrayList<>();
67+
providers.add(new TarWriter());
68+
providers.add(new ZipWriter());
69+
providers.add(new TikaServerParseExceptionMapper(false));
70+
sf.setProviders(providers);
71+
}
72+
73+
private Response postWithConfig(String path) {
74+
ContentDisposition fileCd = new ContentDisposition("form-data; name=\"file\"; filename=\"test.txt\"");
75+
Attachment fileAtt = new Attachment("file",
76+
new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)), fileCd);
77+
Attachment configAtt = new Attachment("config", "application/json",
78+
new ByteArrayInputStream("{\"pdf-parser\":{}}".getBytes(StandardCharsets.UTF_8)));
79+
return WebClient
80+
.create(endPoint + path)
81+
.type("multipart/form-data")
82+
.accept("application/zip")
83+
.post(new MultipartBody(Arrays.asList(fileAtt, configAtt)));
84+
}
85+
86+
@Test
87+
public void testConfigPartRejectedOnUnpackWhenDisabled() throws Exception {
88+
Response response = postWithConfig(BASE_PATH);
89+
assertEquals(403, response.getStatus());
90+
String msg = getStringFromInputStream((InputStream) response.getEntity());
91+
assertTrue(msg.contains("Per-request configuration is disabled"),
92+
"expected the config-disabled message, got: " + msg);
93+
}
94+
95+
@Test
96+
public void testConfigPartRejectedOnUnpackAllWhenDisabled() throws Exception {
97+
Response response = postWithConfig(ALL_PATH);
98+
assertEquals(403, response.getStatus());
99+
}
100+
}

tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ public class UnpackerResourceTest extends CXFTestBase {
9898

9999
private Path unpackTempDir;
100100

101+
@Override
102+
protected boolean isEnableUnsecureFeatures() {
103+
return true; // exercises per-request config injection
104+
}
105+
101106
@Override
102107
protected void setUpResources(JAXRSServerFactoryBean sf) {
103108
sf.setResourceClasses(UnpackerResource.class);

tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/UnpackerResourceWithConfigTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ public class UnpackerResourceWithConfigTest extends CXFTestBase {
7272

7373
private Path unpackTempDir;
7474

75+
@Override
76+
protected boolean isEnableUnsecureFeatures() {
77+
return true; // exercises per-request config injection
78+
}
79+
7580
@Override
7681
protected void setUpResources(JAXRSServerFactoryBean sf) {
7782
sf.setResourceClasses(UnpackerResource.class);

0 commit comments

Comments
 (0)