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]