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

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


The following commit(s) were added to refs/heads/main by this push:
     new 0bf86e2ee231 Do not print credentials in configuration toString() 
(#29705)
0bf86e2ee231 is described below

commit 0bf86e2ee2316c39fa975f62804d0ebed7a6db3d
Author: dhairya05 <[email protected]>
AuthorDate: Thu Nov 12 19:54:48 2026 +0530

    Do not print credentials in configuration toString() (#29705)
    
    * Do not print credentials in configuration toString()
    
    Four classes write secrets into their toString() in clear:
    
      camel-zendesk     ZendeskConfiguration - password, token, oauthToken
      camel-kubernetes  KubernetesConfiguration - password, oauthToken,
                        caCertData, clientCertData, clientKeyData,
                        clientKeyPassphrase
      camel-smpp        SmppConfiguration - password, httpProxyPassword
      camel-huaweicloud-dms CreateInstanceRequestBody - password,
                        kafkaManagerPassword
    
    Each component's own catalog marks those options secret = true, and
    clientKeyData is a private key. A configuration's toString() reaches a log
    file, a JMX attribute or an exception message without anyone meaning to
    publish it, so marking an option secret has to hold there too.
    
    The credential material is masked with xxxxxx, the mask URISupport already
    uses when it sanitizes a URI, and only when the value is non-null, so a line
    still says whether a credential was configured. What points at the material 
-
    masterUrl, serverUrl, username, systemId, caCertFile, clientKeyFile - stays
    readable, so the output is still useful for diagnosis.
    
    camel-netty-http's HttpPrincipal ("do not display the password") and
    camel-snmp's SnmpEndpoint ("only show address to avoid user and password
    details to be shown") already do this.
    
    * Regen
    
    ---------
    
    Co-authored-by: Guillaume Nodet <[email protected]>
---
 .../dms/models/CreateInstanceRequestBody.java      | 12 ++++-
 .../kubernetes/KubernetesConfiguration.java        | 19 ++++++--
 .../kubernetes/KubernetesConfigurationTest.java    | 56 +++++++++++++++++++++
 .../camel/component/smpp/SmppConfiguration.java    | 12 ++++-
 .../component/smpp/SmppConfigurationTest.java      | 17 +++++++
 .../component/zendesk/ZendeskConfiguration.java    | 10 +++-
 .../zendesk/ZendeskConfigurationTest.java          | 57 ++++++++++++++++++++++
 7 files changed, 174 insertions(+), 9 deletions(-)

diff --git 
a/components/camel-huawei/camel-huaweicloud-dms/src/main/java/org/apache/camel/component/huaweicloud/dms/models/CreateInstanceRequestBody.java
 
b/components/camel-huawei/camel-huaweicloud-dms/src/main/java/org/apache/camel/component/huaweicloud/dms/models/CreateInstanceRequestBody.java
index b094b0c4248e..b1daf9b0e550 100644
--- 
a/components/camel-huawei/camel-huaweicloud-dms/src/main/java/org/apache/camel/component/huaweicloud/dms/models/CreateInstanceRequestBody.java
+++ 
b/components/camel-huawei/camel-huaweicloud-dms/src/main/java/org/apache/camel/component/huaweicloud/dms/models/CreateInstanceRequestBody.java
@@ -505,14 +505,14 @@ public class CreateInstanceRequestBody {
                ", storageSpace=" + storageSpace +
                ", partitionNum=" + partitionNum +
                ", accessUser='" + accessUser + '\'' +
-               ", password='" + password + '\'' +
+               ", password='" + mask(password) + '\'' +
                ", vpcId='" + vpcId + '\'' +
                ", securityGroupId='" + securityGroupId + '\'' +
                ", subnetId='" + subnetId + '\'' +
                ", availableZones=" + availableZones +
                ", productId='" + productId + '\'' +
                ", kafkaManagerUser='" + kafkaManagerUser + '\'' +
-               ", kafkaManagerPassword='" + kafkaManagerPassword + '\'' +
+               ", kafkaManagerPassword='" + mask(kafkaManagerPassword) + '\'' +
                ", maintainBegin='" + maintainBegin + '\'' +
                ", maintainEnd='" + maintainEnd + '\'' +
                ", enablePublicip=" + enablePublicip +
@@ -526,4 +526,12 @@ public class CreateInstanceRequestBody {
                ", enterpriseProjectId='" + enterpriseProjectId + '\'' +
                '}';
     }
+
+    /**
+     * Masks a credential so that logging this request body cannot leak it. 
{@code xxxxxx} is the mask Camel uses when
+     * it sanitizes a URI, and whether the value was set at all is still 
visible.
+     */
+    private static String mask(Object secret) {
+        return secret != null ? "xxxxxx" : "null";
+    }
 }
diff --git 
a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/KubernetesConfiguration.java
 
b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/KubernetesConfiguration.java
index 3b533831f889..678c7caf5c26 100644
--- 
a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/KubernetesConfiguration.java
+++ 
b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/KubernetesConfiguration.java
@@ -499,15 +499,26 @@ public class KubernetesConfiguration implements Cloneable 
{
     public String toString() {
         return "KubernetesConfiguration [masterUrl=" + masterUrl + ", 
category=" + category + ", kubernetesClient="
                + kubernetesClient + ", username=" + username + ", password="
-               + password + ", operation=" + operation + ", apiVersion=" + 
apiVersion + ", caCertData=" + caCertData
+               + mask(password) + ", operation=" + operation + ", apiVersion=" 
+ apiVersion + ", caCertData="
+               + mask(caCertData)
                + ", caCertFile=" + caCertFile + ", clientCertData="
-               + clientCertData + ", clientCertFile=" + clientCertFile + ", 
clientKeyAlgo=" + clientKeyAlgo + ", clientKeyData="
-               + clientKeyData + ", clientKeyFile="
-               + clientKeyFile + ", clientKeyPassphrase=" + 
clientKeyPassphrase + ", oauthToken=" + oauthToken + ", trustCerts="
+               + mask(clientCertData) + ", clientCertFile=" + clientCertFile + 
", clientKeyAlgo=" + clientKeyAlgo
+               + ", clientKeyData="
+               + mask(clientKeyData) + ", clientKeyFile="
+               + clientKeyFile + ", clientKeyPassphrase=" + 
mask(clientKeyPassphrase) + ", oauthToken="
+               + mask(oauthToken) + ", trustCerts="
                + trustCerts + ", namespace=" + namespace
                + ", labelKey=" + labelKey + ", labelValue=" + labelValue + ", 
resourceName=" + resourceName + ", portName="
                + portName + ", dnsDomain=" + dnsDomain + ", poolSize="
                + poolSize + ", connectionTimeout=" + connectionTimeout + "]";
     }
 
+    /**
+     * Masks a credential so that logging or a JMX attribute of this 
configuration cannot leak it. {@code xxxxxx} is the
+     * mask Camel uses when it sanitizes a URI, and whether the value was set 
at all is still visible.
+     */
+    private static String mask(Object secret) {
+        return secret != null ? "xxxxxx" : "null";
+    }
+
 }
diff --git 
a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/KubernetesConfigurationTest.java
 
b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/KubernetesConfigurationTest.java
new file mode 100644
index 000000000000..7003a68f95c7
--- /dev/null
+++ 
b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/KubernetesConfigurationTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.camel.component.kubernetes;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class KubernetesConfigurationTest {
+
+    @Test
+    public void toStringShouldNotLeakCredentials() {
+        KubernetesConfiguration configuration = new KubernetesConfiguration();
+        configuration.setMasterUrl("https://localhost:6443";);
+        configuration.setUsername("someone");
+        configuration.setPassword("s3cret");
+        configuration.setOauthToken("0authT0ken");
+        configuration.setCaCertData("caCertD4ta");
+        configuration.setClientCertData("clientCertD4ta");
+        configuration.setClientKeyData("clientKeyD4ta");
+        configuration.setClientKeyPassphrase("passphr4se");
+        configuration.setClientKeyFile("/etc/kubernetes/client.key");
+
+        String actual = configuration.toString();
+
+        assertFalse(actual.contains("s3cret"), actual);
+        assertFalse(actual.contains("0authT0ken"), actual);
+        assertFalse(actual.contains("caCertD4ta"), actual);
+        assertFalse(actual.contains("clientCertD4ta"), actual);
+        assertFalse(actual.contains("clientKeyD4ta"), actual);
+        assertFalse(actual.contains("passphr4se"), actual);
+
+        assertTrue(actual.contains("password=xxxxxx"), actual);
+        assertTrue(actual.contains("oauthToken=xxxxxx"), actual);
+        assertTrue(actual.contains("clientKeyData=xxxxxx"), actual);
+        // the paths that point at the material, and the cluster being talked 
to, stay readable
+        
assertTrue(actual.contains("clientKeyFile=/etc/kubernetes/client.key"), actual);
+        assertTrue(actual.contains("masterUrl=https://localhost:6443";), 
actual);
+    }
+
+}
diff --git 
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppConfiguration.java
 
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppConfiguration.java
index 271f683b70ce..79d3a0e46e01 100644
--- 
a/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppConfiguration.java
+++ 
b/components/camel-smpp/src/main/java/org/apache/camel/component/smpp/SmppConfiguration.java
@@ -774,7 +774,7 @@ public class SmppConfiguration implements Cloneable {
                + ", hostnameVerification=" + hostnameVerification
                + ", enquireLinkTimer=" + enquireLinkTimer
                + ", host=" + host
-               + ", password=" + password
+               + ", password=" + mask(password)
                + ", port=" + port
                + ", systemId=" + systemId
                + ", systemType=" + systemType
@@ -807,10 +807,18 @@ public class SmppConfiguration implements Cloneable {
                + ", httpProxyHost=" + httpProxyHost
                + ", httpProxyPort=" + httpProxyPort
                + ", httpProxyUsername=" + httpProxyUsername
-               + ", httpProxyPassword=" + httpProxyPassword
+               + ", httpProxyPassword=" + mask(httpProxyPassword)
                + ", splittingPolicy=" + splittingPolicy
                + ", proxyHeaders=" + proxyHeaders
                + ", interfaceVersion=" + interfaceVersion
                + "]";
     }
+
+    /**
+     * Masks a credential so that logging or a JMX attribute of this 
configuration cannot leak it. {@code xxxxxx} is the
+     * mask Camel uses when it sanitizes a URI, and whether the value was set 
at all is still visible.
+     */
+    private static String mask(Object secret) {
+        return secret != null ? "xxxxxx" : "null";
+    }
 }
diff --git 
a/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppConfigurationTest.java
 
b/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppConfigurationTest.java
index 46677b40e258..c7461b0ce228 100644
--- 
a/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppConfigurationTest.java
+++ 
b/components/camel-smpp/src/test/java/org/apache/camel/component/smpp/SmppConfigurationTest.java
@@ -31,8 +31,10 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * JUnit test class for 
<code>org.apache.camel.component.smpp.SmppConfiguration</code>
@@ -240,6 +242,21 @@ public class SmppConfigurationTest {
         assertEquals(expected, configuration.toString());
     }
 
+    @Test
+    public void toStringShouldNotLeakCredentials() {
+        configuration.setPassword("s3cret");
+        configuration.setHttpProxyPassword("proxyS3cret");
+
+        String actual = configuration.toString();
+
+        assertFalse(actual.contains("s3cret"), actual);
+        assertFalse(actual.contains("proxyS3cret"), actual);
+        assertTrue(actual.contains("password=xxxxxx"), actual);
+        assertTrue(actual.contains("httpProxyPassword=xxxxxx"), actual);
+        // the systemId is not a credential, and stays readable
+        assertTrue(actual.contains("systemId=smppclient"), actual);
+    }
+
     private void setNoneDefaultValues(SmppConfiguration config) {
         config.setDestAddr("1919");
         config.setDestAddrNpi(NumberingPlanIndicator.NATIONAL.value());
diff --git 
a/components/camel-zendesk/src/main/java/org/apache/camel/component/zendesk/ZendeskConfiguration.java
 
b/components/camel-zendesk/src/main/java/org/apache/camel/component/zendesk/ZendeskConfiguration.java
index d664e7281742..29f49523f0ec 100644
--- 
a/components/camel-zendesk/src/main/java/org/apache/camel/component/zendesk/ZendeskConfiguration.java
+++ 
b/components/camel-zendesk/src/main/java/org/apache/camel/component/zendesk/ZendeskConfiguration.java
@@ -147,7 +147,15 @@ public class ZendeskConfiguration {
     public String toString() {
         return String.format(
                 "ZendeskConfiguration:[methodName=%s, serverUrl=%s, 
username=%s, password=%s, token=%s, oauthToken=%s]",
-                methodName, serverUrl, username, password, token, oauthToken);
+                methodName, serverUrl, username, mask(password), mask(token), 
mask(oauthToken));
+    }
+
+    /**
+     * Masks a credential so that logging or a JMX attribute of this 
configuration cannot leak it. {@code xxxxxx} is the
+     * mask Camel uses when it sanitizes a URI, and whether the value was set 
at all is still visible.
+     */
+    private static String mask(Object secret) {
+        return secret != null ? "xxxxxx" : "null";
     }
 
     @Override
diff --git 
a/components/camel-zendesk/src/test/java/org/apache/camel/component/zendesk/ZendeskConfigurationTest.java
 
b/components/camel-zendesk/src/test/java/org/apache/camel/component/zendesk/ZendeskConfigurationTest.java
new file mode 100644
index 000000000000..965df030c221
--- /dev/null
+++ 
b/components/camel-zendesk/src/test/java/org/apache/camel/component/zendesk/ZendeskConfigurationTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.camel.component.zendesk;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ZendeskConfigurationTest {
+
+    @Test
+    public void toStringShouldNotLeakCredentials() {
+        ZendeskConfiguration configuration = new ZendeskConfiguration();
+        configuration.setServerUrl("https://example.zendesk.com";);
+        configuration.setUsername("[email protected]");
+        configuration.setPassword("s3cret");
+        configuration.setToken("t0ken");
+        configuration.setOauthToken("0authT0ken");
+
+        String actual = configuration.toString();
+
+        assertFalse(actual.contains("s3cret"), actual);
+        assertFalse(actual.contains("t0ken"), actual);
+        assertFalse(actual.contains("0authT0ken"), actual);
+        assertTrue(actual.contains("password=xxxxxx"), actual);
+        assertTrue(actual.contains("token=xxxxxx"), actual);
+        assertTrue(actual.contains("oauthToken=xxxxxx"), actual);
+        // what the configuration points at stays readable, so the line is 
still useful
+        assertTrue(actual.contains("serverUrl=https://example.zendesk.com";), 
actual);
+    }
+
+    @Test
+    public void toStringShouldStillShowThatACredentialIsUnset() {
+        ZendeskConfiguration configuration = new ZendeskConfiguration();
+
+        String actual = configuration.toString();
+
+        assertTrue(actual.contains("password=null"), actual);
+        assertTrue(actual.contains("token=null"), actual);
+    }
+
+}

Reply via email to