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

Croway pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 14eeda5d48f79cc9f640e83474d70546233501b5
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Aug 31 10:17:59 2026 +0200

    CAMEL-24423: camel-tika - filter parsed document metadata before mapping it 
to headers (#25819)
    
    TikaProducer.convertMetadataToHeaders() copied every metadata name produced 
by the
    parse straight onto the Camel message. Those names come out of the document 
itself,
    so a document could ask for any header name at all, including names in the
    Camel-internal namespace - an HTML <meta name="CamelFileName" 
content="../../x"/>
    reached the message as CamelFileName and would then be picked up by a later 
file:
    producer.
    
    Filter the names the same way a consumer filters names supplied by an 
external
    sender: a DefaultHeaderFilterStrategy with lowerCase matching and 
inFilterStartsWith
    of Camel, camel and org.apache.camel. A filtered name is skipped and logged 
at DEBUG.
    Metadata outside that namespace is mapped exactly as before.
    
    Filtering rather than prefixing all parsed metadata keeps the change small 
enough to
    backport; prefixing would rename every header the component produces today.
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    
    (cherry picked from commit b6f6b4708543677a8b6032304c66cbe7f687a2fb)
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 components/camel-tika/pom.xml                      |  5 ++
 .../apache/camel/component/tika/TikaProducer.java  | 25 ++++++--
 .../tika/TikaMetadataHeaderFilterTest.java         | 74 ++++++++++++++++++++++
 3 files changed, 100 insertions(+), 4 deletions(-)

diff --git a/components/camel-tika/pom.xml b/components/camel-tika/pom.xml
index 14f9bb976493..9d659c2f1ef1 100644
--- a/components/camel-tika/pom.xml
+++ b/components/camel-tika/pom.xml
@@ -86,6 +86,11 @@
             <version>${hamcrest-version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git 
a/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaProducer.java
 
b/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaProducer.java
index 9328dc47d344..6d8f2c2625c2 100644
--- 
a/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaProducer.java
+++ 
b/components/camel-tika/src/main/java/org/apache/camel/component/tika/TikaProducer.java
@@ -35,6 +35,8 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 
 import org.apache.camel.Exchange;
+import org.apache.camel.spi.HeaderFilterStrategy;
+import org.apache.camel.support.DefaultHeaderFilterStrategy;
 import org.apache.camel.support.DefaultProducer;
 import org.apache.tika.config.TikaConfig;
 import org.apache.tika.detect.Detector;
@@ -53,6 +55,8 @@ public class TikaProducer extends DefaultProducer {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(TikaProducer.class);
 
+    private static final HeaderFilterStrategy HEADER_FILTER_STRATEGY = 
createHeaderFilterStrategy();
+
     private final TikaConfiguration tikaConfiguration;
 
     private final Parser parser;
@@ -127,11 +131,15 @@ public class TikaProducer extends DefaultProducer {
         if (metadata != null) {
             for (String metaname : metadata.names()) {
                 String[] values = metadata.getValues(metaname);
-                if (values.length == 1) {
-                    exchange.getIn().setHeader(metaname, values[0]);
-                } else {
-                    exchange.getIn().setHeader(metaname, values);
+                Object value = values.length == 1 ? values[0] : values;
+                // The names come out of the parsed document, so they are 
chosen by whoever produced it.
+                // Filter them the same way a consumer filters names supplied 
by an external sender, so a
+                // document cannot declare a metadata name that lands in the 
Camel-internal namespace.
+                if 
(HEADER_FILTER_STRATEGY.applyFilterToExternalHeaders(metaname, value, 
exchange)) {
+                    LOG.debug("Skipping parsed metadata {} as the name is in 
the Camel-internal namespace", metaname);
+                    continue;
                 }
+                exchange.getIn().setHeader(metaname, value);
             }
         }
     }
@@ -178,4 +186,13 @@ public class TikaProducer extends DefaultProducer {
 
         return handler;
     }
+
+    private static HeaderFilterStrategy createHeaderFilterStrategy() {
+        DefaultHeaderFilterStrategy strategy = new 
DefaultHeaderFilterStrategy();
+        // Match case-insensitively, and cover the fully qualified form as 
well as the Camel prefix
+        strategy.setLowerCase(true);
+        strategy.setInFilterStartsWith("Camel", "camel", "org.apache.camel.");
+        return strategy;
+    }
+
 }
diff --git 
a/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaMetadataHeaderFilterTest.java
 
b/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaMetadataHeaderFilterTest.java
new file mode 100644
index 000000000000..0a8d1e0c8108
--- /dev/null
+++ 
b/components/camel-tika/src/test/java/org/apache/camel/component/tika/TikaMetadataHeaderFilterTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.camel.component.tika;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * The metadata names handed to {@code convertMetadataToHeaders} come out of 
the parsed document, so they are chosen by
+ * whoever produced it. An HTML {@code <meta name="..">} is the most direct 
way to demonstrate that: the name attribute
+ * reaches Tika's metadata verbatim, so a document can ask for any header name 
at all.
+ */
+class TikaMetadataHeaderFilterTest extends CamelTestSupport {
+
+    @EndpointInject("mock:result")
+    protected MockEndpoint resultEndpoint;
+
+    @Test
+    void documentMetadataCannotSetCamelInternalHeaders() throws Exception {
+        String html = "<html><head>"
+                      + "<meta name=\"CamelFileName\" 
content=\"../../pwned\"/>"
+                      + "<meta name=\"camelfilename\" 
content=\"../../pwned\"/>"
+                      + "<meta name=\"CAMELHttpUri\" 
content=\"http://other.example/x\"/>"
+                      + "<meta name=\"org.apache.camel.internal\" 
content=\"nope\"/>"
+                      + "<meta name=\"author\" content=\"kept\"/>"
+                      + "<title>t</title></head><body>hi</body></html>";
+
+        resultEndpoint.setExpectedMessageCount(1);
+        template.sendBody("direct:start", 
html.getBytes(StandardCharsets.UTF_8));
+        resultEndpoint.assertIsSatisfied();
+
+        Exchange exchange = resultEndpoint.getExchanges().get(0);
+        assertThat(exchange.getIn().getHeader(Exchange.FILE_NAME)).isNull();
+        assertThat(exchange.getIn().getHeader("camelfilename")).isNull();
+        assertThat(exchange.getIn().getHeader("CAMELHttpUri")).isNull();
+        
assertThat(exchange.getIn().getHeader("org.apache.camel.internal")).isNull();
+
+        // metadata outside the internal namespace is still mapped, so the 
filter has not simply dropped everything
+        assertThat(exchange.getIn().getHeader("author")).isEqualTo("kept");
+        assertThat(exchange.getIn().getHeader("dc:title")).isEqualTo("t");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start").to("tika:parse").to("mock:result");
+            }
+        };
+    }
+}

Reply via email to