From 49688976e34f4601b6b28f3e9d78b5dfd5d9090c Mon Sep 17 00:00:00 2001 From: Vitaliy Baschlykoff Date: Thu, 23 Nov 2023 07:45:33 +1100 Subject: [PATCH] Escape column names with backticks in order by clause of generated hibernate query to prevent potential hql injection --- .../test/JpaOperationsSortTest.java | 25 +++++++++++++++---- .../common/runtime/PanacheJpaUtil.java | 18 ++++++++++++- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/test/java/io/quarkus/hibernate/orm/panache/deployment/test/JpaOperationsSortTest.java b/extensions/panache/hibernate-orm-panache/deployment/src/test/java/io/quarkus/hibernate/orm/panache/deployment/test/JpaOperationsSortTest.java index 30e13c7a45c5f..39e14f149d201 100644 --- a/extensions/panache/hibernate-orm-panache/deployment/src/test/java/io/quarkus/hibernate/orm/panache/deployment/test/JpaOperationsSortTest.java +++ b/extensions/panache/hibernate-orm-panache/deployment/src/test/java/io/quarkus/hibernate/orm/panache/deployment/test/JpaOperationsSortTest.java @@ -2,9 +2,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import io.quarkus.panache.common.Sort; +import io.quarkus.panache.common.exception.PanacheQueryException; import io.quarkus.panache.hibernate.common.runtime.PanacheJpaUtil; public class JpaOperationsSortTest { @@ -18,7 +20,7 @@ public void testEmptySortByYieldsEmptyString() { @Test public void testSortBy() { Sort sort = Sort.by("foo", "bar"); - assertEquals(" ORDER BY foo , bar", PanacheJpaUtil.toOrderBy(sort)); + assertEquals(" ORDER BY `foo` , `bar`", PanacheJpaUtil.toOrderBy(sort)); } @Test @@ -29,14 +31,27 @@ public void testEmptySortEmptyYieldsEmptyString() { @Test public void testSortByNullsFirst() { - Sort emptySort = Sort.by("foo", Sort.Direction.Ascending, Sort.NullPrecedence.NULLS_FIRST); - assertEquals(" ORDER BY foo NULLS FIRST", PanacheJpaUtil.toOrderBy(emptySort)); + Sort sort = Sort.by("foo", Sort.Direction.Ascending, Sort.NullPrecedence.NULLS_FIRST); + assertEquals(" ORDER BY `foo` NULLS FIRST", PanacheJpaUtil.toOrderBy(sort)); } @Test public void testSortByNullsLast() { - Sort emptySort = Sort.by("foo", Sort.Direction.Descending, Sort.NullPrecedence.NULLS_LAST); - assertEquals(" ORDER BY foo DESC NULLS LAST", PanacheJpaUtil.toOrderBy(emptySort)); + Sort sort = Sort.by("foo", Sort.Direction.Descending, Sort.NullPrecedence.NULLS_LAST); + assertEquals(" ORDER BY `foo` DESC NULLS LAST", PanacheJpaUtil.toOrderBy(sort)); + } + + @Test + public void testSortByColumnWithBacktick() { + Sort sort = Sort.by("jeanne", "d`arc"); + Assertions.assertThrowsExactly(PanacheQueryException.class, () -> PanacheJpaUtil.toOrderBy(sort), + "Sort column name cannot have backticks"); + } + + @Test + public void testSortByQuotedColumn() { + Sort sort = Sort.by("`foo`", "bar"); + assertEquals(" ORDER BY `foo` , `bar`", PanacheJpaUtil.toOrderBy(sort)); } } diff --git a/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/PanacheJpaUtil.java b/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/PanacheJpaUtil.java index 3029a33398242..14edc447ad802 100644 --- a/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/PanacheJpaUtil.java +++ b/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/PanacheJpaUtil.java @@ -175,7 +175,7 @@ public static String toOrderBy(Sort sort) { Sort.Column column = sort.getColumns().get(i); if (i > 0) sb.append(" , "); - sb.append(column.getName()); + sb.append('`').append(unquoteColumnName(column)).append('`'); if (column.getDirection() != Sort.Direction.Ascending) { sb.append(" DESC"); } @@ -191,4 +191,20 @@ public static String toOrderBy(Sort sort) { } return sb.toString(); } + + private static String unquoteColumnName(Sort.Column column) { + String columnName = column.getName(); + String unquotedColumnName; + //Note HQL uses backticks to escape/quote special words that are used as identifiers + if (columnName.charAt(0) == '`' && columnName.charAt(columnName.length() - 1) == '`') { + unquotedColumnName = columnName.substring(1, columnName.length() - 1); + } else { + unquotedColumnName = columnName; + } + // Note we're not dealing with columns but with entity attributes so no backticks expected in unquoted column name + if (unquotedColumnName.indexOf('`') >= 0) { + throw new PanacheQueryException("Sort column name cannot have backticks"); + } + return unquotedColumnName; + } }