VYPR
Moderate severityNVD Advisory· Published Aug 2, 2021· Updated Sep 17, 2024

MongoDB Rust Driver may publish events containing authentication-related data to a connection pool event listener configured by an application

CVE-2021-20332

Description

Specific MongoDB Rust Driver versions can include credentials used by the connection pool to authenticate connections in the monitoring event that is emitted when the pool is created. The user's logging infrastructure could then potentially ingest these events and unexpectedly leak the credentials. Note that such monitoring is not enabled by default. This issue affects MongoDB Rust Driver version 2.0.0-alpha, MongoDB Rust Driver version 2.0.0-alpha1 and MongoDB Rust Driver version 1.0.0 through to and including 1.2.1

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

MongoDB Rust Driver versions 1.0.0–1.2.1 and 2.0.0-alpha/alpha1 expose authentication credentials in connection pool monitoring events, risking credential leakage if logging is enabled.

Vulnerability

Vulnerability in MongoDB Rust Driver versions 1.0.0 through 1.2.1 (inclusive) and prerelease versions 2.0.0-alpha and 2.0.0-alpha1: the ConnectionPoolOptions struct stores credential as a public field, and this credential is included in the CMAP monitoring event emitted when the pool is created. The driver's connection pool event handler may expose these credentials to user-configured logging infrastructure if monitoring is enabled. Monitoring is not enabled by default [1][2][4].

Exploitation

An attacker requires access to the application's logging infrastructure where connection pool events are ingested. The attacker must also have privileges to read log output (e.g., local file access, log aggregation systems). No network position or authentication to MongoDB is required; the credentials leak via the monitoring path when a user enables pool event listeners and logs the events. The sequence is: (1) user enables CMAP event monitoring, (2) pool creation emits a PoolCreatedEvent containing the credential, (3) user's logger records the event, (4) attacker accesses the log [2][4].

Impact

Successful exploitation leads to unauthorized disclosure of MongoDB authentication credentials (CWE-200). An attacker who obtains the credentials can authenticate to the MongoDB deployment as the compromised user, potentially gaining read/write access to databases. The CVSS 3.1 vector (AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N) indicates high confidentiality impact, but only with local access and high privileges [2].

Mitigation

The fix was implemented in commit 9e8782b, which changes ConnectionPoolOptions fields (including credential) from pub to pub(crate) and introduces a separate type for monitoring events without credentials [3]. The fix is included in MongoDB Rust Driver version 2.0.0 (released after alpha1). Users of affected versions (1.0.0–1.2.1) must upgrade to 2.0.0 or later. Upgrading from 1.x to 2.x requires migration per the release notes [1]. Workaround: disable CMAP event logging if monitoring is not required. The v1.x branch has reached end of life and receives no further updates [1].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
mongodbcrates.io
>= 1.0.0, < 2.0.0-beta2.0.0-beta

Affected products

2
  • ghsa-coords
    Range: >= 1.0.0, < 2.0.0-beta
  • MongoDB Inc./MongoDB Rust Driverv5
    Range: 2.0.0-alpha

Patches

1
9e8782b1bb11

