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

davsclaus 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 0606943dfe40 CAMEL-24900: camel-jcr - apply header filtering when 
mapping node properties to Exchange headers
0606943dfe40 is described below

commit 0606943dfe40c1fb205c636beab3cec915e9f436
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Sep 28 17:42:07 2026 +0200

    CAMEL-24900: camel-jcr - apply header filtering when mapping node 
properties to Exchange headers
    
    The jcr producer copied JCR node properties onto the message as headers
    (CamelJcrGetById) and message headers onto node properties (insert)
    without any filtering. A node property named like a Camel internal header,
    for example CamelHttpUri, could therefore steer a later endpoint in the
    route.
    
    JcrEndpoint now implements HeaderFilterStrategyAware with a
    DefaultHeaderFilterStrategy by default, so header names starting with
    Camel or camel (case-insensitively) are no longer copied in either
    direction. The JCR control headers stay stripped unconditionally on
    insert, also when a custom strategy lets Camel* headers through.
    
    Closes #26748
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../org/apache/camel/catalog/components/jcr.json   |  3 +-
 .../camel/component/jcr/JcrEndpointConfigurer.java |  6 ++
 .../camel/component/jcr/JcrEndpointUriFactory.java |  3 +-
 .../org/apache/camel/component/jcr/jcr.json        |  3 +-
 .../apache/camel/component/jcr/JcrEndpoint.java    | 21 ++++-
 .../apache/camel/component/jcr/JcrProducer.java    | 16 +++-
 .../jcr/JcrGetNodeByIdHeaderInjectionTest.java     | 95 ++++++++++++++++++++++
 .../jcr/JcrInsertHeaderInjectionTest.java          | 78 ++++++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    | 14 ++++
 .../endpoint/dsl/JcrEndpointBuilderFactory.java    | 32 ++++++++
 10 files changed, 266 insertions(+), 5 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jcr.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jcr.json
