Skip to content

fix(NODE-7986): remove auto-encryption listeners when the user client removes them - #5104

Open
tap1r wants to merge 1 commit into
mongodb:mainfrom
tap1r:fix/encrypter-remove-listener
Open

tap1r wants to merge 1 commit into
mongodb:mainfrom
tap1r:fix/encrypter-remove-listener

Conversation

@tap1r

@tap1r tap1r commented Nov 3, 2026 •

Copy link
Copy Markdown

Description

Summary of Changes

The internal key-vault client used by automatic encryption now drops a listener when the application client drops it, and Encrypter.close() detaches the forwarding hooks.

Notes for Reviewers

newListener still forwards every event name, which is the previous behavior, not only MONGO_CLIENT_EVENTS. removeListener matches that set. Listeners that were already attached when getInternalClient ran are copied, and removeListener removes those too, because they are the same function references.

Registering the removeListener hook emits newListener, so that function is also attached to the internal client. close() removes it there directly. Once it has been removed from the application client it cannot observe its own removal.

new Encrypter() constructs AutoEncrypter and can spawn mongocryptd. The unit test calls getInternalClient on an uninitialized instance so the forwarding can be tested without that. close() is covered with a stub autoEncrypter.

Jira: NODE-7986

What is the motivation for this change?

Applications that subscribe to command monitoring for the life of one operation, then remove the listener, kept receiving key-vault traffic and retained the listener for the life of the encrypted client.

For bug fixes

Current (incorrect) behavior:

Removing a listener from the application MongoClient does not remove it from the internal client created for the key vault. Listeners added after the internal client is created are copied on newListener and then left in place. Encrypter.close() does not remove the forwarder.

Expected behavior:

client.removeListener(event, listener) removes that function from the internal client. After encrypter.close(client), new subscriptions on the application client are not copied, and the internal client no longer holds the forwarding function.

How to reproduce:

const client = new MongoClient(uri, { autoEncryption });
const listener = () => {};
client.on('commandStarted', listener);
// getInternalClient has copied listener onto the key-vault client
client.removeListener('commandStarted', listener);
// listener still runs when the key-vault client emits commandStarted

Unit coverage: test/unit/client-side-encryption/encrypter.test.ts.

Affected versions:

Present on main for any client that creates the internal key-vault client (auto-encryption without an explicit keyVaultClient, and with maxPoolSize !== 0). No specific server version.

Double check the following

  • Lint is passing (npm run check:lint)
  • Self-review completed using the steps outlined here
  • PR title follows the correct format: type(NODE-xxxx)[!]: description
    • Example: feat(NODE-1234)!: rewriting everything in coffeescript
  • Changes are covered by tests
  • New TODOs have a related JIRA ticket

getInternalClient copied the application's client listeners onto the
internal key-vault client and forwarded every later newListener. It did
not forward removeListener, and close() left the forwarder in place.
A listener removed from the application client kept running for
key-vault traffic and stayed reachable from the internal client.

Forward removeListener for the same events newListener already mirrors,
and detach both hooks when the encrypter closes. Registering the
removeListener hook emits newListener, which copies that function onto
the internal client; close() removes that copy explicitly.
@tap1r
tap1r requested a review from a team as a code owner November 3, 2026 23:57
@tap1r tap1r changed the title fix: remove auto-encryption listeners when the user client removes them fix(NODE-7986): remove auto-encryption listeners when the user client removes them Nov 4, 2026
@PavelSafronov PavelSafronov added tracked-in-jira Ticket filed in MongoDB's Jira system External Submission PR submitted from outside the team labels Nov 6, 2026
@PavelSafronov

Copy link
Copy Markdown
Contributor

@tap1r, thank you for taking the time to create this PR! This is tracked in NODE-7986. The team is currently at capacity, so we will be reviewing it over the next few quarters as capacity allows.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

External Submission PR submitted from outside the team tracked-in-jira Ticket filed in MongoDB's Jira system

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants