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

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


The following commit(s) were added to refs/heads/main by this push:
     new b6f6b4708543 CAMEL-24423: camel-tika - filter parsed document metadata 
before mapping it to headers (#25819)
b6f6b4708543 is described below

commit b6f6b4708543677a8b6032304c66cbe7f687a2fb
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Aug 28 12:08:02 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]>
    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 ++++++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    | 17 +++++
 4 files changed, 117 insertions(+), 4 deletions(-)

diff --git a/components/camel-tika/pom.xml b/components/camel-tika/pom.xml
index 33257b665291..a9409bf1c66e 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");
+            }
+        };
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index afb004c9e82b..029f23952ec0 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -702,3 +702,20 @@ inherit values set on `defaultInstance`, and 
`defaultInstance` itself remains un
 
 Routes that compared unmarshalled bodies by identity, or that mutated one body 
expecting the change to
 be visible on another, must be updated.
+
+=== camel-tika
+
+The `tika:parse` producer copies the metadata of the parsed document 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="..."/>`, for
+example, reached the message as `CamelFileName` and would then be picked up by 
a later `file:`
+producer.
+
+Parsed metadata names are now filtered the same way a consumer filters names 
supplied by an external
+sender: a name that starts with `Camel`, `camel` or `org.apache.camel.` 
(matched case-insensitively)
+is skipped and logged at `DEBUG` instead of being set as a header. Metadata 
outside that namespace is
+mapped exactly as before.
+
+Routes that deliberately read a `Camel`-prefixed header produced by the Tika 
parse must set it
+themselves after the `tika:parse` step, for example with a `setHeader` reading 
the corresponding
+non-prefixed metadata name.

Reply via email to