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

coheigea pushed a commit to branch coheigea/fiql-sql
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 44075e935adb55cace9daeba2a0341091dbbe1aa
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Fri May 8 13:10:35 2026 +0100

    Validate SQL expressions after FIQL conversion
---
 .../jaxrs/ext/search/sql/SQLPrinterVisitor.java    | 36 ++++++++++++++++++++++
 .../ext/search/sql/SQLHierarchicalQueryTest.java   |  8 +++++
 .../ext/search/sql/SQLPrinterVisitorTest.java      | 23 ++++++++++++++
 3 files changed, 67 insertions(+)

diff --git 
a/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/sql/SQLPrinterVisitor.java
 
b/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/sql/SQLPrinterVisitor.java
index 77ef992043b..04c1c358ae5 100644
--- 
a/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/sql/SQLPrinterVisitor.java
+++ 
b/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/sql/SQLPrinterVisitor.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 import org.apache.cxf.jaxrs.ext.search.PrimitiveStatement;
 import org.apache.cxf.jaxrs.ext.search.SearchCondition;
@@ -32,6 +33,10 @@ import 
org.apache.cxf.jaxrs.ext.search.visitor.AbstractUntypedSearchConditionVis
 
 public class SQLPrinterVisitor<T> extends 
AbstractUntypedSearchConditionVisitor<T, String> {
 
+    private static final Pattern SQL_IDENTIFIER_PATTERN = 
Pattern.compile("[A-Za-z_][A-Za-z0-9_]*");
+    private static final Pattern SQL_QUALIFIED_IDENTIFIER_PATTERN =
+        Pattern.compile("[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*");
+
     private String primaryTable;
     private String tableAlias;
     private List<String> columns;
@@ -61,6 +66,7 @@ public class SQLPrinterVisitor<T> extends 
AbstractUntypedSearchConditionVisitor<
         this.columns = columns;
         this.primaryTable = table;
         this.tableAlias = tableAlias;
+        validateConfiguredSqlIdentifiers();
         prepareTopStringBuilder();
     }
 
@@ -81,6 +87,9 @@ public class SQLPrinterVisitor<T> extends 
AbstractUntypedSearchConditionVisitor<
                     }
                     joinDone = true;
                     String joinTable = getRealPropertyName(properties[0]);
+                    if (!isValidSqlIdentifier(joinTable)) {
+                        throw new SearchParseException("Invalid SQL JOIN table 
identifier: " + joinTable);
+                    }
                     // Joining key can be pre-configured
                     String joiningKey = primaryTable;
                     if (joiningKey.endsWith("s")) {
@@ -96,6 +105,9 @@ public class SQLPrinterVisitor<T> extends 
AbstractUntypedSearchConditionVisitor<
                 }
 
                 String name = getRealPropertyName(property);
+                if (!isValidSqlQualifiedIdentifier(name)) {
+                    throw new SearchParseException("Invalid SQL property 
identifier: " + name);
+                }
                 String originalValue = getPropertyValue(name, 
statement.getValue());
                 validatePropertyValue(name, originalValue);
 
@@ -151,4 +163,28 @@ public class SQLPrinterVisitor<T> extends 
AbstractUntypedSearchConditionVisitor<
         }
     }
 
+    private void validateConfiguredSqlIdentifiers() {
+        if (primaryTable != null && !isValidSqlIdentifier(primaryTable)) {
+            throw new IllegalArgumentException("Invalid SQL primary table 
identifier: " + primaryTable);
+        }
+        if (tableAlias != null && !isValidSqlIdentifier(tableAlias)) {
+            throw new IllegalArgumentException("Invalid SQL table alias 
identifier: " + tableAlias);
+        }
+        if (columns != null) {
+            for (String column : columns) {
+                if (!"*".equals(column) && 
!isValidSqlQualifiedIdentifier(column)) {
+                    throw new IllegalArgumentException("Invalid SQL column 
identifier: " + column);
+                }
+            }
+        }
+    }
+
+    private static boolean isValidSqlIdentifier(String identifier) {
+        return identifier != null && 
SQL_IDENTIFIER_PATTERN.matcher(identifier).matches();
+    }
+
+    private static boolean isValidSqlQualifiedIdentifier(String identifier) {
+        return identifier != null && 
SQL_QUALIFIED_IDENTIFIER_PATTERN.matcher(identifier).matches();
+    }
+
 }
diff --git 
a/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/sql/SQLHierarchicalQueryTest.java
 
b/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/sql/SQLHierarchicalQueryTest.java
index 737140e6e02..9f90b8b5edc 100644
--- 
a/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/sql/SQLHierarchicalQueryTest.java
+++ 
b/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/sql/SQLHierarchicalQueryTest.java
@@ -56,6 +56,14 @@ public class SQLHierarchicalQueryTest {
                      + " WHERE (name = 'Epson') AND (cartridges.colour = 
'blue')",
                      sql);
     }
+
+    @Test(expected = SearchParseException.class)
+    public void testJoinTableNameSqlInjectionRejected() throws 
SearchParseException {
+        FiqlParser<SearchBean> parser = new FiqlParser<>(SearchBean.class);
+        SearchCondition<SearchBean> filter = 
parser.parse("users--.name==admin");
+        SQLPrinterVisitor<SearchBean> visitor = new 
SQLPrinterVisitor<>("accounts");
+        filter.accept(visitor.visitor());
+    }
     
     @Test(expected = SearchParseException.class)
     public void testLongHierarchicalQuery() {
diff --git 
a/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/sql/SQLPrinterVisitorTest.java
 
b/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/sql/SQLPrinterVisitorTest.java
index 50b75a8fb2d..2e723d15973 100644
--- 
a/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/sql/SQLPrinterVisitorTest.java
+++ 
b/rt/rs/extensions/search/src/test/java/org/apache/cxf/jaxrs/ext/search/sql/SQLPrinterVisitorTest.java
@@ -164,6 +164,29 @@ public class SQLPrinterVisitorTest {
         assertEquals("SELECT NAMES FROM table WHERE NAMES = 'test'", sql);
     }
 
+    @Test(expected = SearchParseException.class)
+    public void testInvalidSqlPropertyIdentifierRejected() throws 
SearchParseException {
+        FiqlParser<SearchBean> beanParser = new FiqlParser<>(SearchBean.class);
+        SearchCondition<SearchBean> filter = beanParser.parse("na-me==test");
+        SQLPrinterVisitor<SearchBean> visitor = new 
SQLPrinterVisitor<>("table");
+        filter.accept(visitor);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidPrimaryTableIdentifierRejected() {
+        new SQLPrinterVisitor<SearchBean>(null, "table--", null, 
Collections.emptyList());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidTableAliasIdentifierRejected() {
+        new SQLPrinterVisitor<SearchBean>(null, "table", "t--", 
Collections.emptyList());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidColumnIdentifierRejected() {
+        new SQLPrinterVisitor<SearchBean>(null, "table", null, 
Collections.singletonList("name--"));
+    }
+
     @Ignore
     public static class Condition {
         private String name;

Reply via email to