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

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


The following commit(s) were added to refs/heads/camel-4.10.x by this push:
     new 723e2cd98ce4 CAMEL-22719 - camel-neo4j - Improve detection of message 
body (#20037)
723e2cd98ce4 is described below

commit 723e2cd98ce4b4ceb1dd38837bc113fca0cef170
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Nov 24 11:19:59 2025 +0100

    CAMEL-22719 - camel-neo4j - Improve detection of message body (#20037)
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 components/camel-ai/camel-neo4j/pom.xml            |   4 +
 .../camel/component/neo4j/Neo4jProducer.java       | 131 +++++++++++++++++----
 .../camel/component/neo4j/it/Neo4jNodeIT.java      |  18 +--
 3 files changed, 124 insertions(+), 29 deletions(-)

diff --git a/components/camel-ai/camel-neo4j/pom.xml 
b/components/camel-ai/camel-neo4j/pom.xml
index 2914de8d6407..137d0190e74c 100644
--- a/components/camel-ai/camel-neo4j/pom.xml
+++ b/components/camel-ai/camel-neo4j/pom.xml
@@ -47,6 +47,10 @@
       <groupId>org.apache.camel</groupId>
       <artifactId>camel-support</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-jackson</artifactId>
+    </dependency>
     <dependency>
       <groupId>dev.langchain4j</groupId>
       <artifactId>langchain4j-core</artifactId>
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 c1ac6cb0a871..0e2f0995174c 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
@@ -21,6 +21,8 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.camel.Exchange;
 import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.Message;
@@ -50,6 +52,10 @@ import static 
org.apache.camel.component.neo4j.Neo4jConstants.Headers.QUERY_RETR
 
 public class Neo4jProducer extends DefaultProducer {
 
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+    private static final TypeReference<Map<String, Object>> MAP_TYPE_REF = new 
TypeReference<>() {
+    };
+
     private Driver driver;
 
     public Neo4jProducer(Neo4jEndpoint endpoint) {
@@ -104,15 +110,24 @@ public class Neo4jProducer extends DefaultProducer {
 
         final String databaseName = getEndpoint().getName();
 
-        var query = "";
-        Map<String, Object> properties = null;
+        // Always use parameterized queries to prevent Cypher injection
+        var query = String.format("CREATE (%s:%s $props)", alias, label);
+        Map<String, Object> properties;
 
         if (body instanceof String) {
-            // Case we get the object in a Json format
-            query = String.format("CREATE (%s:%s %s)", alias, label, body);
+            try {
+                // Convert JSON string to Map for parameterized query
+                Map<String, Object> bodyMap = OBJECT_MAPPER.readValue((String) 
body, MAP_TYPE_REF);
+                properties = Map.of("props", bodyMap);
+            } catch (Exception e) {
+                exchange.setException(
+                        new Neo4jOperationException(
+                                Neo4Operation.CREATE_NODE,
+                                new IllegalArgumentException("Failed to parse 
body as JSON: " + body, e)));
+                return;
+            }
         } else {
-            // body should be a list of properties
-            query = String.format("CREATE (%s:%s $props)", alias, label);
+            // body should be a Map or similar object
             properties = Map.of("props", body);
         }
 
@@ -126,17 +141,55 @@ public class Neo4jProducer extends DefaultProducer {
         final String alias = getEndpoint().getConfiguration().getAlias();
         ObjectHelper.notNull(alias, "alias");
 
-        String matchQuery = exchange.getMessage().getHeader(MATCH_PROPERTIES, 
String.class);
-        // in this case we search all nodes
-        if (matchQuery == null) {
-            matchQuery = "";
-        }
+        String matchProperties = 
exchange.getMessage().getHeader(MATCH_PROPERTIES, String.class);
 
         final String databaseName = getEndpoint().getName();
 
-        var query = String.format("MATCH (%s:%s %s) RETURN %s", alias, label, 
matchQuery, alias);
+        String query;
+        Map<String, Object> queryParams = null;
+
+        if (matchProperties == null || matchProperties.isEmpty()) {
+            // Search all nodes
+            query = String.format("MATCH (%s:%s) RETURN %s", alias, label, 
alias);
+        } else {
+            try {
+                // Convert JSON string to Map and build WHERE clause with 
parameters
+                Map<String, Object> matchMap = 
OBJECT_MAPPER.readValue(matchProperties, MAP_TYPE_REF);
+
+                if (!matchMap.isEmpty()) {
+                    StringBuilder whereClause = new StringBuilder();
+                    queryParams = new java.util.HashMap<>();
+                    int paramIndex = 0;
+
+                    for (Map.Entry<String, Object> entry : 
matchMap.entrySet()) {
+                        if (paramIndex > 0) {
+                            whereClause.append(" AND ");
+                        }
+                        String paramName = "param" + paramIndex;
+                        
whereClause.append(alias).append(".").append(entry.getKey())
+                                .append(" = $").append(paramName);
+                        queryParams.put(paramName, entry.getValue());
+                        paramIndex++;
+                    }
+
+                    query = String.format("MATCH (%s:%s) WHERE %s RETURN %s",
+                            alias, label, whereClause.toString(), alias);
+                } else {
+                    // Empty map, match all nodes
+                    query = String.format("MATCH (%s:%s) RETURN %s", alias, 
label, alias);
+                }
+            } catch (Exception e) {
+                exchange.setException(
+                        new Neo4jOperationException(
+                                RETRIEVE_NODES,
+                                new IllegalArgumentException(
+                                        "Failed to parse MATCH_PROPERTIES as 
JSON: " + matchProperties,
+                                        e)));
+                return;
+            }
+        }
 
-        queryRetriveNodes(exchange, databaseName, null, query, RETRIEVE_NODES);
+        queryRetriveNodes(exchange, databaseName, queryParams, query, 
RETRIEVE_NODES);
     }
 
     private void retrieveNodesWithCypherQuery(Exchange exchange) throws 
NoSuchHeaderException {
@@ -184,19 +237,57 @@ public class Neo4jProducer extends DefaultProducer {
         final String alias = getEndpoint().getConfiguration().getAlias();
         ObjectHelper.notNull(alias, "alias");
 
-        String matchQuery = exchange.getMessage().getHeader(MATCH_PROPERTIES, 
String.class);
-        // in this case we search all nodes
-        if (matchQuery == null) {
-            matchQuery = "";
-        }
+        String matchProperties = 
exchange.getMessage().getHeader(MATCH_PROPERTIES, String.class);
 
         final String databaseName = getEndpoint().getName();
 
         final String detached = 
getEndpoint().getConfiguration().isDetachRelationship() ? "DETACH" : "";
 
-        var query = String.format("MATCH (%s:%s %s) %s DELETE %s", alias, 
label, matchQuery, detached, alias);
+        String query;
+        Map<String, Object> queryParams = null;
+
+        if (matchProperties == null || matchProperties.isEmpty()) {
+            // Delete all nodes of this label
+            query = String.format("MATCH (%s:%s) %s DELETE %s", alias, label, 
detached, alias);
+        } else {
+            try {
+                // Convert JSON string to Map and build WHERE clause with 
parameters
+                Map<String, Object> matchMap = 
OBJECT_MAPPER.readValue(matchProperties, MAP_TYPE_REF);
+
+                if (!matchMap.isEmpty()) {
+                    StringBuilder whereClause = new StringBuilder();
+                    queryParams = new java.util.HashMap<>();
+                    int paramIndex = 0;
+
+                    for (Map.Entry<String, Object> entry : 
matchMap.entrySet()) {
+                        if (paramIndex > 0) {
+                            whereClause.append(" AND ");
+                        }
+                        String paramName = "param" + paramIndex;
+                        
whereClause.append(alias).append(".").append(entry.getKey())
+                                .append(" = $").append(paramName);
+                        queryParams.put(paramName, entry.getValue());
+                        paramIndex++;
+                    }
+
+                    query = String.format("MATCH (%s:%s) WHERE %s %s DELETE 
%s",
+                            alias, label, whereClause.toString(), detached, 
alias);
+                } else {
+                    // Empty map, delete all nodes of this label
+                    query = String.format("MATCH (%s:%s) %s DELETE %s", alias, 
label, detached, alias);
+                }
+            } catch (Exception e) {
+                exchange.setException(
+                        new Neo4jOperationException(
+                                Neo4Operation.DELETE_NODE,
+                                new IllegalArgumentException(
+                                        "Failed to parse MATCH_PROPERTIES as 
JSON: " + matchProperties,
+                                        e)));
+                return;
+            }
+        }
 
-        executeWriteQuery(exchange, query, null, databaseName, 
Neo4Operation.DELETE_NODE);
+        executeWriteQuery(exchange, query, queryParams, databaseName, 
Neo4Operation.DELETE_NODE);
     }
 
     private void createVectorIndex(Exchange exchange) {
diff --git 
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jNodeIT.java
 
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jNodeIT.java
index 45aa62fb8a22..3f29848a2dd0 100644
--- 
a/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jNodeIT.java
+++ 
b/components/camel-ai/camel-neo4j/src/test/java/org/apache/camel/component/neo4j/it/Neo4jNodeIT.java
@@ -41,8 +41,8 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
     @Order(0)
     void createNodeWithJsonObject() {
 
-        var body = "{name: 'Alice', email: '[email protected]', age: 30}";
-        var expectedCypherQuery = "CREATE (u1:User {name: 'Alice', email: 
'[email protected]', age: 30})";
+        var body = "{\"name\": \"Alice\", \"email\": \"[email protected]\", 
\"age\": 30}";
+        var expectedCypherQuery = "CREATE (u1:User $props)";
 
         Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u1&label=User")
                 .withBodyAs(body, String.class)
@@ -141,7 +141,7 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
     void testRetrieveNode() {
         Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
                 .withHeader(Neo4jConstants.Headers.OPERATION, 
Neo4Operation.RETRIEVE_NODES)
-                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 
'Alice'}")
+                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, 
"{\"name\": \"Alice\"}")
                 .request(Exchange.class);
 
         assertNotNull(result);
@@ -193,7 +193,7 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
         // delete node
         Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
                 .withHeader(Neo4jConstants.Headers.OPERATION, 
Neo4Operation.DELETE_NODE)
-                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 
'Alice'}")
+                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, 
"{\"name\": \"Alice\"}")
                 .request(Exchange.class);
 
         assertNotNull(result);
@@ -215,7 +215,7 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
 
         result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
                 .withHeader(Neo4jConstants.Headers.OPERATION, 
Neo4Operation.RETRIEVE_NODES)
-                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 
'Alice'}")
+                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, 
"{\"name\": \"Alice\"}")
                 .request(Exchange.class);
 
         assertNotNull(result);
@@ -233,7 +233,7 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
         // try to delete user named Diana and this should fail as Diana has a 
relationship with Ethan
         Exchange result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
                 .withHeader(Neo4jConstants.Headers.OPERATION, 
Neo4Operation.DELETE_NODE)
-                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 
'Diana'}")
+                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, 
"{\"name\": \"Diana\"}")
                 .request(Exchange.class);
 
         assertNotNull(result);
@@ -245,7 +245,7 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
         // delete the Diana by detaching its relationship with Ethan - 
detachRelationship=true
         result = 
fluentTemplate.to("neo4j:neo4j?alias=u&label=User&detachRelationship=true")
                 .withHeader(Neo4jConstants.Headers.OPERATION, 
Neo4Operation.DELETE_NODE)
-                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 
'Diana'}")
+                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, 
"{\"name\": \"Diana\"}")
                 .request(Exchange.class);
         assertNotNull(result);
         assertNull("No exception anymore when deleting relationship at same 
time", result.getException());
@@ -269,7 +269,7 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
 
         result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
                 .withHeader(Neo4jConstants.Headers.OPERATION, 
Neo4Operation.RETRIEVE_NODES)
-                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 
'Diana'}")
+                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, 
"{\"name\": \"Diana\"}")
                 .request(Exchange.class);
 
         assertNotNull(result);
@@ -311,7 +311,7 @@ public class Neo4jNodeIT extends Neo4jTestSupport {
 
         result = fluentTemplate.to("neo4j:neo4j?alias=u&label=User")
                 .withHeader(Neo4jConstants.Headers.OPERATION, 
Neo4Operation.RETRIEVE_NODES)
-                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, "{name: 
'Bob'}")
+                .withHeader(Neo4jConstants.Headers.MATCH_PROPERTIES, 
"{\"name\": \"Bob\"}")
                 .request(Exchange.class);
 
         assertNotNull(result);

Reply via email to