Skip to content

RUBY-3903 Fix libmongocrypt version floor, ClientEncryption timeout_ms and missing range_opts - #3096

Merged
comandeo-mongo merged 3 commits into
mongodb:masterfrom
comandeo-mongo:qe-small-fixes
Jul 29, 2026
Merged

RUBY-3903 Fix libmongocrypt version floor, ClientEncryption timeout_ms and missing range_opts#3096
comandeo-mongo merged 3 commits into
mongodb:masterfrom
comandeo-mongo:qe-small-fixes

Conversation

@comandeo-mongo

@comandeo-mongo comandeo-mongo commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Three small QE fixes found while auditing the driver against the client-side-encryption
spec. Each is a separate commit.

Require libmongocrypt 1.20.0

#3088 attaches mongocrypt_ctx_setopt_algorithm_text unconditionally, but
MIN_LIBMONGOCRYPT_VERSION stayed at 1.12.0. The version check runs at
binding.rb:136, well before that attach_function at line 1891, so an older
libmongocrypt passes the gate and then dies with

FFI::NotFoundError: Function 'mongocrypt_ctx_setopt_algorithm_text' not found

instead of the intended "libmongocrypt version X or above is required". 1.20.0 is what
the GA text fixtures require (QE-Text-substring.yml, MONGOCRYPT-936) and matches the
libmongocrypt-helper ~> 1.20.1 pin. version_spec.rb derives its cases from the
constant, so it needs no change.

Pass timeout_ms from ClientEncryption to the encrypter

ClientEncryption#initialize documents :timeout_ms but called
ExplicitEncrypter.new with four arguments, leaving the fifth (timeout_ms) nil.
ExplicitEncrypter threads @timeout_ms into every key management call, so CSOT on
ClientEncryption was silently dead. The new spec fails on master and passes here.

Raise ArgumentError when range_opts is missing

encrypt(algorithm: 'Range') without :range_opts raised
NoMethodError: undefined method '[]' for nil out of nil.dup.tap. Now it reports the
missing option, matching how the String algorithm handles a missing :string_opts.

The same commit adds a spec pinning trim_factor: 0. The guard
if opts[:trim_factor] reads like it would drop 0, but 0 is truthy in Ruby, so the
value is passed through — the spec keeps it that way.

The text algorithm binding added for QE text queries attaches
mongocrypt_ctx_setopt_algorithm_text unconditionally, but the version gate
still allowed 1.12.0. Older libmongocrypt passed the gate and then failed
with a raw FFI::NotFoundError instead of the version error. 1.20.0 is what
the GA text fixtures require (QE-Text-substring.yml, MONGOCRYPT-936) and
matches the pinned libmongocrypt-helper.
ClientEncryption documents :timeout_ms but never forwarded it, so the
timeout was silently ignored for every key management operation.
Encrypting with the Range algorithm and no :range_opts raised NoMethodError
from nil.dup. Match the String algorithm, which reports the missing option.

Also pins trim_factor: 0 with a spec: 0 is truthy in Ruby, so it is passed
through, but the guard reads as if it might not be.
@comandeo-mongo
comandeo-mongo marked this pull request as ready for review July 29, 2026 10:56
@comandeo-mongo
comandeo-mongo requested a review from a team as a code owner July 29, 2026 10:56
@comandeo-mongo
comandeo-mongo requested review from Copilot and jamis July 29, 2026 10:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR applies three correctness fixes to the driver’s client-side encryption (Queryable Encryption) support: enforcing an appropriate libmongocrypt minimum version, ensuring ClientEncryption operation timeouts are actually applied, and improving the error surfaced when required Range algorithm options are missing.

Changes:

  • Raise the minimum supported libmongocrypt version to 1.20.0 to match required QE text fixtures/bindings.
  • Thread :timeout_ms from Mongo::ClientEncryption into Crypt::ExplicitEncrypter so CSOT is honored.
  • Raise a clear ArgumentError when algorithm: "Range" is used without :range_opts, and add specs for this behavior (including trim_factor: 0).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
lib/mongo/crypt/binding.rb Updates the libmongocrypt minimum version gate to prevent late FFI symbol failures.
lib/mongo/client_encryption.rb Passes :timeout_ms into the explicit encrypter so timeouts take effect.
lib/mongo/crypt/explicit_encryption_context.rb Adds an explicit ArgumentError when Range encryption is configured without :range_opts.
spec/mongo/client_encryption_spec.rb Adds a spec that timeout_ms is propagated to the encrypter.
spec/mongo/crypt/explicit_encryption_context_spec.rb Adds specs covering Range context initialization, trim_factor: 0, and missing :range_opts.
Comments suppressed due to low confidence (1)

lib/mongo/crypt/explicit_encryption_context.rb:156

  • In convert_range_opts, the if opts[:trim_factor] guard is easy to misread as potentially dropping 0 values (even though 0 is truthy in Ruby), and it also won’t delete :trim_factor if the caller explicitly provides trim_factor: nil. Consider switching to a key? check and only setting trimFactor when non-nil, to make intent unambiguous and keep the resulting options hash free of unexpected keys.
        range_opts.dup.tap do |opts|
          opts[:sparsity] = BSON::Int64.new(opts[:sparsity]) if opts[:sparsity] && !opts[:sparsity].is_a?(BSON::Int64)
          opts[:trimFactor] = opts.delete(:trim_factor) if opts[:trim_factor]
        end

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@comandeo-mongo
comandeo-mongo merged commit f39ffb6 into mongodb:master Jul 29, 2026
205 checks passed
@comandeo-mongo
comandeo-mongo deleted the qe-small-fixes branch July 29, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants