VYPR
High severityNVD Advisory· Published Jul 26, 2025· Updated Jul 28, 2025

XWiki Platform's searchDocuments API allows for SQL injection

CVE-2025-54385

Description

XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. In versions between 17.0.0-rc1 to 17.2.2 and versions 16.10.5 and below, it's possible to execute any SQL query in Oracle by using the function like DBMS_XMLGEN or DBMS_XMLQUERY. The XWiki#searchDocuments APIs pass queries directly to Hibernate without sanitization. Even when these APIs enforce a specific SELECT clause, attackers can still inject malicious code through HQL's native function support in other parts of the query (such as the WHERE clause). This is fixed in versions 16.10.6 and 17.3.0-rc-1.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.xwiki.platform:xwiki-platform-oldcoreMaven
>= 1.0, < 16.10.616.10.6
org.xwiki.platform:xwiki-platform-oldcoreMaven
>= 17.0.0-rc1, < 17.3.0-rc-117.3.0-rc-1

Affected products

1

Patches

2
7c4087d44ac5

XWIKI-22728: Improve search query validation

https://github.com/xwiki/xwiki-platformThomas MortagneFeb 27, 2025via ghsa
3 files changed · +137 49
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/XWiki.java+59 9 modified
    @@ -44,6 +44,7 @@
     import org.xwiki.model.reference.PageReference;
     import org.xwiki.model.reference.SpaceReference;
     import org.xwiki.model.reference.WikiReference;
    +import org.xwiki.query.hql.internal.HQLStatementValidator;
     import org.xwiki.rendering.renderer.PrintRendererFactory;
     import org.xwiki.rendering.syntax.Syntax;
     import org.xwiki.security.authorization.AuthorizationException;
    @@ -58,6 +59,7 @@
     import com.xpn.xwiki.doc.XWikiDocument;
     import com.xpn.xwiki.internal.XWikiInitializerJob;
     import com.xpn.xwiki.internal.XWikiInitializerJobStatus;
    +import com.xpn.xwiki.internal.store.hibernate.query.HqlQueryUtils;
     import com.xpn.xwiki.objects.meta.MetaClass;
     import com.xpn.xwiki.user.api.XWikiUser;
     import com.xpn.xwiki.util.Programming;
    @@ -105,6 +107,8 @@ public class XWiki extends Api
     
         private ContextualAuthorizationManager contextualAuthorizationManager;
     
    +    private HQLStatementValidator hqlValidator;
    +
         /**
          * XWiki API Constructor
          *
    @@ -167,6 +171,15 @@ private DocumentRevisionProvider getDocumentRevisionProvider()
             return this.documentRevisionProvider;
         }
     
    +    private HQLStatementValidator getHQLStatementValidator()
    +    {
    +        if (this.hqlValidator == null) {
    +            this.hqlValidator = Utils.getComponent(HQLStatementValidator.class);
    +        }
    +
    +        return this.hqlValidator;
    +    }
    +
         /**
          * Privileged API allowing to access the underlying main XWiki Object
          *
    @@ -710,6 +723,23 @@ public MetaClass getMetaclass()
             return this.xwiki.getMetaclass();
         }
     
    +    private void checkSearchQueryAllowed(String whereSQL) throws XWikiException
    +    {
    +        if (!hasProgrammingRights()) {
    +            try {
    +                if (!getHQLStatementValidator()
    +                    .isSafe(HqlQueryUtils.createLegacySQLQuery("select distinct doc.fullName", whereSQL))) {
    +                    throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
    +                        XWikiException.ERROR_XWIKI_ACCESS_DENIED,
    +                        "The query [" + whereSQL + "] requires programming right");
    +                }
    +            } catch (Exception e) {
    +                throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
    +                    "Failed to validate the query [" + whereSQL + "], requiring programming right", e);
    +            }
    +        }
    +    }
    +
         /**
          * API allowing to search for document names matching a query. Examples:
          * <ul>
    @@ -742,6 +772,8 @@ public MetaClass getMetaclass()
         @Deprecated
         public List<String> searchDocuments(String wheresql) throws XWikiException
         {
    +        checkSearchQueryAllowed(wheresql);
    +
             return this.xwiki.getStore().searchDocumentsNames(wheresql, getXWikiContext());
         }
     
    @@ -760,6 +792,8 @@ public List<String> searchDocuments(String wheresql) throws XWikiException
         @Deprecated
         public List<String> searchDocuments(String wheresql, int nb, int start) throws XWikiException
         {
    +        checkSearchQueryAllowed(wheresql);
    +
             return this.xwiki.getStore().searchDocumentsNames(wheresql, nb, start, getXWikiContext());
         }
     
    @@ -796,6 +830,8 @@ public List<String> searchDocuments(String wheresql, int nb, int start, String s
          */
         public List<Document> searchDocuments(String wheresql, boolean distinctbylocale) throws XWikiException
         {
    +        checkSearchQueryAllowed(wheresql);
    +
             return convert(this.xwiki.getStore().searchDocuments(wheresql, distinctbylocale, getXWikiContext()));
         }
     
    @@ -812,6 +848,8 @@ public List<Document> searchDocuments(String wheresql, boolean distinctbylocale)
         public List<Document> searchDocuments(String wheresql, boolean distinctbylocale, int nb, int start)
             throws XWikiException
         {
    +        checkSearchQueryAllowed(wheresql);
    +
             return convert(this.xwiki.getStore().searchDocuments(wheresql, distinctbylocale, nb, start, getXWikiContext()));
         }
     
    @@ -845,6 +883,8 @@ public List<Document> searchDocuments(String wheresql, boolean distinctbylocale,
         public List<String> searchDocuments(String parameterizedWhereClause, int maxResults, int startOffset,
             List<?> parameterValues) throws XWikiException
         {
    +        checkSearchQueryAllowed(parameterizedWhereClause);
    +
             return this.xwiki.getStore().searchDocumentsNames(parameterizedWhereClause, maxResults, startOffset,
                 parameterValues, getXWikiContext());
         }
    @@ -858,6 +898,8 @@ public List<String> searchDocuments(String parameterizedWhereClause, int maxResu
         @Deprecated
         public List<String> searchDocuments(String parameterizedWhereClause, List<?> parameterValues) throws XWikiException
         {
    +        checkSearchQueryAllowed(parameterizedWhereClause);
    +
             return this.xwiki.getStore().searchDocumentsNames(parameterizedWhereClause, parameterValues, getXWikiContext());
         }
     
    @@ -885,6 +927,8 @@ public List<String> searchDocumentsNames(String wikiName, String parameterizedWh
             try {
                 this.context.setWikiId(wikiName);
     
    +            checkSearchQueryAllowed(parameterizedWhereClause);
    +
                 return searchDocuments(parameterizedWhereClause, maxResults, startOffset, parameterValues);
             } finally {
                 this.context.setWikiId(database);
    @@ -895,18 +939,20 @@ public List<String> searchDocumentsNames(String wikiName, String parameterizedWh
          * Search spaces by passing HQL where clause values as parameters. See
          * {@link #searchDocuments(String, int, int, List)} for more about parameterized hql clauses.
          *
    -     * @param parametrizedSqlClause the HQL where clause. For example
    +     * @param parameterizedSqlClause the HQL where clause. For example
          *            {@code where doc.fullName <> ?1 and (doc.parent = ?2 or (doc.parent = ?3 and doc.space = ?4))}
          * @param nb the number of rows to return. If 0 then all rows are returned
          * @param start the number of rows to skip. If 0 don't skip any row
          * @param parameterValues the where clause values that replace the question marks (?)
          * @return a list of spaces names.
          * @throws XWikiException in case of error while performing the query
          */
    -    public List<String> searchSpacesNames(String parametrizedSqlClause, int nb, int start, List<?> parameterValues)
    +    public List<String> searchSpacesNames(String parameterizedSqlClause, int nb, int start, List<?> parameterValues)
             throws XWikiException
         {
    -        return this.xwiki.getStore().search("select distinct doc.space from XWikiDocument doc " + parametrizedSqlClause,
    +        checkSearchQueryAllowed(parameterizedSqlClause);
    +
    +        return this.xwiki.getStore().search("select distinct doc.space from XWikiDocument doc " + parameterizedSqlClause,
                 nb, start, parameterValues, this.context);
         }
     
    @@ -915,7 +961,7 @@ public List<String> searchSpacesNames(String parametrizedSqlClause, int nb, int
          * {@link #searchDocuments(String, int, int, List)} for more about parameterized hql clauses. You can specify
          * properties of attach (the attachment) or doc (the document it is attached to)
          *
    -     * @param parametrizedSqlClause The HQL where clause. For example
    +     * @param parameterizedSqlClause The HQL where clause. For example
          *            {@code where doc.fullName <> ?1 and (doc.parent = ?2 or (doc.parent = ?3 and doc.space = ?4))}
          * @param nb The number of rows to return. If 0 then all rows are returned
          * @param start The number of rows to skip at the beginning.
    @@ -924,27 +970,31 @@ public List<String> searchSpacesNames(String parametrizedSqlClause, int nb, int
          * @throws XWikiException in case of error while performing the query
          * @since 5.0M2
          */
    -    public List<Attachment> searchAttachments(String parametrizedSqlClause, int nb, int start, List<?> parameterValues)
    +    public List<Attachment> searchAttachments(String parameterizedSqlClause, int nb, int start, List<?> parameterValues)
             throws XWikiException
         {
    +        checkSearchQueryAllowed(parameterizedSqlClause);
    +
             return convertAttachments(
    -            this.xwiki.searchAttachments(parametrizedSqlClause, true, nb, start, parameterValues, this.context));
    +            this.xwiki.searchAttachments(parameterizedSqlClause, true, nb, start, parameterValues, this.context));
         }
     
         /**
          * Count attachments returned by a given parameterized query
          *
    -     * @param parametrizedSqlClause Everything which would follow the "WHERE" in HQL see:
    +     * @param parameterizedSqlClause Everything which would follow the "WHERE" in HQL see:
          *            {@link #searchDocuments(String, int, int, List)}
          * @param parameterValues A {@link java.util.List} of the where clause values that replace the question marks (?)
          * @return int number of attachments found.
          * @throws XWikiException
          * @see #searchAttachments(String, int, int, List)
          * @since 5.0M2
          */
    -    public int countAttachments(String parametrizedSqlClause, List<?> parameterValues) throws XWikiException
    +    public int countAttachments(String parameterizedSqlClause, List<?> parameterValues) throws XWikiException
         {
    -        return this.xwiki.countAttachments(parametrizedSqlClause, parameterValues, this.context);
    +        checkSearchQueryAllowed(parameterizedSqlClause);
    +
    +        return this.xwiki.countAttachments(parameterizedSqlClause, parameterValues, this.context);
         }
     
         /**
    
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/store/hibernate/query/HqlQueryUtils.java+75 1 modified
    @@ -19,6 +19,7 @@
      */
     package com.xpn.xwiki.internal.store.hibernate.query;
     
    +import java.util.StringTokenizer;
     import java.util.regex.Matcher;
     import java.util.regex.Pattern;
     
    @@ -34,6 +35,16 @@
      */
     public final class HqlQueryUtils
     {
    +    private static final String FROM = "from";
    +
    +    private static final String WHERE = "where ";
    +
    +    private static final String ORDER = "order";
    +
    +    private static final String ORDER_BY = "order by";
    +
    +    private static final String COMMA = ",";
    +
         private static final Pattern LEGACY_ORDINAL_PARAMS_PATTERN = Pattern.compile("([=\\s,\\(<>])\\?([=\\s,\\)<>]|$)");
     
         private HqlQueryUtils()
    @@ -47,7 +58,7 @@ private HqlQueryUtils()
          */
         public static boolean isShortFormStatement(String statement)
         {
    -        return StringUtils.startsWithAny(statement.trim().toLowerCase(), ",", "where ", "order by");
    +        return StringUtils.startsWithAny(statement.trim().toLowerCase(), COMMA, WHERE, ORDER_BY);
         }
     
         /**
    @@ -129,4 +140,67 @@ public String getStatement()
     
             return completeQuery;
         }
    +
    +    /**
    +     * @param queryPrefix the start of the SQL query (for example "select distinct doc.space, doc.name")
    +     * @param whereSQL the where clause to append
    +     * @return the full formed SQL query, to which the order by columns have been added as returned columns (this is
    +     *         required for example for HSQLDB).
    +     * @since 17.0.3RC1
    +     * @since 16.10.6
    +     */
    +    public static String createLegacySQLQuery(String queryPrefix, String whereSQL)
    +    {
    +        StringBuilder sql = new StringBuilder(queryPrefix);
    +
    +        String normalizedWhereSQL;
    +        if (StringUtils.isBlank(whereSQL)) {
    +            normalizedWhereSQL = "";
    +        } else {
    +            normalizedWhereSQL = whereSQL.trim();
    +        }
    +
    +        sql.append(getColumnsForSelectStatement(normalizedWhereSQL));
    +        sql.append(" from XWikiDocument as doc");
    +
    +        if (!normalizedWhereSQL.equals("")) {
    +            if ((!normalizedWhereSQL.startsWith(WHERE)) && (normalizedWhereSQL.charAt(0) != ',')) {
    +                sql.append(" where ");
    +            } else {
    +                sql.append(" ");
    +            }
    +            sql.append(normalizedWhereSQL);
    +        }
    +
    +        return sql.toString();
    +    }
    +
    +    /**
    +     * @param whereSQL the SQL where clause
    +     * @return the list of columns to return in the select clause as a string starting with ", " if there are columns or
    +     *         an empty string otherwise. The returned columns are extracted from the where clause. One reason for doing
    +     *         so is because HSQLDB only support SELECT DISTINCT SQL statements where the columns operated on are
    +     *         returned from the query.
    +     * @since 17.0.3RC1
    +     * @since 16.10.6
    +     */
    +    public static String getColumnsForSelectStatement(String whereSQL)
    +    {
    +        StringBuilder columns = new StringBuilder();
    +
    +        int orderByPos = whereSQL.toLowerCase().indexOf(ORDER_BY);
    +        if (orderByPos >= 0) {
    +            String orderByStatement = whereSQL.substring(orderByPos + ORDER_BY.length() + 1);
    +            StringTokenizer tokenizer = new StringTokenizer(orderByStatement, COMMA);
    +            while (tokenizer.hasMoreTokens()) {
    +                String column = tokenizer.nextToken().trim();
    +                // Remove "desc" or "asc" from the column found
    +                column = StringUtils.removeEndIgnoreCase(column, " desc");
    +                column = StringUtils.removeEndIgnoreCase(column, " asc");
    +                columns.append(", ").append(column.trim());
    +            }
    +        }
    +
    +        return columns.toString();
    +    }
     }
    
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java+3 39 modified
    @@ -38,7 +38,6 @@
     import java.util.Objects;
     import java.util.Optional;
     import java.util.Set;
    -import java.util.StringTokenizer;
     import java.util.concurrent.atomic.AtomicReference;
     import java.util.concurrent.locks.Lock;
     import java.util.concurrent.locks.ReentrantLock;
    @@ -101,6 +100,7 @@
     import com.xpn.xwiki.doc.XWikiLock;
     import com.xpn.xwiki.doc.XWikiSpace;
     import com.xpn.xwiki.internal.store.hibernate.legacy.LegacySessionImplementor;
    +import com.xpn.xwiki.internal.store.hibernate.query.HqlQueryUtils;
     import com.xpn.xwiki.monitor.api.MonitorPlugin;
     import com.xpn.xwiki.objects.BaseCollection;
     import com.xpn.xwiki.objects.BaseElement;
    @@ -2902,28 +2902,7 @@ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbyla
          */
         protected String createSQLQuery(String queryPrefix, String whereSQL)
         {
    -        StringBuilder sql = new StringBuilder(queryPrefix);
    -
    -        String normalizedWhereSQL;
    -        if (StringUtils.isBlank(whereSQL)) {
    -            normalizedWhereSQL = "";
    -        } else {
    -            normalizedWhereSQL = whereSQL.trim();
    -        }
    -
    -        sql.append(getColumnsForSelectStatement(normalizedWhereSQL));
    -        sql.append(" from XWikiDocument as doc");
    -
    -        if (!normalizedWhereSQL.equals("")) {
    -            if ((!normalizedWhereSQL.startsWith("where")) && (!normalizedWhereSQL.startsWith(","))) {
    -                sql.append(" where ");
    -            } else {
    -                sql.append(" ");
    -            }
    -            sql.append(normalizedWhereSQL);
    -        }
    -
    -        return sql.toString();
    +        return HqlQueryUtils.createLegacySQLQuery(queryPrefix, whereSQL);
         }
     
         /**
    @@ -2935,22 +2914,7 @@ protected String createSQLQuery(String queryPrefix, String whereSQL)
          */
         protected String getColumnsForSelectStatement(String whereSQL)
         {
    -        StringBuilder columns = new StringBuilder();
    -
    -        int orderByPos = whereSQL.toLowerCase().indexOf("order by");
    -        if (orderByPos >= 0) {
    -            String orderByStatement = whereSQL.substring(orderByPos + "order by".length() + 1);
    -            StringTokenizer tokenizer = new StringTokenizer(orderByStatement, ",");
    -            while (tokenizer.hasMoreTokens()) {
    -                String column = tokenizer.nextToken().trim();
    -                // Remove "desc" or "asc" from the column found
    -                column = StringUtils.removeEndIgnoreCase(column, " desc");
    -                column = StringUtils.removeEndIgnoreCase(column, " asc");
    -                columns.append(", ").append(column.trim());
    -            }
    -        }
    -
    -        return columns.toString();
    +        return HqlQueryUtils.getColumnsForSelectStatement(whereSQL);
         }
     
         @Override
    
7313dc9b533c

XWIKI-22728: Improve search query validation

https://github.com/xwiki/xwiki-platformThomas MortagneFeb 27, 2025via ghsa
3 files changed · +137 49
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/XWiki.java+59 9 modified
    @@ -44,6 +44,7 @@
     import org.xwiki.model.reference.PageReference;
     import org.xwiki.model.reference.SpaceReference;
     import org.xwiki.model.reference.WikiReference;
    +import org.xwiki.query.hql.internal.HQLStatementValidator;
     import org.xwiki.rendering.renderer.PrintRendererFactory;
     import org.xwiki.rendering.syntax.Syntax;
     import org.xwiki.security.authorization.AuthorizationException;
    @@ -58,6 +59,7 @@
     import com.xpn.xwiki.doc.XWikiDocument;
     import com.xpn.xwiki.internal.XWikiInitializerJob;
     import com.xpn.xwiki.internal.XWikiInitializerJobStatus;
    +import com.xpn.xwiki.internal.store.hibernate.query.HqlQueryUtils;
     import com.xpn.xwiki.objects.meta.MetaClass;
     import com.xpn.xwiki.user.api.XWikiUser;
     import com.xpn.xwiki.util.Programming;
    @@ -105,6 +107,8 @@ public class XWiki extends Api
     
         private ContextualAuthorizationManager contextualAuthorizationManager;
     
    +    private HQLStatementValidator hqlValidator;
    +
         /**
          * XWiki API Constructor
          *
    @@ -167,6 +171,15 @@ private DocumentRevisionProvider getDocumentRevisionProvider()
             return this.documentRevisionProvider;
         }
     
    +    private HQLStatementValidator getHQLStatementValidator()
    +    {
    +        if (this.hqlValidator == null) {
    +            this.hqlValidator = Utils.getComponent(HQLStatementValidator.class);
    +        }
    +
    +        return this.hqlValidator;
    +    }
    +
         /**
          * Privileged API allowing to access the underlying main XWiki Object
          *
    @@ -710,6 +723,23 @@ public MetaClass getMetaclass()
             return this.xwiki.getMetaclass();
         }
     
    +    private void checkSearchQueryAllowed(String whereSQL) throws XWikiException
    +    {
    +        if (!hasProgrammingRights()) {
    +            try {
    +                if (!getHQLStatementValidator()
    +                    .isSafe(HqlQueryUtils.createLegacySQLQuery("select distinct doc.fullName", whereSQL))) {
    +                    throw new XWikiException(XWikiException.MODULE_XWIKI_STORE,
    +                        XWikiException.ERROR_XWIKI_ACCESS_DENIED,
    +                        "The query [" + whereSQL + "] requires programming right");
    +                }
    +            } catch (Exception e) {
    +                throw new XWikiException(XWikiException.MODULE_XWIKI_STORE, XWikiException.ERROR_XWIKI_ACCESS_DENIED,
    +                    "Failed to validate the query [" + whereSQL + "], requiring programming right", e);
    +            }
    +        }
    +    }
    +
         /**
          * API allowing to search for document names matching a query. Examples:
          * <ul>
    @@ -742,6 +772,8 @@ public MetaClass getMetaclass()
         @Deprecated
         public List<String> searchDocuments(String wheresql) throws XWikiException
         {
    +        checkSearchQueryAllowed(wheresql);
    +
             return this.xwiki.getStore().searchDocumentsNames(wheresql, getXWikiContext());
         }
     
    @@ -760,6 +792,8 @@ public List<String> searchDocuments(String wheresql) throws XWikiException
         @Deprecated
         public List<String> searchDocuments(String wheresql, int nb, int start) throws XWikiException
         {
    +        checkSearchQueryAllowed(wheresql);
    +
             return this.xwiki.getStore().searchDocumentsNames(wheresql, nb, start, getXWikiContext());
         }
     
    @@ -796,6 +830,8 @@ public List<String> searchDocuments(String wheresql, int nb, int start, String s
          */
         public List<Document> searchDocuments(String wheresql, boolean distinctbylocale) throws XWikiException
         {
    +        checkSearchQueryAllowed(wheresql);
    +
             return convert(this.xwiki.getStore().searchDocuments(wheresql, distinctbylocale, getXWikiContext()));
         }
     
    @@ -812,6 +848,8 @@ public List<Document> searchDocuments(String wheresql, boolean distinctbylocale)
         public List<Document> searchDocuments(String wheresql, boolean distinctbylocale, int nb, int start)
             throws XWikiException
         {
    +        checkSearchQueryAllowed(wheresql);
    +
             return convert(this.xwiki.getStore().searchDocuments(wheresql, distinctbylocale, nb, start, getXWikiContext()));
         }
     
    @@ -845,6 +883,8 @@ public List<Document> searchDocuments(String wheresql, boolean distinctbylocale,
         public List<String> searchDocuments(String parameterizedWhereClause, int maxResults, int startOffset,
             List<?> parameterValues) throws XWikiException
         {
    +        checkSearchQueryAllowed(parameterizedWhereClause);
    +
             return this.xwiki.getStore().searchDocumentsNames(parameterizedWhereClause, maxResults, startOffset,
                 parameterValues, getXWikiContext());
         }
    @@ -858,6 +898,8 @@ public List<String> searchDocuments(String parameterizedWhereClause, int maxResu
         @Deprecated
         public List<String> searchDocuments(String parameterizedWhereClause, List<?> parameterValues) throws XWikiException
         {
    +        checkSearchQueryAllowed(parameterizedWhereClause);
    +
             return this.xwiki.getStore().searchDocumentsNames(parameterizedWhereClause, parameterValues, getXWikiContext());
         }
     
    @@ -885,6 +927,8 @@ public List<String> searchDocumentsNames(String wikiName, String parameterizedWh
             try {
                 this.context.setWikiId(wikiName);
     
    +            checkSearchQueryAllowed(parameterizedWhereClause);
    +
                 return searchDocuments(parameterizedWhereClause, maxResults, startOffset, parameterValues);
             } finally {
                 this.context.setWikiId(database);
    @@ -895,18 +939,20 @@ public List<String> searchDocumentsNames(String wikiName, String parameterizedWh
          * Search spaces by passing HQL where clause values as parameters. See
          * {@link #searchDocuments(String, int, int, List)} for more about parameterized hql clauses.
          *
    -     * @param parametrizedSqlClause the HQL where clause. For example
    +     * @param parameterizedSqlClause the HQL where clause. For example
          *            {@code where doc.fullName <> ?1 and (doc.parent = ?2 or (doc.parent = ?3 and doc.space = ?4))}
          * @param nb the number of rows to return. If 0 then all rows are returned
          * @param start the number of rows to skip. If 0 don't skip any row
          * @param parameterValues the where clause values that replace the question marks (?)
          * @return a list of spaces names.
          * @throws XWikiException in case of error while performing the query
          */
    -    public List<String> searchSpacesNames(String parametrizedSqlClause, int nb, int start, List<?> parameterValues)
    +    public List<String> searchSpacesNames(String parameterizedSqlClause, int nb, int start, List<?> parameterValues)
             throws XWikiException
         {
    -        return this.xwiki.getStore().search("select distinct doc.space from XWikiDocument doc " + parametrizedSqlClause,
    +        checkSearchQueryAllowed(parameterizedSqlClause);
    +
    +        return this.xwiki.getStore().search("select distinct doc.space from XWikiDocument doc " + parameterizedSqlClause,
                 nb, start, parameterValues, this.context);
         }
     
    @@ -915,7 +961,7 @@ public List<String> searchSpacesNames(String parametrizedSqlClause, int nb, int
          * {@link #searchDocuments(String, int, int, List)} for more about parameterized hql clauses. You can specify
          * properties of attach (the attachment) or doc (the document it is attached to)
          *
    -     * @param parametrizedSqlClause The HQL where clause. For example
    +     * @param parameterizedSqlClause The HQL where clause. For example
          *            {@code where doc.fullName <> ?1 and (doc.parent = ?2 or (doc.parent = ?3 and doc.space = ?4))}
          * @param nb The number of rows to return. If 0 then all rows are returned
          * @param start The number of rows to skip at the beginning.
    @@ -924,27 +970,31 @@ public List<String> searchSpacesNames(String parametrizedSqlClause, int nb, int
          * @throws XWikiException in case of error while performing the query
          * @since 5.0M2
          */
    -    public List<Attachment> searchAttachments(String parametrizedSqlClause, int nb, int start, List<?> parameterValues)
    +    public List<Attachment> searchAttachments(String parameterizedSqlClause, int nb, int start, List<?> parameterValues)
             throws XWikiException
         {
    +        checkSearchQueryAllowed(parameterizedSqlClause);
    +
             return convertAttachments(
    -            this.xwiki.searchAttachments(parametrizedSqlClause, true, nb, start, parameterValues, this.context));
    +            this.xwiki.searchAttachments(parameterizedSqlClause, true, nb, start, parameterValues, this.context));
         }
     
         /**
          * Count attachments returned by a given parameterized query
          *
    -     * @param parametrizedSqlClause Everything which would follow the "WHERE" in HQL see:
    +     * @param parameterizedSqlClause Everything which would follow the "WHERE" in HQL see:
          *            {@link #searchDocuments(String, int, int, List)}
          * @param parameterValues A {@link java.util.List} of the where clause values that replace the question marks (?)
          * @return int number of attachments found.
          * @throws XWikiException
          * @see #searchAttachments(String, int, int, List)
          * @since 5.0M2
          */
    -    public int countAttachments(String parametrizedSqlClause, List<?> parameterValues) throws XWikiException
    +    public int countAttachments(String parameterizedSqlClause, List<?> parameterValues) throws XWikiException
         {
    -        return this.xwiki.countAttachments(parametrizedSqlClause, parameterValues, this.context);
    +        checkSearchQueryAllowed(parameterizedSqlClause);
    +
    +        return this.xwiki.countAttachments(parameterizedSqlClause, parameterValues, this.context);
         }
     
         /**
    
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/internal/store/hibernate/query/HqlQueryUtils.java+75 1 modified
    @@ -19,6 +19,7 @@
      */
     package com.xpn.xwiki.internal.store.hibernate.query;
     
    +import java.util.StringTokenizer;
     import java.util.regex.Matcher;
     import java.util.regex.Pattern;
     
    @@ -34,6 +35,16 @@
      */
     public final class HqlQueryUtils
     {
    +    private static final String FROM = "from";
    +
    +    private static final String WHERE = "where ";
    +
    +    private static final String ORDER = "order";
    +
    +    private static final String ORDER_BY = "order by";
    +
    +    private static final String COMMA = ",";
    +
         private static final Pattern LEGACY_ORDINAL_PARAMS_PATTERN = Pattern.compile("([=\\s,\\(<>])\\?([=\\s,\\)<>]|$)");
     
         private HqlQueryUtils()
    @@ -47,7 +58,7 @@ private HqlQueryUtils()
          */
         public static boolean isShortFormStatement(String statement)
         {
    -        return StringUtils.startsWithAny(statement.trim().toLowerCase(), ",", "where ", "order by");
    +        return StringUtils.startsWithAny(statement.trim().toLowerCase(), COMMA, WHERE, ORDER_BY);
         }
     
         /**
    @@ -129,4 +140,67 @@ public String getStatement()
     
             return completeQuery;
         }
    +
    +    /**
    +     * @param queryPrefix the start of the SQL query (for example "select distinct doc.space, doc.name")
    +     * @param whereSQL the where clause to append
    +     * @return the full formed SQL query, to which the order by columns have been added as returned columns (this is
    +     *         required for example for HSQLDB).
    +     * @since 17.0.3RC1
    +     * @since 16.10.6
    +     */
    +    public static String createLegacySQLQuery(String queryPrefix, String whereSQL)
    +    {
    +        StringBuilder sql = new StringBuilder(queryPrefix);
    +
    +        String normalizedWhereSQL;
    +        if (StringUtils.isBlank(whereSQL)) {
    +            normalizedWhereSQL = "";
    +        } else {
    +            normalizedWhereSQL = whereSQL.trim();
    +        }
    +
    +        sql.append(getColumnsForSelectStatement(normalizedWhereSQL));
    +        sql.append(" from XWikiDocument as doc");
    +
    +        if (!normalizedWhereSQL.equals("")) {
    +            if ((!normalizedWhereSQL.startsWith(WHERE)) && (normalizedWhereSQL.charAt(0) != ',')) {
    +                sql.append(" where ");
    +            } else {
    +                sql.append(" ");
    +            }
    +            sql.append(normalizedWhereSQL);
    +        }
    +
    +        return sql.toString();
    +    }
    +
    +    /**
    +     * @param whereSQL the SQL where clause
    +     * @return the list of columns to return in the select clause as a string starting with ", " if there are columns or
    +     *         an empty string otherwise. The returned columns are extracted from the where clause. One reason for doing
    +     *         so is because HSQLDB only support SELECT DISTINCT SQL statements where the columns operated on are
    +     *         returned from the query.
    +     * @since 17.0.3RC1
    +     * @since 16.10.6
    +     */
    +    public static String getColumnsForSelectStatement(String whereSQL)
    +    {
    +        StringBuilder columns = new StringBuilder();
    +
    +        int orderByPos = whereSQL.toLowerCase().indexOf(ORDER_BY);
    +        if (orderByPos >= 0) {
    +            String orderByStatement = whereSQL.substring(orderByPos + ORDER_BY.length() + 1);
    +            StringTokenizer tokenizer = new StringTokenizer(orderByStatement, COMMA);
    +            while (tokenizer.hasMoreTokens()) {
    +                String column = tokenizer.nextToken().trim();
    +                // Remove "desc" or "asc" from the column found
    +                column = StringUtils.removeEndIgnoreCase(column, " desc");
    +                column = StringUtils.removeEndIgnoreCase(column, " asc");
    +                columns.append(", ").append(column.trim());
    +            }
    +        }
    +
    +        return columns.toString();
    +    }
     }
    
  • xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/store/XWikiHibernateStore.java+3 39 modified
    @@ -38,7 +38,6 @@
     import java.util.Objects;
     import java.util.Optional;
     import java.util.Set;
    -import java.util.StringTokenizer;
     import java.util.concurrent.atomic.AtomicReference;
     import java.util.concurrent.locks.Lock;
     import java.util.concurrent.locks.ReentrantLock;
    @@ -101,6 +100,7 @@
     import com.xpn.xwiki.doc.XWikiLock;
     import com.xpn.xwiki.doc.XWikiSpace;
     import com.xpn.xwiki.internal.store.hibernate.legacy.LegacySessionImplementor;
    +import com.xpn.xwiki.internal.store.hibernate.query.HqlQueryUtils;
     import com.xpn.xwiki.monitor.api.MonitorPlugin;
     import com.xpn.xwiki.objects.BaseCollection;
     import com.xpn.xwiki.objects.BaseElement;
    @@ -2916,28 +2916,7 @@ public List<XWikiDocument> searchDocuments(String wheresql, boolean distinctbyla
          */
         protected String createSQLQuery(String queryPrefix, String whereSQL)
         {
    -        StringBuilder sql = new StringBuilder(queryPrefix);
    -
    -        String normalizedWhereSQL;
    -        if (StringUtils.isBlank(whereSQL)) {
    -            normalizedWhereSQL = "";
    -        } else {
    -            normalizedWhereSQL = whereSQL.trim();
    -        }
    -
    -        sql.append(getColumnsForSelectStatement(normalizedWhereSQL));
    -        sql.append(" from XWikiDocument as doc");
    -
    -        if (!normalizedWhereSQL.equals("")) {
    -            if ((!normalizedWhereSQL.startsWith("where")) && (!normalizedWhereSQL.startsWith(","))) {
    -                sql.append(" where ");
    -            } else {
    -                sql.append(" ");
    -            }
    -            sql.append(normalizedWhereSQL);
    -        }
    -
    -        return sql.toString();
    +        return HqlQueryUtils.createLegacySQLQuery(queryPrefix, whereSQL);
         }
     
         /**
    @@ -2949,22 +2928,7 @@ protected String createSQLQuery(String queryPrefix, String whereSQL)
          */
         protected String getColumnsForSelectStatement(String whereSQL)
         {
    -        StringBuilder columns = new StringBuilder();
    -
    -        int orderByPos = whereSQL.toLowerCase().indexOf("order by");
    -        if (orderByPos >= 0) {
    -            String orderByStatement = whereSQL.substring(orderByPos + "order by".length() + 1);
    -            StringTokenizer tokenizer = new StringTokenizer(orderByStatement, ",");
    -            while (tokenizer.hasMoreTokens()) {
    -                String column = tokenizer.nextToken().trim();
    -                // Remove "desc" or "asc" from the column found
    -                column = StringUtils.removeEndIgnoreCase(column, " desc");
    -                column = StringUtils.removeEndIgnoreCase(column, " asc");
    -                columns.append(", ").append(column.trim());
    -            }
    -        }
    -
    -        return columns.toString();
    +        return HqlQueryUtils.getColumnsForSelectStatement(whereSQL);
         }
     
         @Override
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.