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

difin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git


The following commit(s) were added to refs/heads/master by this push:
     new c3f56b4522e HIVE-29985: Validate orderByClause on compaction requests 
(#6850)
c3f56b4522e is described below

commit c3f56b4522ebc0997178ead3ad7f49087239443c
Author: Dmitriy Fingerman <[email protected]>
AuthorDate: Tue Oct 6 19:48:40 2026 -0600

    HIVE-29985: Validate orderByClause on compaction requests (#6850)
    
    Co-authored-by: Dmitriy Fingerman <[email protected]>
---
 .../mr/hive/compaction/IcebergQueryCompactor.java  | 17 +++++-
 .../ql/txn/compactor/CompactionQueryBuilder.java   |  4 ++
 .../utils/CompactionOrderByValidator.java          | 63 ++++++++++++++++++++++
 .../utils/TestCompactionOrderByValidator.java      | 62 +++++++++++++++++++++
 .../txn/jdbc/functions/CompactFunction.java        | 10 ++++
 5 files changed, 155 insertions(+), 1 deletion(-)

diff --git 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/compaction/IcebergQueryCompactor.java
 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/compaction/IcebergQueryCompactor.java
index 6aa2855f82d..d30f8a8be58 100644
--- 
a/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/compaction/IcebergQueryCompactor.java
+++ 
b/iceberg/iceberg-handler/src/main/java/org/apache/iceberg/mr/hive/compaction/IcebergQueryCompactor.java
@@ -29,6 +29,7 @@
 import org.apache.hadoop.hive.metastore.api.CompactionType;
 import org.apache.hadoop.hive.metastore.api.MetaException;
 import org.apache.hadoop.hive.metastore.txn.entities.CompactionInfo;
+import org.apache.hadoop.hive.metastore.utils.CompactionOrderByValidator;
 import org.apache.hadoop.hive.ql.Context.RewritePolicy;
 import org.apache.hadoop.hive.ql.DriverUtils;
 import org.apache.hadoop.hive.ql.ErrorMsg;
@@ -101,7 +102,7 @@ private String buildCompactionQuery(CompactorContext 
context, String compactTabl
     org.apache.hadoop.hive.ql.metadata.Table table = 
Hive.get(conf).getTable(context.getTable().getDbName(),
         context.getTable().getTableName());
     Table icebergTable = IcebergTableUtil.getTable(conf, table.getTTable());
-    String orderBy = ci.orderByClause == null ? "" : ci.orderByClause;
+    String orderBy = validatedOrderByClause(ci.orderByClause, 
compactTableName);
     String fileSizePredicate = buildMinorFileSizePredicate(ci, 
compactTableName, conf, table);
 
     String columnsList = "*";
@@ -251,4 +252,18 @@ private String buildPartitionPredicate(CompactionInfo ci, 
PartitionSpec spec) th
       return String.format("`partition`.%s = %s", column, literal);
     }).collect(Collectors.joining(" AND "));
   }
+
+  /**
+   * The compaction queue is written from a client-settable Thrift field: 
treat its contents as
+   * untrusted and re-validate here, so a clause injected before (or around) 
the enqueue-time
+   * check can never be concatenated into the privileged compaction query.
+   */
+  private static String validatedOrderByClause(String orderByClause, String 
compactTableName) throws HiveException {
+    try {
+      CompactionOrderByValidator.validate(orderByClause);
+    } catch (IllegalArgumentException e) {
+      throw new HiveException("Refusing to compact " + compactTableName + ": " 
+ e.getMessage(), e);
+    }
+    return orderByClause == null ? "" : orderByClause;
+  }
 }
diff --git 
a/ql/src/java/org/apache/hadoop/hive/ql/txn/compactor/CompactionQueryBuilder.java
 
b/ql/src/java/org/apache/hadoop/hive/ql/txn/compactor/CompactionQueryBuilder.java
index 1c5baa1ea9c..2280fdd8944 100644
--- 
a/ql/src/java/org/apache/hadoop/hive/ql/txn/compactor/CompactionQueryBuilder.java
+++ 
b/ql/src/java/org/apache/hadoop/hive/ql/txn/compactor/CompactionQueryBuilder.java
@@ -26,6 +26,7 @@
 import org.apache.hadoop.hive.metastore.api.Partition;
 import org.apache.hadoop.hive.metastore.api.StorageDescriptor;
 import org.apache.hadoop.hive.metastore.api.Table;
