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

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

commit ddf96d912eaae47d0f083177bcea1f69017ada0a
Author: opencode <[email protected]>
AuthorDate: Mon Oct 26 15:47:59 2026 +0100

    Mask the password in DataSourceProxy.getDbProperties()
    
    setPassword() stores the password into the shared database properties,
    and getDbProperties() handed out that live object unsanitized. DataSource
    is designed to be registered as its own MBean/bean, so the plaintext
    password reached the DbProperties bean/JMX view and any reflective
    exporter or direct caller, bypassing the module's deliberate masking: the
    same class masks getPassword() with a fixed placeholder, and the pool's
    own MBean has always exposed this view as a copy without the password.
    
    The proxy getter now returns that same password-free copy. The connect
    path is unaffected - it clones the properties from the PoolConfiguration
    directly - but callers that mutated the returned object expect that it no
    longer feeds back into the configuration.
---
 .../apache/tomcat/jdbc/pool/DataSourceProxy.java   | 13 ++++++++-
 .../tomcat/jdbc/pool/jmx/TestJmxMonitor.java       | 33 ++++++++++++++++++++++
 2 files changed, 45 insertions(+), 1 deletion(-)

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 7954be2b99..d51cd1438d 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
@@ -863,9 +863,20 @@ public class DataSourceProxy implements PoolConfiguration {
     }
 
 
+    /**
+     * Returns a copy of the database properties with the password removed.
+     * This object doubles as its own JMX/bean surface and
+     * {@link #setPassword(String)} stores the password into the database
+     * properties, so returning them as they are would expose the plaintext
+     * password through a getter while {@link #getPassword()} is explicitly
+     * masked - the same protection the pool's own MBean
+     * ({@link 
org.apache.tomcat.jdbc.pool.jmx.ConnectionPool#getDbProperties()})
+     * already applies.
+     * {@inheritDoc}
+     */
     @Override
     public Properties getDbProperties() {
-        return getPoolProperties().getDbProperties();
+        return 
PoolUtilities.cloneWithoutPassword(getPoolProperties().getDbProperties());
     }
 
 
diff --git 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/jmx/TestJmxMonitor.java
 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/jmx/TestJmxMonitor.java
index 85419e32d0..877de0b96d 100644
--- 
a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/jmx/TestJmxMonitor.java
+++ 
b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/jmx/TestJmxMonitor.java
@@ -240,6 +240,39 @@ class TestJmxMonitor extends JdbcPoolBaseTest {
         }
     }
 
+    @Test
+    void theDataSourceBeanSurfaceNeverExposesThePassword() throws Exception {
+        final String secret = "s3cr3t-pool-password";
+        DataSource ds = newDataSource();
+        ds.createPool(); // connect with the fixture credentials first
+        // the pool is already connected; this only records what a real
+        // configuration holds
+        ds.setPassword(secret);
+        ds.setConnectionProperties("appconfigured=yes");
+
+        // The DataSource object doubles as its own MBean/bean surface:
+        // the modeler and reflective exporters read these public getters.
+        // The deliberately masked getPassword() must not be bypassed by a
+        // sibling getter that still hands out the live db-properties.
+        Properties dbProperties = ds.getDbProperties();
+        Assertions.assertNotNull(dbProperties);
+        for (String name : dbProperties.stringPropertyNames()) {
+            
Assertions.assertFalse(secret.equals(dbProperties.getProperty(name)),
+                    "the bean view of the driver properties must not carry the"
+                            + " password, property " + name);
+        }
+        Assertions.assertEquals("yes", 
dbProperties.getProperty("appconfigured"),
+                "the sanitization must still show every other driver 
property");
+        // The returned view is a copy: writing through it cannot smuggle
+        // values back into the pool configuration.
+        dbProperties.setProperty("injected", "through-the-view");
+        
Assertions.assertNull(ds.getPoolProperties().getDbProperties().getProperty("injected"),
+                "the exposed db-properties must be a view, not the live 
object");
+        // and the pool must still authenticate - the copy is display-side
+        // only, the connect path keeps its own credentials
+        Assertions.assertDoesNotThrow(() -> ds.getConnection().close());
+    }
+
     @Test
     void closingThePoolUnregistersItsMBean() throws Exception {
         MBeanServer server = ManagementFactory.getPlatformMBeanServer();


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

Reply via email to