index 497abb9f97ba..3796e983a64c 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jcr.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/jcr.json
@@ -50,6 +50,7 @@
     "exceptionHandler": { "index": 12, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By de [...]
     "exchangePattern": { "index": 13, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
     "lazyStartProducer": { "index": 14, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
-    "password": { "index": 15, "kind": "parameter", "displayName": "Password", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": true, "security": "secret", "description": "Password for login" }
+    "headerFilterStrategy": { "index": 15, "kind": "parameter", "displayName": 
"Header Filter Strategy", "group": "filter", "label": "producer,filter", 
"required": false, "type": "object", "javaType": 
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": 
false, "secret": false, "description": "To use a custom 
org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel 
message." },
+    "password": { "index": 16, "kind": "parameter", "displayName": "Password", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": true, "security": "secret", "description": "Password for login" }
   }
 }
diff --git 
a/components/camel-jcr/src/generated/java/org/apache/camel/component/jcr/JcrEndpointConfigurer.java
 
b/components/camel-jcr/src/generated/java/org/apache/camel/component/jcr/JcrEndpointConfigurer.java
index 3395318e319c..af12eb9156d3 100644
--- 
a/components/camel-jcr/src/generated/java/org/apache/camel/component/jcr/JcrEndpointConfigurer.java
+++ 
b/components/camel-jcr/src/generated/java/org/apache/camel/component/jcr/JcrEndpointConfigurer.java
@@ -32,6 +32,8 @@ public class JcrEndpointConfigurer extends 
PropertyConfigurerSupport implements
         case "exceptionHandler": 
target.setExceptionHandler(property(camelContext, 
org.apache.camel.spi.ExceptionHandler.class, value)); return true;
         case "exchangepattern":
         case "exchangePattern": 
target.setExchangePattern(property(camelContext, 
org.apache.camel.ExchangePattern.class, value)); return true;
+        case "headerfilterstrategy":
+        case "headerFilterStrategy": 
target.setHeaderFilterStrategy(property(camelContext, 
org.apache.camel.spi.HeaderFilterStrategy.class, value)); return true;
         case "lazystartproducer":
         case "lazyStartProducer": 
target.setLazyStartProducer(property(camelContext, boolean.class, value)); 
return true;
         case "nolocal":
@@ -63,6 +65,8 @@ public class JcrEndpointConfigurer extends 
PropertyConfigurerSupport implements
         case "exceptionHandler": return 
org.apache.camel.spi.ExceptionHandler.class;
         case "exchangepattern":
         case "exchangePattern": return org.apache.camel.ExchangePattern.class;
+        case "headerfilterstrategy":
+        case "headerFilterStrategy": return 
org.apache.camel.spi.HeaderFilterStrategy.class;
         case "lazystartproducer":
         case "lazyStartProducer": return boolean.class;
         case "nolocal":
@@ -95,6 +99,8 @@ public class JcrEndpointConfigurer extends 
PropertyConfigurerSupport implements
         case "exceptionHandler": return target.getExceptionHandler();
         case "exchangepattern":
         case "exchangePattern": return target.getExchangePattern();
+        case "headerfilterstrategy":
+        case "headerFilterStrategy": return target.getHeaderFilterStrategy();
         case "lazystartproducer":
         case "lazyStartProducer": return target.isLazyStartProducer();
         case "nolocal":
diff --git 
a/components/camel-jcr/src/generated/java/org/apache/camel/component/jcr/JcrEndpointUriFactory.java
 
b/components/camel-jcr/src/generated/java/org/apache/camel/component/jcr/JcrEndpointUriFactory.java
index d22a323c87e8..86627e32b164 100644
--- 
a/components/camel-jcr/src/generated/java/org/apache/camel/component/jcr/JcrEndpointUriFactory.java
+++ 
b/components/camel-jcr/src/generated/java/org/apache/camel/component/jcr/JcrEndpointUriFactory.java
@@ -24,13 +24,14 @@ public class JcrEndpointUriFactory extends 
org.apache.camel.support.component.En
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(16);
+        Set<String> props = new HashSet<>(17);
         props.add("base");
         props.add("bridgeErrorHandler");
         props.add("deep");
         props.add("eventTypes");
         props.add("exceptionHandler");
         props.add("exchangePattern");
+        props.add("headerFilterStrategy");
         props.add("host");
         props.add("lazyStartProducer");
         props.add("noLocal");
diff --git 
a/components/camel-jcr/src/generated/resources/META-INF/org/apache/camel/component/jcr/jcr.json
 
b/components/camel-jcr/src/generated/resources/META-INF/org/apache/camel/component/jcr/jcr.json
index 497abb9f97ba..3796e983a64c 100644
--- 
a/components/camel-jcr/src/generated/resources/META-INF/org/apache/camel/component/jcr/jcr.json
+++ 
b/components/camel-jcr/src/generated/resources/META-INF/org/apache/camel/component/jcr/jcr.json
@@ -50,6 +50,7 @@
     "exceptionHandler": { "index": 12, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By de [...]
     "exchangePattern": { "index": 13, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "enum", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
     "lazyStartProducer": { "index": 14, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
-    "password": { "index": 15, "kind": "parameter", "displayName": "Password", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": true, "security": "secret", "description": "Password for login" }
+    "headerFilterStrategy": { "index": 15, "kind": "parameter", "displayName": 
"Header Filter Strategy", "group": "filter", "label": "producer,filter", 
"required": false, "type": "object", "javaType": 
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": 
false, "secret": false, "description": "To use a custom 
org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel 
message." },
+    "password": { "index": 16, "kind": "parameter", "displayName": "Password", 
"group": "security", "label": "security", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": true, "security": "secret", "description": "Password for login" }
   }
 }
diff --git 
a/components/camel-jcr/src/main/java/org/apache/camel/component/jcr/JcrEndpoint.java
 
b/components/camel-jcr/src/main/java/org/apache/camel/component/jcr/JcrEndpoint.java
index 33b61cfedaee..5f2f224f2fc0 100644
--- 
a/components/camel-jcr/src/main/java/org/apache/camel/component/jcr/JcrEndpoint.java
+++ 
b/components/camel-jcr/src/main/java/org/apache/camel/component/jcr/JcrEndpoint.java
@@ -28,11 +28,14 @@ import org.apache.camel.Consumer;
 import org.apache.camel.Processor;
 import org.apache.camel.Producer;
 import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.HeaderFilterStrategy;
+import org.apache.camel.spi.HeaderFilterStrategyAware;
 import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.UriEndpoint;
 import org.apache.camel.spi.UriParam;
 import org.apache.camel.spi.UriPath;
 import org.apache.camel.support.DefaultEndpoint;
+import org.apache.camel.support.DefaultHeaderFilterStrategy;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StringHelper;
 
@@ -42,7 +45,7 @@ import org.apache.camel.util.StringHelper;
 @UriEndpoint(firstVersion = "1.3.0", scheme = "jcr", title = "JCR", syntax = 
"jcr:host/base",
              alternativeSyntax = "jcr:username:password@host/base",
              category = { Category.DATABASE, Category.CMS }, headersClass = 
JcrConstants.class)
-public class JcrEndpoint extends DefaultEndpoint {
+public class JcrEndpoint extends DefaultEndpoint implements 
HeaderFilterStrategyAware {
 
     private Credentials credentials;
     private Repository repository;
@@ -72,6 +75,9 @@ public class JcrEndpoint extends DefaultEndpoint {
     private long sessionLiveCheckInterval = 60000L;
     @UriParam
     private String workspaceName;
+    @UriParam(label = "producer,filter",
+              description = "To use a custom 
org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel 
message.")
+    private HeaderFilterStrategy headerFilterStrategy = new 
DefaultHeaderFilterStrategy();
 
     protected JcrEndpoint(String endpointUri, JcrComponent component) {
         super(endpointUri, component);
@@ -292,6 +298,19 @@ public class JcrEndpoint extends DefaultEndpoint {
         this.workspaceName = workspaceName;
     }
 
+    @Override
+    public HeaderFilterStrategy getHeaderFilterStrategy() {
+        return headerFilterStrategy;
+    }
+
+    /**
+     * To use a custom {@link org.apache.camel.spi.HeaderFilterStrategy} to 
filter header to and from Camel message.
+     */
+    @Override
+    public void setHeaderFilterStrategy(HeaderFilterStrategy 
headerFilterStrategy) {
+        this.headerFilterStrategy = headerFilterStrategy;
+    }
+
     /**
      * Gets the destination name which was configured from the endpoint uri.
      *
diff --git 
a/components/camel-jcr/src/main/java/org/apache/camel/component/jcr/JcrProducer.java
 
b/components/camel-jcr/src/main/java/org/apache/camel/component/jcr/JcrProducer.java
index c7f3c5aa86b8..1ead9b840321 100644
--- 
a/components/camel-jcr/src/main/java/org/apache/camel/component/jcr/JcrProducer.java
+++ 
b/components/camel-jcr/src/main/java/org/apache/camel/component/jcr/JcrProducer.java
@@ -34,6 +34,7 @@ import org.apache.camel.Exchange;
 import org.apache.camel.Message;
 import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.TypeConverter;
+import org.apache.camel.spi.HeaderFilterStrategy;
 import org.apache.camel.support.DefaultProducer;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.jackrabbit.util.Text;
@@ -49,6 +50,7 @@ public class JcrProducer extends DefaultProducer {
         TypeConverter converter = exchange.getContext().getTypeConverter();
         Session session = openSession();
         Message message = exchange.getIn();
+        HeaderFilterStrategy headerFilterStrategy = 
getJcrEndpoint().getHeaderFilterStrategy();
         String operation = determineOperation(message);
         try {
             if (JcrConstants.JCR_INSERT.equals(operation)) {
@@ -57,6 +59,10 @@ public class JcrProducer extends DefaultProducer {
                 Map<String, Object> headers = 
filterComponentHeaders(message.getHeaders());
                 for (String key : headers.keySet()) {
                     Object header = message.getHeader(key);
+                    if (headerFilterStrategy != null
+                            && 
headerFilterStrategy.applyFilterToCamelHeaders(key, header, exchange)) {
+                        continue;
+                    }
                     if (header != null && 
Object[].class.isAssignableFrom(header.getClass())) {
                         Value[] value = converter.convertTo(Value[].class, 
exchange, header);
                         node.setProperty(key, value);
@@ -81,7 +87,11 @@ public class JcrProducer extends DefaultProducer {
                     } else {
                         value = converter.convertTo(aClass, exchange, 
property.getValue());
                     }
-                    message.setHeader(property.getName(), value);
+                    String name = property.getName();
+                    if (headerFilterStrategy == null
+                            || 
!headerFilterStrategy.applyFilterToExternalHeaders(name, value, exchange)) {
+                        message.setHeader(name, value);
+                    }
                 }
             } else {
                 throw new RuntimeCamelException("Unsupported operation: " + 
operation);
@@ -93,6 +103,10 @@ public class JcrProducer extends DefaultProducer {
         }
     }
 
+    // Strips the JCR control keys (operation, node name, node type) 
unconditionally, independent of the configured
+    // HeaderFilterStrategy. The default strategy already filters these (they 
are Camel-prefixed), but a custom
+    // strategy that does not filter Camel* headers must still never persist 
these operational headers as node
+    // properties, so this filtering is kept as a separate, unconditional 
guard.
     private Map<String, Object> filterComponentHeaders(Map<String, Object> 
properties) {
         Map<String, Object> result = new HashMap<>(properties.size());
         for (Map.Entry<String, Object> entry : properties.entrySet()) {
diff --git 
a/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrGetNodeByIdHeaderInjectionTest.java
 
b/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrGetNodeByIdHeaderInjectionTest.java
new file mode 100644
index 000000000000..e5802ae60984
--- /dev/null
+++ 
b/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrGetNodeByIdHeaderInjectionTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.jcr;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The JCR producer's {@code CamelJcrGetById} operation maps the properties of 
the retrieved node into Exchange headers.
+ * A content author must not be able to use a property named after a Camel 
internal header (in any casing) to inject
+ * that header, as those steer downstream processing (HTTP target URI, bean 
method dispatch, file names, and so on).
+ * Ordinary document properties must still be mapped.
+ */
+public class JcrGetNodeByIdHeaderInjectionTest extends JcrRouteTestSupport {
+
+    private static final String CONTENT = "content is here";
+
+    private static final String[] CAMEL_HEADER_VARIANTS = {
+            "CamelHttpUri", "camelHttpUri", "caMELHttpUri", "CAMELHTTPURI" };
+
+    @EndpointInject("mock:result")
+    private MockEndpoint result;
+
+    private String identifier;
+
+    @Override
+    public void doPreSetup() throws RepositoryException {
+        Session session = openSession();
+        Node node = 
session.getRootNode().addNode("injectionRoot").addNode("test");
+        node.setProperty("my.contents.property", CONTENT);
+        for (String variant : CAMEL_HEADER_VARIANTS) {
+            node.setProperty(variant, "malicious");
+        }
+        identifier = node.getIdentifier();
+
+        session.save();
+        session.logout();
+    }
+
+    @Test
+    public void camelHeadersInNodePropertiesAreFilteredRegardlessOfCase() 
throws Exception {
+        result.expectedMessageCount(1);
+
+        Exchange exchange = createExchangeWithBody(identifier);
+        template.send("direct:a", exchange);
+        MockEndpoint.assertIsSatisfied(context);
+
+        Message in = result.getReceivedExchanges().get(0).getIn();
+        for (String variant : CAMEL_HEADER_VARIANTS) {
+            // the Camel header map is case-insensitive, so this lookup also 
catches the other spellings
+            assertNull(in.getHeader(variant),
+                    "a Camel internal header must not be injectable through a 
JCR node property: " + variant);
+        }
+        assertEquals(CONTENT, in.getHeader("my.contents.property", 
String.class),
+                "an ordinary document property must still be mapped to a 
header");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:a")
+                        .setHeader(JcrConstants.JCR_OPERATION, 
constant(JcrConstants.JCR_GET_BY_ID))
+                        .to("jcr://user:pass@repository")
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrInsertHeaderInjectionTest.java
 
b/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrInsertHeaderInjectionTest.java
new file mode 100644
index 000000000000..628a5c124688
--- /dev/null
+++ 
b/components/camel-jcr/src/test/java/org/apache/camel/component/jcr/JcrInsertHeaderInjectionTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.jcr;
+
+import javax.jcr.Node;
+import javax.jcr.Session;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The JCR producer's insert operation persists message headers as node 
properties. A Camel internal header carried on
+ * the message (in any casing) must not be persisted as a node property, as 
those are operational headers rather than
+ * document content; ordinary headers must still be stored.
+ */
+public class JcrInsertHeaderInjectionTest extends JcrRouteTestSupport {
+
+    private static final String CONTENT = "content is here";
+
+    private static final String[] CAMEL_HEADER_VARIANTS = {
+            "CamelHttpUri", "camelHttpUri", "caMELHttpUri", "CAMELHTTPURI" };
+
+    @Test
+    public void camelHeadersAreNotPersistedAsNodeProperties() throws Exception 
{
+        Exchange exchange = createExchangeWithBody("");
+        exchange.getIn().setHeader(JcrConstants.JCR_NODE_NAME, "node");
+        exchange.getIn().setHeader("my.contents.property", CONTENT);
+        for (String variant : CAMEL_HEADER_VARIANTS) {
+            exchange.getIn().setHeader(variant, "malicious");
+        }
+
+        Exchange out = template.send("direct:a", exchange);
+        String identifier = out.getMessage().getBody(String.class);
+
+        Session session = openSession();
+        try {
+            Node node = session.getNodeByIdentifier(identifier);
+            assertTrue(node.hasProperty("my.contents.property"),
+                    "an ordinary header must still be stored as a node 
property");
+            assertEquals(CONTENT, 
node.getProperty("my.contents.property").getString());
+            for (String variant : CAMEL_HEADER_VARIANTS) {
+                assertFalse(node.hasProperty(variant),
+                        "a Camel internal header must not be persisted as a 
node property: " + variant);
+            }
+        } finally {
+            session.logout();
+        }
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:a").to("jcr://user:pass@repository/home/test");
+            }
+        };
+    }
+}
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 1edcbea72f23..7afd41e043c9 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
@@ -3097,6 +3097,20 @@ Due to the package rename from `com.bazaarvoice.jolt` to 
`io.joltcommunity.jolt`
 `io.joltcommunity.jolt.ContextualTransform`.
 Users referencing `Removr` directly also need to update their import to 
`io.joltcommunity.jolt.removr.Removr`.
 
+=== camel-jcr - internal Camel headers are now filtered when mapping node 
properties
+
+The `jcr` producer now applies a `HeaderFilterStrategy` when converting 
between JCR node properties and
+Camel message headers. With the default strategy, header names starting with 
`Camel` or `camel`
+(case-insensitively) are no longer copied in either direction:
+
+* the `CamelJcrGetById` operation no longer maps a node property whose name is 
a Camel internal header
+(for example `CamelHttpUri`) onto the message;
+* the insert operation no longer persists Camel internal headers carried on 
the message as node properties.
+
+Ordinary document properties are unaffected and continue to be mapped as 
before. A route that relies on the
+previous behaviour can supply a custom `headerFilterStrategy` on the `jcr` 
endpoint to opt out of, or narrow,
+the filtering.
+
 === camel-mustache, camel-chunk - potential breaking change
 
 The Exchange header constants in `MustacheConstants` and `ChunkConstants` have 
been renamed to
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JcrEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JcrEndpointBuilderFactory.java
index 5429930402e1..3228b6409a56 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JcrEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/JcrEndpointBuilderFactory.java
@@ -643,6 +643,38 @@ public interface JcrEndpointBuilderFactory {
             doSetProperty("workspaceName", workspaceName);
             return this;
         }
+        /**
+         * To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter
+         * header to and from Camel message.
+         * 
+         * The option is a:
+         * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
+         * 
+         * Group: filter
+         * 
+         * @param headerFilterStrategy the value to set
+         * @return the dsl builder
+         */
+        default JcrEndpointProducerBuilder 
headerFilterStrategy(org.apache.camel.spi.HeaderFilterStrategy 
headerFilterStrategy) {
+            doSetProperty("headerFilterStrategy", headerFilterStrategy);
+            return this;
+        }
+        /**
+         * To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter
+         * header to and from Camel message.
+         * 
+         * The option will be converted to a
+         * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
+         * 
+         * Group: filter
+         * 
+         * @param headerFilterStrategy the value to set
+         * @return the dsl builder
+         */
+        default JcrEndpointProducerBuilder headerFilterStrategy(String 
headerFilterStrategy) {
+            doSetProperty("headerFilterStrategy", headerFilterStrategy);
+            return this;
+        }
         /**
          * Password for login.
          * 

Reply via email to