+import org.apache.hadoop.hive.metastore.utils.CompactionOrderByValidator;
 import org.apache.hadoop.hive.ql.exec.DDLPlanUtils;
 import org.apache.hadoop.hive.ql.io.AcidDirectory;
 import org.apache.hadoop.hive.ql.io.AcidUtils;
@@ -144,6 +145,9 @@ CompactionQueryBuilder setSourceTabForInsert(String 
sourceTabForInsert) {
    * @param orderByClause The ORDER BY clause to use for data reordering.
    */
   public CompactionQueryBuilder setOrderByClause(String orderByClause) {
+    // The clause travels through the client-writable compaction queue; allow 
only a plain ORDER BY
+    // over column names before it is concatenated into a compaction query.
+    CompactionOrderByValidator.validate(orderByClause);
     this.orderByClause = orderByClause;
     return this;
   }
diff --git 
a/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/utils/CompactionOrderByValidator.java
 
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/utils/CompactionOrderByValidator.java
new file mode 100644
index 00000000000..9e4f1497485
--- /dev/null
+++ 
b/standalone-metastore/metastore-common/src/main/java/org/apache/hadoop/hive/metastore/utils/CompactionOrderByValidator.java
@@ -0,0 +1,63 @@
+/*
+ * 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.hadoop.hive.metastore.utils;
+
+import java.util.regex.Pattern;
+
+/**
+ * Validates the ORDER BY clause carried on a compaction request.
+ *
+ * The clause originates from the client-settable Thrift field {@code 
CompactionRequest.orderByClause},
+ * is persisted verbatim in the compaction queue and is later concatenated 
into a query that the
+ * compaction worker executes with the compaction session's privileges. It 
must therefore be treated
+ * as untrusted input everywhere it is consumed: only a plain {@code ORDER BY} 
over column names
+ * (optionally qualified, quoted, with ASC/DESC and NULLS FIRST/LAST 
modifiers) is accepted -
+ * exactly the shape HiveServer2 produces for
+ * {@code ALTER TABLE ... COMPACT ... ORDER BY col [ASC|DESC] [NULLS 
FIRST|LAST], ...}.
+ * Anything else (subqueries, UNION tails, LIMIT, expressions, comments) is 
rejected.
+ */
+public final class CompactionOrderByValidator {
+
+  private static final String IDENTIFIER = "(?:[A-Za-z0-9_]+|`(?:[^`]|``)+`)";
+  private static final String COLUMN = IDENTIFIER + "(?:\\s*\\.\\s*" + 
IDENTIFIER + ")*";
+  private static final String ITEM =
+      COLUMN + "(?:\\s+(?i:ASC|DESC))?(?:\\s+(?i:NULLS)\\s+(?i:FIRST|LAST))?";
+  private static final Pattern ORDER_BY_PATTERN = Pattern.compile(
+      "\\s*(?i:ORDER)\\s+(?i:BY)\\s+" + ITEM + "(?:\\s*,\\s*" + ITEM + 
")*\\s*");
+
+  private CompactionOrderByValidator() {
+    throw new UnsupportedOperationException("CompactionOrderByValidator should 
not be instantiated");
+  }
+
+  /**
+   * Validates a compaction request ORDER BY clause.
+   *
+   * @param orderByClause the clause as stored on the request, e.g. "order by 
`col` DESC nulls last, col2";
+   *                      null or blank is accepted (no reordering requested)
+   * @throws IllegalArgumentException if the clause is anything but a plain 
ORDER BY over column names
+   */
+  public static void validate(String orderByClause) {
+    if (orderByClause == null || orderByClause.trim().isEmpty()) {
+      return;
+    }
+    if (!ORDER_BY_PATTERN.matcher(orderByClause).matches()) {
+      throw new IllegalArgumentException("orderByClause must be a plain ORDER 
BY over column names " +
+          "(optionally with ASC/DESC and NULLS FIRST/LAST), got: " + 
orderByClause);
+    }
+  }
+}
diff --git 
a/standalone-metastore/metastore-common/src/test/java/org/apache/hadoop/hive/metastore/utils/TestCompactionOrderByValidator.java
 
b/standalone-metastore/metastore-common/src/test/java/org/apache/hadoop/hive/metastore/utils/TestCompactionOrderByValidator.java
new file mode 100644
index 00000000000..874c8ac0a96
--- /dev/null
+++ 
b/standalone-metastore/metastore-common/src/test/java/org/apache/hadoop/hive/metastore/utils/TestCompactionOrderByValidator.java
@@ -0,0 +1,62 @@
+/*
+ * 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.hadoop.hive.metastore.utils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCompactionOrderByValidator {
+
+  @Test
+  public void testNullAndBlankAccepted() {
+    CompactionOrderByValidator.validate(null);
+    CompactionOrderByValidator.validate("");
+    CompactionOrderByValidator.validate("   ");
+  }
+
+  @Test
+  public void testPlainOrderByAccepted() {
+    CompactionOrderByValidator.validate("order by col1");
+    CompactionOrderByValidator.validate("ORDER BY col1 ASC");
+    CompactionOrderByValidator.validate("order by col1 desc, col2");
+    CompactionOrderByValidator.validate("Order By col1 asc nulls first, col2 
DESC NULLS LAST");
+    CompactionOrderByValidator.validate("order by `quoted col`, t.col2");
+    CompactionOrderByValidator.validate("order by 1, 2 desc");
+  }
+
+  @Test
+  public void testInjectionsRejected() {
+    // statement tail smuggling (privileged data copy)
+    assertRejected("order by 1 union all select * from finance.salaries");
+    // empty-rewrite data destruction
+    assertRejected("order by 1 limit 0");
+    // expressions / function calls
+    assertRejected("order by upper(col1)");
+    assertRejected("order by (select 1)");
+    // comment smuggling and statement separators
+    assertRejected("order by col1 -- comment");
+    assertRejected("order by col1; drop table t");
+    // missing ORDER BY prefix
+    assertRejected("col1 asc");
+    assertRejected("union all select 1");
+  }
+
+  private static void assertRejected(String clause) {
+    Assert.assertThrows(IllegalArgumentException.class, () -> 
CompactionOrderByValidator.validate(clause));
+  }
+}
diff --git 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/functions/CompactFunction.java
 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/functions/CompactFunction.java
index ea43a4e74b4..ab01273ad4a 100644
--- 
a/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/functions/CompactFunction.java
+++ 
b/standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/txn/jdbc/functions/CompactFunction.java
@@ -31,6 +31,7 @@
 import 
org.apache.hadoop.hive.metastore.txn.jdbc.commands.InsertCompactionRequestCommand;
 import org.apache.hadoop.hive.metastore.txn.jdbc.MultiDataSourceJdbcResource;
 import org.apache.hadoop.hive.metastore.txn.jdbc.TransactionalFunction;
+import org.apache.hadoop.hive.metastore.utils.CompactionOrderByValidator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
@@ -63,6 +64,15 @@ public CompactFunction(CompactionRequest rqst, long 
openTxnTimeOutMillis, TxnSto
 
   @Override
   public CompactionResponse execute(MultiDataSourceJdbcResource jdbcResource) 
throws MetaException {
+    // CompactionRequest.orderByClause is client-supplied over Thrift and is 
later concatenated into
+    // a query the compaction worker executes with the compaction session's 
privileges. Reject
+    // anything that is not a plain ORDER BY before it enters the queue.
+    try {
+      CompactionOrderByValidator.validate(rqst.getOrderByClause());
+    } catch (IllegalArgumentException e) {
+      throw new MetaException("Invalid orderByClause in compaction request for 
" + rqst.getDbname() + "." +
+          rqst.getTablename() + ": " + e.getMessage());
+    }
     // Put a compaction request in the queue.
     TxnStore.MutexAPI.LockHandle handle = null;
     try {

Reply via email to