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

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


The following commit(s) were added to refs/heads/master by this push:
     new fbc34d7e7b2 Mask more credential keys in Obfuscator's defaults (#19884)
fbc34d7e7b2 is described below

commit fbc34d7e7b28e7bb2ff72a35fb178e945c0ba3ac
Author: Dylan Dreyer <[email protected]>
AuthorDate: Thu Oct 15 21:27:20 2026 -0700

    Mask more credential keys in Obfuscator's defaults (#19884)
    
    ---------
    
    Co-authored-by: Claude Opus 5.5 <[email protected]>
---
 .../org/apache/pinot/spi/utils/Obfuscator.java     | 21 +++++++++----
 .../org/apache/pinot/spi/utils/ObfuscatorTest.java | 35 ++++++++++++++++++++++
 2 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/Obfuscator.java 
b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/Obfuscator.java
index ae5b09ac5ed..ef40c677266 100644
--- a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/Obfuscator.java
+++ b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/Obfuscator.java
@@ -37,7 +37,7 @@ import java.util.stream.Stream;
 /// ```
 /// {
 ///   "type": "sample object",
-///   "nestedCredentials": {
+///   "connection": {
 ///     "user": "admin",
 ///     "password": "verysecret"
 ///   }
@@ -49,7 +49,7 @@ import java.util.stream.Stream;
 /// ```
 /// {
 ///   "type": "sample object",
-///   "nestedCredentials": {
+///   "connection": {
 ///     "user": "admin",
 ///     "password": "*****"
 ///   }
@@ -58,16 +58,27 @@ import java.util.stream.Stream;
 public final class Obfuscator {
 
   private static final String DEFAULT_MASKED_VALUE = "*****";
+  /// Suffix-anchored, so that a key is masked for what it ends in and a 
structural name such as
+  /// `pinot.broker.auth.provider.class` stays readable. Beyond the generic 
credential words, this covers
+  /// credentials whose key does not end in one: the AWS access and secret key 
pair, dotted or not (as in
+  /// `input.fs.prop.accessKey`, `fs.s3a.access.key` and `fs.s3a.secret.key`), 
the Azure storage
+  /// `account.key`, Kafka's `sasl.jaas.config`, Confluent's 
`basic.auth.user.info` (a literal
+  /// `user:password`), and inline private or service-account key material. 
Key *file* names are not
+  /// masked, because their values are paths rather than secrets.
   private static final List<Pattern> DEFAULT_PATTERNS =
-      Stream.of("(?i).*secret$", "(?i).*secret[\\s_-]*key$", 
"(?i).*api[\\s_-]*key$", "(?i).*password$",
-              "(?i).*keytab$", "(?i).*token$")
+      Stream.of("(?i).*secret$", "(?i).*secret[\\s._-]*key$", 
"(?i).*api[\\s._-]*key$", "(?i).*password$",
+              "(?i).*passwd$", "(?i).*passphrase$", "(?i).*keytab$", 
"(?i).*token$", "(?i).*credentials?$",
+              "(?i).*access[\\s._-]*key([\\s._-]*id)?$", 
"(?i).*private[\\s._-]*key([\\s._-]*id)?$",
+              "(?i).*account[\\s._-]*key$", "(?i).*jaas[\\s._-]*config$", 
"(?i).*user[\\s._-]*info$",
+              "(?i).*json[\\s._-]*key$", 
"(?i).*key[\\s._-]*content([\\s._-]*base64)?$")
           .map(Pattern::compile).collect(Collectors.toList());
   public static final Obfuscator DEFAULT = new Obfuscator();
 
   private final String _maskedValue;
   private final List<Pattern> _patterns;
 
-  /// Obfuscator with default behavior matching (ignore case) "secret", 
"password", and "token" suffixes. Masks any
+  /// Obfuscator with default behavior matching (ignore case) credential 
suffixes such as "secret", "password",
+  /// "token" and "accessKey" - see [#DEFAULT_PATTERNS]. Masks any
   /// values with '\*\*\*\*\*'
   public Obfuscator() {
     this(DEFAULT_MASKED_VALUE, DEFAULT_PATTERNS);
diff --git 
a/pinot-spi/src/test/java/org/apache/pinot/spi/utils/ObfuscatorTest.java 
b/pinot-spi/src/test/java/org/apache/pinot/spi/utils/ObfuscatorTest.java
index 77037a21d31..d7e0318f892 100644
--- a/pinot-spi/src/test/java/org/apache/pinot/spi/utils/ObfuscatorTest.java
+++ b/pinot-spi/src/test/java/org/apache/pinot/spi/utils/ObfuscatorTest.java
@@ -206,6 +206,41 @@ public class ObfuscatorTest {
     Assert.assertFalse(output.contains(SECRET));
   }
 
+  @Test
+  public void testMasksCredentialsWithoutAGenericSuffix() {
+    // Each of these is a credential whose key does not end in "secret", 
"password" or "token".
+    List<String> keys = List.of("accessKey", "accessKeyId", 
"input.fs.prop.accessKey", "fs.s3a.access.key",
+        "fs.s3a.secret.key", "secret.key", "api.key", "awsAccessKey", 
"restAccessKeyId", "storageSecretAccessKey",
+        "fs.azure.account.key", "storageAccountKey", "sasl.jaas.config",
+        "stream.kafka.decoder.prop.basic.auth.user.info", "userInfo", 
"privateKey", "private_key_id",
+        "sql.snowflake.privateKey", "sql.bigquery.jsonKey", 
"delta.ingestion.table.gcs.keyContentBase64",
+        "credentials", "gcs.credential", "passwd", "ssl.key.passphrase");
+    Map<String, Object> configs = new HashMap<>();
+    keys.forEach(key -> configs.put(key, SECRET));
+    configs.put("value", VALUE);
+
+    JsonNode output = _obfuscator.toJson(configs);
+
+    keys.forEach(key -> Assert.assertEquals(output.get(key).asText(), "*****", 
key));
+    Assert.assertEquals(output.get("value").asText(), VALUE);
+  }
+
+  @Test
+  public void testKeepsStructuralNames() {
+    // Names that only mention a credential word, which an operator reads to 
debug authentication.
+    List<String> keys = List.of("pinot.broker.auth.provider.class", 
"fs.s3a.aws.credentials.provider",
+        "stream.kafka.decoder.prop.basic.auth.credentials.source", 
"primaryKeyColumns", "partitionKey",
+        "accessKeyRotationDays", "tokenUri", "jaas.login.module", 
"sortedColumn",
+        // Key file locations are paths, not secrets.
+        "sslKeyFile", "publicKeyFile", 
"fs.gs.auth.service.account.json.keyfile");
+    Map<String, Object> configs = new HashMap<>();
+    keys.forEach(key -> configs.put(key, VALUE));
+
+    JsonNode output = _obfuscator.toJson(configs);
+
+    keys.forEach(key -> Assert.assertEquals(output.get(key).asText(), VALUE, 
key));
+  }
+
   private static Map<String, Object> createEntry(String key, String value) {
     Map<String, Object> map = new HashMap<>();
     map.put(key, value);


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

Reply via email to