Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ private static ServerDetails initServer(TikaServerConfig tikaServerConfig) throw
LOG.info("Pipes-based parsing enabled for /tika and /rmeta endpoints");
}

TikaResource.init(tikaLoader, serverStatus, pipesParsingHelper);
TikaResource.init(tikaLoader, serverStatus, pipesParsingHelper,
tikaServerConfig.isEnableUnsecureFeatures());
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();

List<ResourceProvider> resourceProviders = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@
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.StreamingOutput;
import org.apache.cxf.attachment.ContentDisposition;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
Expand Down Expand Up @@ -85,19 +88,24 @@ public class TikaResource {
private static ServerStatus SERVER_STATUS = null;
private static PipesParsingHelper PIPES_PARSING_HELPER = null;
private static MetadataWriteLimiterFactory DEFAULT_METADATA_WRITE_LIMITER_FACTORY = null;
// Whether per-request config injection (multipart "config" parts) is permitted.
// Enforced in setupMultipartConfig so every config-consuming endpoint honors it.
private static boolean ENABLE_UNSECURE_FEATURES = false;

/**
* Initialize TikaResource with pipes-based parsing for process isolation.
*
* @param tikaLoader the Tika loader
* @param serverStatus server status tracker
* @param pipesParsingHelper helper for pipes-based parsing, may be null if /tika endpoint is not enabled
* @param enableUnsecureFeatures whether per-request config injection is permitted
*/
public static void init(TikaLoader tikaLoader, ServerStatus serverStatus,
PipesParsingHelper pipesParsingHelper) {
PipesParsingHelper pipesParsingHelper, boolean enableUnsecureFeatures) {
TIKA_LOADER = tikaLoader;
SERVER_STATUS = serverStatus;
PIPES_PARSING_HELPER = pipesParsingHelper;
ENABLE_UNSECURE_FEATURES = enableUnsecureFeatures;
// MetadataWriteLimiterFactory is now loaded dynamically via loadParseContext()
}

Expand Down Expand Up @@ -264,6 +272,16 @@ public static TikaInputStream setupMultipartConfig(List<Attachment> attachments,
}
}

// Enforce the per-request config gate where the config part is actually
// consumed, so every endpoint that accepts a config part honors
// enableUnsecureFeatures uniformly.
if (configAtt != null && !ENABLE_UNSECURE_FEATURES) {
throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN)
.entity("Per-request configuration is disabled. Set enableUnsecureFeatures=true in server config.")
.type(MediaType.TEXT_PLAIN)
.build());
}

if (fileAtt == null) {
throw new IOException("Missing file attachment (use name='file' or send single unnamed attachment)");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public void setUp() throws Exception {
PipesParsingHelper pipesParsingHelper = new PipesParsingHelper(this.pipesParser, pipesConfig,
inputTempDirectory, getUnpackEmitterBasePath());

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

/**
* Whether per-request config injection is permitted. Defaults to false, matching
* the production default. Tests that POST a multipart "config" part must override
* this to return true, otherwise the config part is rejected with 403.
*/
protected boolean isEnableUnsecureFeatures() {
return false;
}

protected InputStream getPipesConfigInputStream() throws IOException {
if (getPipesInputPath() == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ public class MetadataResourceTest extends CXFTestBase {

private static final String META_PATH = "/meta";

@Override
protected boolean isEnableUnsecureFeatures() {
return true; // exercises per-request config injection
}

@Override
protected void setUpResources(JAXRSServerFactoryBean sf) {
sf.setResourceClasses(MetadataResource.class, XMPMetadataResource.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public class RecursiveMetadataResourceTest extends CXFTestBase {

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

@Override
protected boolean isEnableUnsecureFeatures() {
return true; // exercises per-request config injection
}

@Override
protected void setUpResources(JAXRSServerFactoryBean sf) {
sf.setResourceClasses(RecursiveMetadataResource.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public class TikaResourceTest extends CXFTestBase {
private static final String TIKA_PATH = "/tika";
private static final int UNPROCESSEABLE = 422;

@Override
protected boolean isEnableUnsecureFeatures() {
return true; // exercises per-request config injection
}

@Override
protected void setUpResources(JAXRSServerFactoryBean sf) {
sf.setResourceClasses(TikaResource.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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.standard;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import jakarta.ws.rs.core.Response;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.junit.jupiter.api.Test;

import org.apache.tika.server.core.CXFTestBase;
import org.apache.tika.server.core.TikaServerParseExceptionMapper;
import org.apache.tika.server.core.resource.UnpackerResource;
import org.apache.tika.server.core.writer.TarWriter;
import org.apache.tika.server.core.writer.ZipWriter;

/**
* Verifies that /unpack and /unpack/all honor enableUnsecureFeatures: when per-request
* config injection is disabled (the default), a multipart "config" part is rejected
* with 403. Counterpart to {@link UnpackerResourceWithConfigTest}, which covers the
* enabled path.
*/
public class UnpackerResourceConfigDisabledTest extends CXFTestBase {

private static final String BASE_PATH = "/unpack";
private static final String ALL_PATH = BASE_PATH + "/all";

// isEnableUnsecureFeatures() is intentionally NOT overridden; it defaults to false
// so a config part must be rejected.

@Override
protected void setUpResources(JAXRSServerFactoryBean sf) {
sf.setResourceClasses(UnpackerResource.class);
sf.setResourceProvider(UnpackerResource.class, new SingletonResourceProvider(new UnpackerResource()));
}

@Override
protected void setUpProviders(JAXRSServerFactoryBean sf) {
List<Object> providers = new ArrayList<>();
providers.add(new TarWriter());
providers.add(new ZipWriter());
providers.add(new TikaServerParseExceptionMapper(false));
sf.setProviders(providers);
}

private Response postWithConfig(String path) {
ContentDisposition fileCd = new ContentDisposition("form-data; name=\"file\"; filename=\"test.txt\"");
Attachment fileAtt = new Attachment("file",
new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)), fileCd);
Attachment configAtt = new Attachment("config", "application/json",
new ByteArrayInputStream("{\"pdf-parser\":{}}".getBytes(StandardCharsets.UTF_8)));
return WebClient
.create(endPoint + path)
.type("multipart/form-data")
.accept("application/zip")
.post(new MultipartBody(Arrays.asList(fileAtt, configAtt)));
}

@Test
public void testConfigPartRejectedOnUnpackWhenDisabled() throws Exception {
Response response = postWithConfig(BASE_PATH);
assertEquals(403, response.getStatus());
String msg = getStringFromInputStream((InputStream) response.getEntity());
assertTrue(msg.contains("Per-request configuration is disabled"),
"expected the config-disabled message, got: " + msg);
}

@Test
public void testConfigPartRejectedOnUnpackAllWhenDisabled() throws Exception {
Response response = postWithConfig(ALL_PATH);
assertEquals(403, response.getStatus());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ public class UnpackerResourceTest extends CXFTestBase {

private Path unpackTempDir;

@Override
protected boolean isEnableUnsecureFeatures() {
return true; // exercises per-request config injection
}

@Override
protected void setUpResources(JAXRSServerFactoryBean sf) {
sf.setResourceClasses(UnpackerResource.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ public class UnpackerResourceWithConfigTest extends CXFTestBase {

private Path unpackTempDir;

@Override
protected boolean isEnableUnsecureFeatures() {
return true; // exercises per-request config injection
}

@Override
protected void setUpResources(JAXRSServerFactoryBean sf) {
sf.setResourceClasses(UnpackerResource.class);
Expand Down
Loading