This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch camel-4.14.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.14.x by this push:
new 7881d949c40b [backport camel-4.14.x] CAMEL-23528: validate property
names when building MATCH/DELETE WHERE clause (#23323)
7881d949c40b is described below
commit 7881d949c40befcc602016dcce25a2fb38d070ce
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue May 19 13:45:26 2026 +0200
[backport camel-4.14.x] CAMEL-23528: validate property names when building
MATCH/DELETE WHERE clause (#23323)
* CAMEL-23528: validate property names when building MATCH/DELETE WHERE
clause
Validate Neo4j property names used to build the MATCH/DELETE WHERE clause
in Neo4jProducer, rejecting invalid names with a clear error instead of
producing a malformed query. Adds a unit test.
Closes #23258
* CAMEL-23528: document camel-neo4j property name validation in the 4.14
upgrade guide
Signed-off-by: Andrea Cosentino <[email protected]>
---------
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../camel/component/neo4j/Neo4jProducer.java | 26 ++++++++++++
.../neo4j/Neo4jPropertyNameValidationTest.java | 46 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_14.adoc | 10 +++++
3 files changed, 82 insertions(+)
diff --git
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
index d501c2079259..560efafdbe2f 100644
---
a/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
+++
b/components/camel-ai/camel-neo4j/src/main/java/org/apache/camel/component/neo4j/Neo4jProducer.java
@@ -19,6 +19,7 @@ package org.apache.camel.component.neo4j;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -56,6 +57,10 @@ public class Neo4jProducer extends DefaultProducer {
private static final TypeReference<Map<String, Object>> MAP_TYPE_REF = new
TypeReference<>() {
};
+ // Only property values are passed as bound parameters; the property name
is spliced into the
+ // Cypher query text, so it must be restricted to a safe identifier
pattern.
+ private static final Pattern VALID_PROPERTY_NAME =
Pattern.compile("^[A-Za-z_][A-Za-z0-9_]*$");
+
private Driver driver;
public Neo4jProducer(Neo4jEndpoint endpoint) {
@@ -134,6 +139,19 @@ public class Neo4jProducer extends DefaultProducer {
executeWriteQuery(exchange, query, properties, databaseName,
Neo4Operation.CREATE_NODE);
}
+ /**
+ * Validates a Neo4j property name before it is spliced into a Cypher
query. Property values are passed as bound
+ * parameters, but the property name is inserted into the query text, so
it must be a safe identifier. Names that do
+ * not match are rejected with a clear error instead of producing a
malformed or unintended query.
+ */
+ static void validatePropertyName(String name) {
+ if (name == null || !VALID_PROPERTY_NAME.matcher(name).matches()) {
+ throw new IllegalArgumentException(
+ "Invalid Neo4j property name: '" + name + "'. Property
names must match "
+ +
VALID_PROPERTY_NAME.pattern());
+ }
+ }
+
private void retrieveNodes(Exchange exchange) throws NoSuchHeaderException
{
final String label = getEndpoint().getConfiguration().getLabel();
ObjectHelper.notNull(label, "label");
@@ -165,6 +183,7 @@ public class Neo4jProducer extends DefaultProducer {
if (paramIndex > 0) {
whereClause.append(" AND ");
}
+ validatePropertyName(entry.getKey());
String paramName = "param" + paramIndex;
whereClause.append(alias).append(".").append(entry.getKey())
.append(" = $").append(paramName);
@@ -178,6 +197,9 @@ public class Neo4jProducer extends DefaultProducer {
// Empty map, match all nodes
query = String.format("MATCH (%s:%s) RETURN %s", alias,
label, alias);
}
+ } catch (IllegalArgumentException iae) {
+ exchange.setException(new
Neo4jOperationException(RETRIEVE_NODES, iae));
+ return;
} catch (Exception e) {
exchange.setException(
new Neo4jOperationException(
@@ -263,6 +285,7 @@ public class Neo4jProducer extends DefaultProducer {
if (paramIndex > 0) {
whereClause.append(" AND ");
}
+ validatePropertyName(entry.getKey());
String paramName = "param" + paramIndex;
whereClause.append(alias).append(".").append(entry.getKey())
.append(" = $").append(paramName);
@@ -276,6 +299,9 @@ public class Neo4jProducer extends DefaultProducer {
// Empty map, delete all nodes of this label
query = String.format("MATCH (%s:%s) %s DELETE %s", alias,
label, detached, alias);
}
+ } catch (IllegalArgumentException iae) {
+ exchange.setException(new
Neo4jOperationException(Neo4Operation.DELETE_NODE, iae));
+ return;
} catch (Exception e) {
exchange.setException(
new Neo4jOperationException(
diff --git
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/Neo4jPropertyNameValidationTest.java
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/Neo4jPropertyNameValidationTest.java
new file mode 100644
index 000000000000..f4dff68e931d
--- /dev/null
+++
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/Neo4jPropertyNameValidationTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.neo4j;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class Neo4jPropertyNameValidationTest {
+
+ @Test
+ void acceptsValidPropertyNames() {
+ assertDoesNotThrow(() -> Neo4jProducer.validatePropertyName("name"));
+ assertDoesNotThrow(() ->
Neo4jProducer.validatePropertyName("_internal"));
+ assertDoesNotThrow(() ->
Neo4jProducer.validatePropertyName("firstName2"));
+ assertDoesNotThrow(() -> Neo4jProducer.validatePropertyName("A_B_1"));
+ }
+
+ @Test
+ void rejectsInvalidPropertyNames() {
+ assertThrows(IllegalArgumentException.class, () ->
Neo4jProducer.validatePropertyName(null));
+ assertThrows(IllegalArgumentException.class, () ->
Neo4jProducer.validatePropertyName(""));
+ assertThrows(IllegalArgumentException.class, () ->
Neo4jProducer.validatePropertyName("first name"));
+ assertThrows(IllegalArgumentException.class, () ->
Neo4jProducer.validatePropertyName("name-1"));
+ assertThrows(IllegalArgumentException.class, () ->
Neo4jProducer.validatePropertyName("name.sub"));
+ assertThrows(IllegalArgumentException.class, () ->
Neo4jProducer.validatePropertyName("1name"));
+ // A property name that would otherwise change the structure of the
generated query.
+ assertThrows(IllegalArgumentException.class,
+ () -> Neo4jProducer.validatePropertyName("x) RETURN n UNION
MATCH (m) RETURN m //"));
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_14.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_14.adoc
index 1d1f946ae5b2..a8e84e7196a0 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_14.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_14.adoc
@@ -88,6 +88,16 @@ Hazelcast topic, queue, map, list, set, or in one of the
repositories
above must provide their own `Config` with a
`JavaSerializationFilterConfig` configured for their class names.
+=== camel-neo4j
+
+When using the `RETRIEVE_NODES` or `DELETE_NODE` operations with the
`CamelNeo4jMatchProperties`
+header, the property names provided in the JSON match map are now validated
before the `MATCH` /
+`DELETE` `WHERE` clause is built. Property names must be valid identifiers
matching
+`[A-Za-z_][A-Za-z0-9_]*`. A request whose match map contains a property name
that does not match
+this pattern now fails fast with an `IllegalArgumentException` (wrapped in a
+`Neo4jOperationException`) instead of producing a malformed query. Property
values continue to be
+passed as bound query parameters and are unaffected.
+
== Upgrading from 4.14.5 to 4.14.6
=== camel-platform-http-main