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

rmaucher pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 9f301dd5b78470226d8601c2c2c7418a341672bb
Author: opencode <[email protected]>
AuthorDate: Thu Nov 5 13:46:29 2026 +0100

    Mask inline credentials on the DataSource bean surface
    
    DataSourceProxy doubles as the data source's bean/JMX surface, but 
getUrl(), getConnectionProperties() and getJdbcInterceptors() returned the 
configured strings verbatim while the pool MBean masked the same credentials. A 
client reading the DataSource MBean received a password typed into the URL, 
connectionProperties or an interceptor definition in cleartext.
    
    Apply the same masking to the three getters, leaving raw access through 
getPoolProperties() and the setters. Add a regression test for all three bean 
getters and for the raw programmatic accessor.
---
 .../apache/tomcat/jdbc/pool/DataSourceProxy.java   | 22 +++++++++++++--
 .../jdbc/pool/jmx/TestJmxCredentialAttributes.java | 33 ++++++++++++++++++++++
 2 files changed, 52 insertions(+), 3 deletions(-)

diff --git 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
index 96df1c2bf4..9d4875c1b2 100644
--- 
a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
+++ 
b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
@@ -1140,9 +1140,15 @@ public class DataSourceProxy implements 
PoolConfiguration {
     }
 
 
+    /**
+     * Returns the connection properties with any credential they carry masked 
out, so the data source's own bean/JMX
+     * surface does not expose a password in cleartext. The write is not 
masked: {@link #setConnectionProperties(String)}
+     * takes the string as it is meant to reach the driver. Use {@link 
#getPoolProperties()} for the raw value.
+     * {@inheritDoc}
+     */
     @Override
     public String getConnectionProperties() {
-        return getPoolProperties().getConnectionProperties();
+        return 
PoolProperties.maskCredentials(getPoolProperties().getConnectionProperties());
     }
 
 
@@ -1195,9 +1201,14 @@ public class DataSourceProxy implements 
PoolConfiguration {
     }
 
 
+    /**
+     * Returns the interceptor configuration with any credential its 
definitions carry masked out, so the data source's
+     * own bean/JMX surface does not expose a password in cleartext. Use 
{@link #getPoolProperties()} for the raw value.
+     * {@inheritDoc}
+     */
     @Override
     public String getJdbcInterceptors() {
-        return getPoolProperties().getJdbcInterceptors();
+        return 
PoolProperties.maskInterceptorCredentials(getPoolProperties().getJdbcInterceptors());
     }
 
 
@@ -1269,9 +1280,14 @@ public class DataSourceProxy implements 
PoolConfiguration {
     }
 
 
+    /**
+     * Returns the JDBC URL with any credential it carries masked out, so the 
data source's own bean/JMX surface does
+     * not expose a password in cleartext. The write is not masked: {@link 
#setUrl(String)} takes the URL as it is meant
+     * to reach the driver. Use {@link #getPoolProperties()} for the raw 
value. {@inheritDoc}
+     */
     @Override
     public String getUrl() {
-        return getPoolProperties().getUrl();
+        return PoolProperties.maskCredentials(getPoolProperties().getUrl());
     }
 
 
diff --git 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/jmx/TestJmxCredentialAttributes.java
 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/jmx/TestJmxCredentialAttributes.java
index d75b177c5d..0c88f1c5bc 100644
--- 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/jmx/TestJmxCredentialAttributes.java
+++ 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/jmx/TestJmxCredentialAttributes.java
@@ -201,6 +201,39 @@ class TestJmxCredentialAttributes extends JdbcPoolBaseTest 
{
         }
     }
 
+    @Test
+    void theDataSourceBeanGettersNeverCarryTheInlineCredentials() throws 
Exception {
+        DataSource ds = poolWithInlineCredentials();
+        ds.setJdbcInterceptors("ResetAbandonedTimer(password=" + 
INTERCEPTOR_SECRET + ")");
+        try {
+            Assertions.assertFalse(ds.getUrl().contains(URL_SECRET),
+                    "the data source bean getter must mask a credential typed 
into the URL");
+            Assertions.assertEquals(getUrl() + ";password=" + MASK, 
ds.getUrl(),
+                    "the bean getter must show the same mask as the pool 
MBean");
+            
Assertions.assertFalse(ds.getConnectionProperties().contains(PROPERTIES_SECRET),
+                    "the data source bean getter must mask a credential in the 
connection properties");
+            Assertions.assertEquals("password=" + MASK + ";a=b", 
ds.getConnectionProperties(),
+                    "the bean getter must keep the non-credential pairs 
readable");
+            
Assertions.assertFalse(ds.getJdbcInterceptors().contains(INTERCEPTOR_SECRET),
+                    "the data source bean getter must mask a credential in an 
interceptor definition");
+            Assertions.assertEquals("ResetAbandonedTimer(password=" + MASK + 
")",
+                    ds.getJdbcInterceptors(),
+                    "the bean getter must keep the interceptor name and 
property readable");
+
+            Assertions.assertEquals(getUrl() + ";password=" + URL_SECRET,
+                    ds.getPoolProperties().getUrl(),
+                    "the programmatic configuration accessor must still return 
the raw URL");
+            Assertions.assertEquals("password=" + PROPERTIES_SECRET + ";a=b",
+                    ds.getPoolProperties().getConnectionProperties(),
+                    "the programmatic configuration accessor must still return 
the raw connection properties");
+            Assertions.assertEquals("ResetAbandonedTimer(password=" + 
INTERCEPTOR_SECRET + ")",
+                    ds.getPoolProperties().getJdbcInterceptors(),
+                    "the programmatic configuration accessor must still return 
the raw interceptor string");
+        } finally {
+            ds.close();
+        }
+    }
+
     @Test
     void 
theConnectionPropertiesAttributeOfThePoolMBeanNeverCarriesTheCredentialTypedIntoThem()
             throws Exception {


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

Reply via email to