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

zhouyuan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gluten.git


The following commit(s) were added to refs/heads/main by this push:
     new 63bd7c28e2 [VL] Fix password leak in debug-mode config logging (#12606)
63bd7c28e2 is described below

commit 63bd7c28e2a19ef5e2c524b5dbdd703e02711b6c
Author: Yuan <[email protected]>
AuthorDate: Fri Jul 24 09:34:47 2026 -0700

    [VL] Fix password leak in debug-mode config logging (#12606)
    
    * [CORE] Fix password leak in debug-mode config logging
    
    printConfig() already had redaction logic keyed on spark.redaction.regex,
    but when that config key was absent (the common case) getRedactionRegex()
    returned std::nullopt and every config value — including passwords,
    tokens, and secrets — was logged in plain text.
    
    This patch adds a hard-coded default redaction pattern to guard on this case
    
    
    ---------
    
    Signed-off-by: Yuan <[email protected]>
---
 cpp/core/config/GlutenConfig.cc   |  11 ++--
 cpp/core/tests/CMakeLists.txt     |   1 +
 cpp/core/tests/PrintConfigTest.cc | 108 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 116 insertions(+), 4 deletions(-)

diff --git a/cpp/core/config/GlutenConfig.cc b/cpp/core/config/GlutenConfig.cc
index eb98f6bb90..6ad7c24f6d 100644
--- a/cpp/core/config/GlutenConfig.cc
+++ b/cpp/core/config/GlutenConfig.cc
@@ -19,19 +19,22 @@
 
 #include <boost/regex.hpp>
 #include <jni.h>
-#include <optional>
 #include "compute/ProtobufUtils.h"
 #include "config.pb.h"
 #include "jni/JniError.h"
 
 namespace {
 
-std::optional<boost::regex> getRedactionRegex(const 
std::unordered_map<std::string, std::string>& conf) {
+// Mirrors Spark's built-in default for spark.redaction.regex.
+// See org.apache.spark.internal.config.SECRET_REDACTION_PATTERN.
+constexpr std::string_view kDefaultRedactionRegex = 
"(?i)secret|password|token|access[.]?key";
+
+boost::regex getRedactionRegex(const std::unordered_map<std::string, 
std::string>& conf) {
   auto it = conf.find(gluten::kSparkRedactionRegex);
   if (it != conf.end()) {
     return boost::regex(it->second);
   }
-  return std::nullopt;
+  return boost::regex(kDefaultRedactionRegex.data());
 }
 } // namespace
 
@@ -67,7 +70,7 @@ std::string printConfig(const std::unordered_map<std::string, 
std::string>& conf
   auto redactionRegex = getRedactionRegex(conf);
 
   for (const auto& [k, v] : conf) {
-    if (redactionRegex && boost::regex_match(k, *redactionRegex)) {
+    if (boost::regex_search(k, redactionRegex)) {
       oss << " [" << k << ", " << kSparkRedactionString << "]\n";
     } else {
       oss << " [" << k << ", " << v << "]\n";
diff --git a/cpp/core/tests/CMakeLists.txt b/cpp/core/tests/CMakeLists.txt
index 33026948ce..c022f37aea 100644
--- a/cpp/core/tests/CMakeLists.txt
+++ b/cpp/core/tests/CMakeLists.txt
@@ -17,3 +17,4 @@ add_test_case(round_robin_partitioner_test SOURCES 
RoundRobinPartitionerTest.cc)
 add_test_case(object_store_test SOURCES ObjectStoreTest.cc)
 add_test_case(memory_allocator_test SOURCES MemoryAllocatorTest.cc)
 add_test_case(ffor_codec_test SOURCES FForCodecTest.cc)
+add_test_case(print_config_test SOURCES PrintConfigTest.cc)
diff --git a/cpp/core/tests/PrintConfigTest.cc 
b/cpp/core/tests/PrintConfigTest.cc
new file mode 100644
index 0000000000..c1e42aa37b
--- /dev/null
+++ b/cpp/core/tests/PrintConfigTest.cc
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include "config/GlutenConfig.h"
+
+namespace gluten {
+
+// Helpers to check whether a key's value is redacted or plain in the output.
+static bool isRedacted(const std::string& output, const std::string& key) {
+  // Look for the pattern " [<key>, *********(redacted)]"
+  return output.find("[" + key + ", " + kSparkRedactionString + "]") != 
std::string::npos;
+}
+
+static bool isPlain(const std::string& output, const std::string& key, const 
std::string& value) {
+  return output.find("[" + key + ", " + value + "]") != std::string::npos;
+}
+
+// ── Default-redaction tests (no spark.redaction.regex in config) 
─────────────
+
+TEST(PrintConfig, DefaultRedactsPassword) {
+  std::unordered_map<std::string, std::string> conf = {
+      {"spark.datasource.jdbc.password", "secret123"},
+  };
+  auto out = printConfig(conf);
+  EXPECT_TRUE(isRedacted(out, "spark.datasource.jdbc.password"));
+}
+
+TEST(PrintConfig, DefaultRedactsSecret) {
+  std::unordered_map<std::string, std::string> conf = {
+      {"my.secret.value", "topsecret"},
+  };
+  auto out = printConfig(conf);
+  EXPECT_TRUE(isRedacted(out, "my.secret.value"));
+}
+
+TEST(PrintConfig, DefaultRedactsToken) {
+  std::unordered_map<std::string, std::string> conf = {
+      {"spark.hadoop.fs.s3a.access.token", "tok_abc123"},
+  };
+  auto out = printConfig(conf);
+  EXPECT_TRUE(isRedacted(out, "spark.hadoop.fs.s3a.access.token"));
+}
+
+TEST(PrintConfig, DefaultRedactsAccessKey) {
+  std::unordered_map<std::string, std::string> conf = {
+      {"spark.hadoop.fs.s3a.accesskey", "AKIAIOSFODNN7EXAMPLE"},
+  };
+  auto out = printConfig(conf);
+  EXPECT_TRUE(isRedacted(out, "spark.hadoop.fs.s3a.accesskey"));
+}
+
+TEST(PrintConfig, DefaultDoesNotRedactSafeKey) {
+  std::unordered_map<std::string, std::string> conf = {
+      {"spark.sql.session.timeZone", "UTC"},
+  };
+  auto out = printConfig(conf);
+  EXPECT_TRUE(isPlain(out, "spark.sql.session.timeZone", "UTC"));
+}
+
+// ── Custom-regex tests (spark.redaction.regex present) 
───────────────────────
+
+TEST(PrintConfig, CustomRegexRedactsMatchingKey) {
+  std::unordered_map<std::string, std::string> conf = {
+      {kSparkRedactionRegex, "supersensitive"},
+      {"my.supersensitive.config", "very_private"},
+      {"spark.sql.session.timeZone", "UTC"},
+  };
+  auto out = printConfig(conf);
+  EXPECT_TRUE(isRedacted(out, "my.supersensitive.config"));
+  EXPECT_TRUE(isPlain(out, "spark.sql.session.timeZone", "UTC"));
+}
+
+TEST(PrintConfig, CustomRegexOverridesDefault) {
+  // When spark.redaction.regex is set, only keys matching it are redacted.
+  // A key that would match the default pattern (e.g. "password") but NOT the
+  // custom regex must be printed in plain text.
+  std::unordered_map<std::string, std::string> conf = {
+      {kSparkRedactionRegex, "supersensitive"},
+      {"spark.datasource.jdbc.password", "pass123"},
+  };
+  auto out = printConfig(conf);
+  EXPECT_TRUE(isPlain(out, "spark.datasource.jdbc.password", "pass123"));
+}
+
+TEST(PrintConfig, CaseInsensitiveDefaultRedaction) {
+  std::unordered_map<std::string, std::string> conf = {
+      {"spark.my.PASSWORD", "uppercase_pw"},
+  };
+  auto out = printConfig(conf);
+  EXPECT_TRUE(isRedacted(out, "spark.my.PASSWORD"));
+}
+
+} // namespace gluten


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to