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]