RUST-591 Use separate ConnectionPoolOptions types for monitoring and internal usage (#326)

https://github.com/mongodb/mongo-rust-driverPatrick FreedApr 28, 2021via ghsa
5 files changed · +122 36
  • src/cmap/mod.rs+1 1 modified
    @@ -78,7 +78,7 @@ impl ConnectionPool {
             if let Some(ref handler) = event_handler {
                 handler.handle_pool_created_event(PoolCreatedEvent {
                     address: address.clone(),
    -                options,
    +                options: options.map(|o| o.to_event_options()),
                 });
             };
     
    
  • src/cmap/options.rs+46 28 modified
    @@ -7,44 +7,43 @@ use typed_builder::TypedBuilder;
     use crate::{
         bson_util,
         client::{auth::Credential, options::ServerApi},
    -    event::cmap::CmapEventHandler,
    +    event::cmap::{CmapEventHandler, ConnectionPoolOptions as EventOptions},
         options::{ClientOptions, DriverInfo, StreamAddress, TlsOptions},
     };
     
    -/// Contains the options for creating a connection pool. While these options are specified at the
    -/// client-level, `ConnectionPoolOptions` is exposed for the purpose of CMAP event handling.
    +/// Contains the options for creating a connection pool.
     #[derive(Clone, Default, Deserialize, TypedBuilder, Derivative)]
     #[derivative(Debug, PartialEq)]
     #[builder(field_defaults(default, setter(strip_option)))]
     #[serde(rename_all = "camelCase")]
    -pub struct ConnectionPoolOptions {
    +pub(crate) struct ConnectionPoolOptions {
         /// The application name specified by the user. This is sent to the server as part of the
         /// handshake that each connection makes when it's created.
    -    pub app_name: Option<String>,
    +    pub(crate) app_name: Option<String>,
     
    -    /// The connect timeout passed to each underlying TcpStream when attemtping to connect to the
    +    /// The connect timeout passed to each underlying TcpStream when attempting to connect to the
         /// server.
         #[serde(skip)]
    -    pub connect_timeout: Option<Duration>,
    +    pub(crate) connect_timeout: Option<Duration>,
     
         /// The credential to use for authenticating connections in this pool.
         #[serde(skip)]
    -    pub credential: Option<Credential>,
    +    pub(crate) credential: Option<Credential>,
     
         /// Extra information to append to the driver version in the metadata of the handshake with the
         /// server. This should be used by libraries wrapping the driver, e.g. ODMs.
         #[serde(skip)]
    -    pub driver_info: Option<DriverInfo>,
    +    pub(crate) driver_info: Option<DriverInfo>,
     
         /// Processes all events generated by the pool.
         #[derivative(Debug = "ignore", PartialEq = "ignore")]
         #[serde(skip)]
    -    pub event_handler: Option<Arc<dyn CmapEventHandler>>,
    +    pub(crate) event_handler: Option<Arc<dyn CmapEventHandler>>,
     
         /// How often the background thread performs its maintenance (e.g. ensure minPoolSize).
         #[cfg(test)]
         #[serde(skip)]
    -    pub maintenance_frequency: Option<Duration>,
    +    pub(crate) maintenance_frequency: Option<Duration>,
     
         /// Connections that have been ready for usage in the pool for longer than `max_idle_time` will
         /// not be used.
    @@ -53,20 +52,20 @@ pub struct ConnectionPoolOptions {
         #[serde(rename = "maxIdleTimeMS")]
         #[serde(default)]
         #[serde(deserialize_with = "bson_util::deserialize_duration_from_u64_millis")]
    -    pub max_idle_time: Option<Duration>,
    +    pub(crate) max_idle_time: Option<Duration>,
     
         /// The maximum number of connections that the pool can have at a given time. This includes
         /// connections which are currently checked out of the pool.
         ///
         /// The default is 100.
    -    pub max_pool_size: Option<u32>,
    +    pub(crate) max_pool_size: Option<u32>,
     
         /// The minimum number of connections that the pool can have at a given time. This includes
         /// connections which are currently checked out of the pool. If fewer than `min_pool_size`
         /// connections are in the pool, connections will be added to the pool in the background.
         ///
         /// The default is that no minimum is enforced
    -    pub min_pool_size: Option<u32>,
    +    pub(crate) min_pool_size: Option<u32>,
     
         /// Whether to start the pool as "ready" or not.
         /// For tests only.
    @@ -85,7 +84,7 @@ pub struct ConnectionPoolOptions {
         ///
         /// The default is not to use TLS for connections.
         #[serde(skip)]
    -    pub tls_options: Option<TlsOptions>,
    +    pub(crate) tls_options: Option<TlsOptions>,
     
         /// Rather than wait indefinitely for a connection to become available, instead return an error
         /// after the given duration.
    @@ -94,23 +93,42 @@ pub struct ConnectionPoolOptions {
         #[serde(rename = "waitQueueTimeoutMS")]
         #[serde(default)]
         #[serde(deserialize_with = "bson_util::deserialize_duration_from_u64_millis")]
    -    pub wait_queue_timeout: Option<Duration>,
    +    pub(crate) wait_queue_timeout: Option<Duration>,
     }
     
     impl ConnectionPoolOptions {
         pub(crate) fn from_client_options(options: &ClientOptions) -> Self {
    -        let mut pool_options = Self::builder().build();
    -        pool_options.app_name = options.app_name.clone();
    -        pool_options.connect_timeout = options.connect_timeout;
    -        pool_options.credential = options.credential.clone();
    -        pool_options.driver_info = options.driver_info.clone();
    -        pool_options.event_handler = options.cmap_event_handler.clone();
    -        pool_options.max_idle_time = options.max_idle_time;
    -        pool_options.min_pool_size = options.min_pool_size;
    -        pool_options.tls_options = options.tls_options();
    -        pool_options.wait_queue_timeout = options.wait_queue_timeout;
    -
    -        pool_options
    +        Self {
    +            app_name: options.app_name.clone(),
    +            connect_timeout: options.connect_timeout,
    +            driver_info: options.driver_info.clone(),
    +            max_idle_time: options.max_idle_time,
    +            min_pool_size: options.min_pool_size,
    +            max_pool_size: options.max_pool_size,
    +            server_api: options.server_api.clone(),
    +            tls_options: options.tls_options(),
    +            wait_queue_timeout: options.wait_queue_timeout,
    +            credential: options.credential.clone(),
    +            event_handler: options.cmap_event_handler.clone(),
    +            #[cfg(test)]
    +            maintenance_frequency: None,
    +            #[cfg(test)]
    +            ready: None,
    +        }
    +    }
    +
    +    pub(crate) fn to_event_options(&self) -> EventOptions {
    +        EventOptions {
    +            app_name: self.app_name.clone(),
    +            connect_timeout: self.connect_timeout,
    +            driver_info: self.driver_info.clone(),
    +            max_idle_time: self.max_idle_time,
    +            min_pool_size: self.min_pool_size,
    +            max_pool_size: self.max_pool_size,
    +            server_api: self.server_api.clone(),
    +            tls_options: self.tls_options.clone(),
    +            wait_queue_timeout: self.wait_queue_timeout,
    +        }
         }
     }
     
    
  • src/cmap/test/file.rs+1 1 modified
    @@ -17,7 +17,7 @@ pub struct TestFile {
         version: u8,
         style: TestStyle,
         pub description: String,
    -    pub pool_options: Option<ConnectionPoolOptions>,
    +    pub(crate) pool_options: Option<ConnectionPoolOptions>,
         pub operations: Vec<ThreadedOperation>,
         pub error: Option<Error>,
         pub events: Vec<Event>,
    
  • src/cmap/test/mod.rs+4 4 modified
    @@ -12,8 +12,9 @@ use self::{
     };
     
     use crate::{
    -    cmap::{options::ConnectionPoolOptions, Connection, ConnectionPool},
    +    cmap::{Connection, ConnectionPool, ConnectionPoolOptions},
         error::{Error, Result},
    +    event::cmap::ConnectionPoolOptions as EventOptions,
         options::TlsOptions,
         runtime::AsyncJoinHandle,
         sdam::{ServerUpdate, ServerUpdateSender},
    @@ -329,11 +330,10 @@ impl Matchable for TlsOptions {
         }
     }
     
    -impl Matchable for ConnectionPoolOptions {
    -    fn content_matches(&self, expected: &ConnectionPoolOptions) -> bool {
    +impl Matchable for EventOptions {
    +    fn content_matches(&self, expected: &EventOptions) -> bool {
             self.app_name.matches(&expected.app_name)
                 && self.connect_timeout.matches(&expected.connect_timeout)
    -            && self.credential.matches(&expected.credential)
                 && self.max_idle_time.matches(&expected.max_idle_time)
                 && self.max_pool_size.matches(&expected.max_pool_size)
                 && self.min_pool_size.matches(&expected.min_pool_size)
    
  • src/event/cmap.rs+70 2 modified
    @@ -1,10 +1,14 @@
     //! Contains the events and functionality for monitoring behavior of the connection pooling of a
     //! `Client`.
     
    +use std::time::Duration;
    +
     use serde::Deserialize;
     
    -pub use crate::cmap::options::ConnectionPoolOptions;
    -use crate::options::StreamAddress;
    +use crate::{
    +    client::options::{DriverInfo, ServerApi, TlsOptions},
    +    options::StreamAddress,
    +};
     
     /// We implement `Deserialize` for all of the event types so that we can more easily parse the CMAP
     /// spec tests. However, we have no need to parse the address field from the JSON files (if it's
    @@ -31,6 +35,70 @@ pub struct PoolCreatedEvent {
         pub options: Option<ConnectionPoolOptions>,
     }
     
    +/// Contains the options for creating a connection pool. While these options are specified at the
    +/// client-level, `ConnectionPoolOptions` is exposed for the purpose of CMAP event handling.
    +#[derive(Clone, Default, Deserialize, Debug, PartialEq)]
    +#[serde(rename_all = "camelCase")]
    +#[non_exhaustive]
    +pub struct ConnectionPoolOptions {
    +    /// The application name specified by the user. This is sent to the server as part of the
    +    /// handshake that each connection makes when it's created.
    +    pub app_name: Option<String>,
    +
    +    /// The connect timeout passed to each underlying TcpStream when attempting to connect to the
    +    /// server.
    +    #[serde(skip)]
    +    pub connect_timeout: Option<Duration>,
    +
    +    /// Extra information to append to the driver version in the metadata of the handshake with the
    +    /// server. This should be used by libraries wrapping the driver, e.g. ODMs.
    +    #[serde(skip)]
    +    pub driver_info: Option<DriverInfo>,
    +
    +    /// Connections that have been ready for usage in the pool for longer than `max_idle_time` will
    +    /// not be used.
    +    ///
    +    /// The default is that connections will not be closed due to being idle.
    +    #[serde(rename = "maxIdleTimeMS")]
    +    #[serde(default)]
    +    #[serde(deserialize_with = "crate::bson_util::deserialize_duration_from_u64_millis")]
    +    pub max_idle_time: Option<Duration>,
    +
    +    /// The maximum number of connections that the pool can have at a given time. This includes
    +    /// connections which are currently checked out of the pool.
    +    ///
    +    /// The default is 100.
    +    pub max_pool_size: Option<u32>,
    +
    +    /// The minimum number of connections that the pool can have at a given time. This includes
    +    /// connections which are currently checked out of the pool. If fewer than `min_pool_size`
    +    /// connections are in the pool, connections will be added to the pool in the background.
    +    ///
    +    /// The default is that no minimum is enforced
    +    pub min_pool_size: Option<u32>,
    +
    +    /// The declared API version
    +    ///
    +    /// The default value is to have no declared API version
    +    pub(crate) server_api: Option<ServerApi>,
    +
    +    /// The options specifying how a TLS connection should be configured. If `tls_options` is
    +    /// `None`, then TLS will not be used for the connections.
    +    ///
    +    /// The default is not to use TLS for connections.
    +    #[serde(skip)]
    +    pub tls_options: Option<TlsOptions>,
    +
    +    /// Rather than wait indefinitely for a connection to become available, instead return an error
    +    /// after the given duration.
    +    ///
    +    /// The default is to block indefinitely until a connection becomes available.
    +    #[serde(rename = "waitQueueTimeoutMS")]
    +    #[serde(default)]
    +    #[serde(deserialize_with = "crate::bson_util::deserialize_duration_from_u64_millis")]
    +    pub wait_queue_timeout: Option<Duration>,
    +}
    +
     /// Event emitted when a connection pool becomes ready.
     #[derive(Clone, Debug, Deserialize, PartialEq)]
     #[non_exhaustive]
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.