From f405c060349d45a3aec169f3fc7cbab7be5ac0e8 Mon Sep 17 00:00:00 2001 From: Kyle Burgess Date: Mon, 8 Dec 2025 10:59:45 -0500 Subject: [PATCH 1/8] Original solution --- lib/mongo/uri/srv_protocol.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/mongo/uri/srv_protocol.rb b/lib/mongo/uri/srv_protocol.rb index 4336d1c814..56df8e671a 100644 --- a/lib/mongo/uri/srv_protocol.rb +++ b/lib/mongo/uri/srv_protocol.rb @@ -168,8 +168,7 @@ def parse!(remaining) # The hostname cannot include a port. # # The hostname must not begin with a dot, end with a dot, or have - # consecutive dots. The hostname must have a minimum of 3 total - # components (foo.bar.tld). + # consecutive dots. The hostname must have a minimum of 1 component (tld) # # Raises Error::InvalidURI if validation fails. def validate_srv_hostname(hostname) @@ -185,8 +184,8 @@ def validate_srv_hostname(hostname) if parts.any?(&:empty?) raise_invalid_error!("Hostname cannot have consecutive dots: #{hostname}") end - if parts.length < 3 - raise_invalid_error!("Hostname must have a minimum of 3 components (foo.bar.tld): #{hostname}") + if parts.length < 1 + raise_invalid_error!("Hostname must have a minimum of 1 component (tld): #{hostname}") end end From 1727360dde2900b5b4ea2fb15317769cab044c9f Mon Sep 17 00:00:00 2001 From: Kyle Burgess Date: Mon, 8 Dec 2025 16:59:03 -0500 Subject: [PATCH 2/8] Updating tests and implementation to allow 3 parts to SRV string --- lib/mongo/srv/result.rb | 19 +++++++--- lib/mongo/uri/srv_protocol.rb | 3 -- spec/mongo/srv/result_spec.rb | 67 +++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/lib/mongo/srv/result.rb b/lib/mongo/srv/result.rb index 318011914f..673b0732fd 100644 --- a/lib/mongo/srv/result.rb +++ b/lib/mongo/srv/result.rb @@ -110,17 +110,28 @@ def normalize_hostname(host) # A hostname's domain name consists of each of the '.' delineated # parts after the first. For example, the hostname 'foo.bar.baz' # has the domain name 'bar.baz'. + # + # If the hostname has less than three parts, its domain name is the hostname itself. # # @param [ String ] record_host The host of the SRV record. # # @raise [ Mongo::Error::MismatchedDomain ] If the record's domain name doesn't match that of # the hostname. def validate_same_origin!(record_host) - domain_name ||= query_hostname.split('.')[1..-1] - host_parts = record_host.split('.') + srv_is_less_than_three_parts = query_hostname.split('.').length < 3 + srv_host_domain = if srv_is_less_than_three_parts + query_hostname.split('.') + else + query_hostname.split('.')[1..-1] + end + record_host_parts = record_host.split('.') + + if (srv_is_less_than_three_parts && record_host_parts.length <= srv_host_domain.length) + raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, srv_host_domain]) + end - unless (host_parts.size > domain_name.size) && (domain_name == host_parts[-domain_name.length..-1]) - raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, domain_name]) + unless (srv_host_domain == record_host_parts[-srv_host_domain.size..-1]) + raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, srv_host_domain]) end end end diff --git a/lib/mongo/uri/srv_protocol.rb b/lib/mongo/uri/srv_protocol.rb index 56df8e671a..45da81d1a2 100644 --- a/lib/mongo/uri/srv_protocol.rb +++ b/lib/mongo/uri/srv_protocol.rb @@ -184,9 +184,6 @@ def validate_srv_hostname(hostname) if parts.any?(&:empty?) raise_invalid_error!("Hostname cannot have consecutive dots: #{hostname}") end - if parts.length < 1 - raise_invalid_error!("Hostname must have a minimum of 1 component (tld): #{hostname}") - end end # Obtains the TXT options of a host. diff --git a/spec/mongo/srv/result_spec.rb b/spec/mongo/srv/result_spec.rb index 227679a459..ff53f1537d 100644 --- a/spec/mongo/srv/result_spec.rb +++ b/spec/mongo/srv/result_spec.rb @@ -23,6 +23,73 @@ expect(result.address_strs).to eq(['foo.bar.com:42']) end end + + exampleSrvName = ['i-love-rb', 'i-love-rb.mongodb', 'i-love-ruby.mongodb.io']; + exampleHostName = [ + 'rb-00.i-love-rb', + 'rb-00.i-love-rb.mongodb', + 'i-love-ruby-00.mongodb.io' + ]; + exampleHostNameThatDoNotMatchParent = [ + 'rb-00.i-love-rb-a-little', + 'rb-00.i-love-rb-a-little.mongodb', + 'i-love-ruby-00.evil-mongodb.io' + ]; + + (0..2).each do |i| + context "when srvName has #{i+1} part#{i != 0 ? 's' : ''}" do + let(:srv_name) { exampleSrvName[i] } + let(:host_name) { exampleHostName[i] } + let(:mismatched_host_name) { exampleHostNameThatDoNotMatchParent[i] } + + context 'when address does not match parent domain' do + it 'raises MismatchedDomain error' do + record = double('record').tap do |record| + allow(record).to receive(:target).and_return(mismatched_host_name) + allow(record).to receive(:port).and_return(42) + allow(record).to receive(:ttl).and_return(1) + end + + expect { + result = described_class.new(srv_name) + result.add_record(record) + }.to raise_error(Mongo::Error::MismatchedDomain) + end + end + + context 'when address matches parent domain' do + it 'adds the record' do + record = double('record').tap do |record| + allow(record).to receive(:target).and_return(host_name) + allow(record).to receive(:port).and_return(42) + allow(record).to receive(:ttl).and_return(1) + end + + result = described_class.new(srv_name) + result.add_record(record) + + expect(result.address_strs).to eq([host_name + ':42']) + end + end + + if i < 2 + context 'when the address is less than 3 parts' do + it 'does not accept address if it does not contain an extra domain level' do + record = double('record').tap do |record| + allow(record).to receive(:target).and_return(srv_name) + allow(record).to receive(:port).and_return(42) + allow(record).to receive(:ttl).and_return(1) + end + + expect { + result = described_class.new(srv_name) + result.add_record(record) + }.to raise_error(Mongo::Error::MismatchedDomain) + end + end + end + end + end end describe '#normalize_hostname' do From 0b121092bf33ba6c54cbc98bba18e7fabddd746a Mon Sep 17 00:00:00 2001 From: Kyle Burgess Date: Tue, 9 Dec 2025 10:53:36 -0500 Subject: [PATCH 3/8] Fixing tests --- lib/mongo/srv/result.rb | 2 +- spec/spec_tests/data/connection_string/invalid-uris.yml | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/mongo/srv/result.rb b/lib/mongo/srv/result.rb index 673b0732fd..f2018491a1 100644 --- a/lib/mongo/srv/result.rb +++ b/lib/mongo/srv/result.rb @@ -130,7 +130,7 @@ def validate_same_origin!(record_host) raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, srv_host_domain]) end - unless (srv_host_domain == record_host_parts[-srv_host_domain.size..-1]) + unless (record_host_parts.size > srv_host_domain.size) && (srv_host_domain == record_host_parts[-srv_host_domain.size..-1]) raise Error::MismatchedDomain.new(MISMATCHED_DOMAINNAME % [record_host, srv_host_domain]) end end diff --git a/spec/spec_tests/data/connection_string/invalid-uris.yml b/spec/spec_tests/data/connection_string/invalid-uris.yml index dd4d4ce31c..4b9fe739ca 100644 --- a/spec/spec_tests/data/connection_string/invalid-uris.yml +++ b/spec/spec_tests/data/connection_string/invalid-uris.yml @@ -7,14 +7,6 @@ tests: hosts: ~ auth: ~ options: ~ - - - description: "Invalid scheme" - uri: "mongo://localhost:27017" - valid: false - warning: ~ - hosts: ~ - auth: ~ - options: ~ - description: "Missing host" uri: "mongodb://" From 618275d778bae0cd8035f9c5accbd43b5be389c8 Mon Sep 17 00:00:00 2001 From: Kyle Burgess Date: Tue, 9 Dec 2025 11:17:05 -0500 Subject: [PATCH 4/8] updating tests --- lib/mongo/uri/srv_protocol.rb | 3 +++ spec/mongo/uri/srv_protocol_spec.rb | 36 ++++------------------------- 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/lib/mongo/uri/srv_protocol.rb b/lib/mongo/uri/srv_protocol.rb index 45da81d1a2..7eb420a387 100644 --- a/lib/mongo/uri/srv_protocol.rb +++ b/lib/mongo/uri/srv_protocol.rb @@ -184,6 +184,9 @@ def validate_srv_hostname(hostname) if parts.any?(&:empty?) raise_invalid_error!("Hostname cannot have consecutive dots: #{hostname}") end + if parts.length == 0 + raise_invalid_error!("Hostname cannot be empty: #{hostname}") + end end # Obtains the TXT options of a host. diff --git a/spec/mongo/uri/srv_protocol_spec.rb b/spec/mongo/uri/srv_protocol_spec.rb index a1b13a0195..be2790eeda 100644 --- a/spec/mongo/uri/srv_protocol_spec.rb +++ b/spec/mongo/uri/srv_protocol_spec.rb @@ -56,16 +56,6 @@ end end - context 'when the host in URI does not have {hostname}, {domainname} and {tld}' do - - let(:string) { "#{scheme}#{hosts}" } - let(:hosts) { '10gen.cc/' } - - it 'raises an error' do - expect { uri }.to raise_error(Mongo::Error::InvalidURI) - end - end - context 'when the {tld} is empty' do let(:string) { "#{scheme}#{hosts}" } @@ -220,24 +210,6 @@ expect { uri }.to raise_error(Mongo::Error::InvalidURI) end end - - context 'mongodb+srv://example.com?w=1' do - - let(:string) { "#{scheme}example.com?w=1" } - - it 'raises an error' do - expect { uri }.to raise_error(Mongo::Error::InvalidURI) - end - end - - context 'mongodb+srv://example.com/?w' do - - let(:string) { "#{scheme}example.com/?w" } - - it 'raises an error' do - expect { uri }.to raise_error(Mongo::Error::InvalidURI) - end - end end describe 'valid uris' do @@ -1302,8 +1274,8 @@ 'a' end - it 'raises an error' do - expect { validate }.to raise_error(Mongo::Error::InvalidURI) + it 'does not raise an error' do + expect { validate }.to_not raise_error end end @@ -1313,8 +1285,8 @@ 'a.b' end - it 'raises an error' do - expect { validate }.to raise_error(Mongo::Error::InvalidURI) + it 'validates the hostname' do + expect { validate }.not_to raise_error end end From a65c29772c7ac5c5c87d28bc07cb8d21ab8cc783 Mon Sep 17 00:00:00 2001 From: Kyle Burgess Date: Tue, 9 Dec 2025 12:52:03 -0500 Subject: [PATCH 5/8] typo --- lib/mongo/uri/srv_protocol.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mongo/uri/srv_protocol.rb b/lib/mongo/uri/srv_protocol.rb index 7eb420a387..384be6d147 100644 --- a/lib/mongo/uri/srv_protocol.rb +++ b/lib/mongo/uri/srv_protocol.rb @@ -168,7 +168,7 @@ def parse!(remaining) # The hostname cannot include a port. # # The hostname must not begin with a dot, end with a dot, or have - # consecutive dots. The hostname must have a minimum of 1 component (tld) + # consecutive dots. The hostname must have a minimum of 1 component # # Raises Error::InvalidURI if validation fails. def validate_srv_hostname(hostname) From 25845e7bdec1df136af92ea6097b07ed418c4e68 Mon Sep 17 00:00:00 2001 From: Kyle Burgess Date: Tue, 9 Dec 2025 12:56:22 -0500 Subject: [PATCH 6/8] Making changes according to copilot autoreview --- spec/mongo/srv/result_spec.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/spec/mongo/srv/result_spec.rb b/spec/mongo/srv/result_spec.rb index ff53f1537d..43bf9c4b39 100644 --- a/spec/mongo/srv/result_spec.rb +++ b/spec/mongo/srv/result_spec.rb @@ -24,13 +24,13 @@ end end - exampleSrvName = ['i-love-rb', 'i-love-rb.mongodb', 'i-love-ruby.mongodb.io']; - exampleHostName = [ + example_srv_names = ['i-love-rb', 'i-love-rb.mongodb', 'i-love-ruby.mongodb.io']; + example_host_names = [ 'rb-00.i-love-rb', 'rb-00.i-love-rb.mongodb', 'i-love-ruby-00.mongodb.io' ]; - exampleHostNameThatDoNotMatchParent = [ + example_host_names_that_do_not_match_parent = [ 'rb-00.i-love-rb-a-little', 'rb-00.i-love-rb-a-little.mongodb', 'i-love-ruby-00.evil-mongodb.io' @@ -38,10 +38,9 @@ (0..2).each do |i| context "when srvName has #{i+1} part#{i != 0 ? 's' : ''}" do - let(:srv_name) { exampleSrvName[i] } - let(:host_name) { exampleHostName[i] } - let(:mismatched_host_name) { exampleHostNameThatDoNotMatchParent[i] } - + let(:srv_name) { example_srv_names[i] } + let(:host_name) { example_host_names[i] } + let(:mismatched_host_name) { example_host_names_that_do_not_match_parent[i] } context 'when address does not match parent domain' do it 'raises MismatchedDomain error' do record = double('record').tap do |record| @@ -56,7 +55,6 @@ }.to raise_error(Mongo::Error::MismatchedDomain) end end - context 'when address matches parent domain' do it 'adds the record' do record = double('record').tap do |record| From 2013f187755f38c5cf5d450920c2b013316294d1 Mon Sep 17 00:00:00 2001 From: Kyle Burgess Date: Tue, 9 Dec 2025 14:45:26 -0500 Subject: [PATCH 7/8] reverting changes to invalid-uris --- spec/spec_tests/data/connection_string/invalid-uris.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/spec_tests/data/connection_string/invalid-uris.yml b/spec/spec_tests/data/connection_string/invalid-uris.yml index 4b9fe739ca..dd4d4ce31c 100644 --- a/spec/spec_tests/data/connection_string/invalid-uris.yml +++ b/spec/spec_tests/data/connection_string/invalid-uris.yml @@ -7,6 +7,14 @@ tests: hosts: ~ auth: ~ options: ~ + - + description: "Invalid scheme" + uri: "mongo://localhost:27017" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ - description: "Missing host" uri: "mongodb://" From 298afdc16506d2d212750282742988e06b850fed Mon Sep 17 00:00:00 2001 From: Kyle Burgess Date: Tue, 9 Dec 2025 14:52:31 -0500 Subject: [PATCH 8/8] responding to comments - rewriting tests and moving logic --- lib/mongo/srv/result.rb | 9 ++++----- lib/mongo/uri/srv_protocol.rb | 2 +- spec/mongo/srv/result_spec.rb | 22 +++++++++++++--------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/mongo/srv/result.rb b/lib/mongo/srv/result.rb index f2018491a1..7c63cb9bac 100644 --- a/lib/mongo/srv/result.rb +++ b/lib/mongo/srv/result.rb @@ -118,11 +118,10 @@ def normalize_hostname(host) # @raise [ Mongo::Error::MismatchedDomain ] If the record's domain name doesn't match that of # the hostname. def validate_same_origin!(record_host) - srv_is_less_than_three_parts = query_hostname.split('.').length < 3 - srv_host_domain = if srv_is_less_than_three_parts - query_hostname.split('.') - else - query_hostname.split('.')[1..-1] + srv_host_domain = query_hostname.split('.') + srv_is_less_than_three_parts = srv_host_domain.length < 3 + unless srv_is_less_than_three_parts + srv_host_domain = srv_host_domain[1..-1] end record_host_parts = record_host.split('.') diff --git a/lib/mongo/uri/srv_protocol.rb b/lib/mongo/uri/srv_protocol.rb index 384be6d147..c0484db697 100644 --- a/lib/mongo/uri/srv_protocol.rb +++ b/lib/mongo/uri/srv_protocol.rb @@ -184,7 +184,7 @@ def validate_srv_hostname(hostname) if parts.any?(&:empty?) raise_invalid_error!("Hostname cannot have consecutive dots: #{hostname}") end - if parts.length == 0 + if parts.length < 1 raise_invalid_error!("Hostname cannot be empty: #{hostname}") end end diff --git a/spec/mongo/srv/result_spec.rb b/spec/mongo/srv/result_spec.rb index 43bf9c4b39..c56536f35e 100644 --- a/spec/mongo/srv/result_spec.rb +++ b/spec/mongo/srv/result_spec.rb @@ -42,27 +42,30 @@ let(:host_name) { example_host_names[i] } let(:mismatched_host_name) { example_host_names_that_do_not_match_parent[i] } context 'when address does not match parent domain' do - it 'raises MismatchedDomain error' do - record = double('record').tap do |record| + let(:record) do + double('record').tap do |record| allow(record).to receive(:target).and_return(mismatched_host_name) allow(record).to receive(:port).and_return(42) allow(record).to receive(:ttl).and_return(1) end - + end + it 'raises MismatchedDomain error' do expect { result = described_class.new(srv_name) result.add_record(record) }.to raise_error(Mongo::Error::MismatchedDomain) end end + context 'when address matches parent domain' do - it 'adds the record' do - record = double('record').tap do |record| + let(:record) do + double('record').tap do |record| allow(record).to receive(:target).and_return(host_name) allow(record).to receive(:port).and_return(42) allow(record).to receive(:ttl).and_return(1) end - + end + it 'adds the record' do result = described_class.new(srv_name) result.add_record(record) @@ -72,13 +75,14 @@ if i < 2 context 'when the address is less than 3 parts' do - it 'does not accept address if it does not contain an extra domain level' do - record = double('record').tap do |record| + let(:record) do + double('record').tap do |record| allow(record).to receive(:target).and_return(srv_name) allow(record).to receive(:port).and_return(42) allow(record).to receive(:ttl).and_return(1) end - + end + it 'does not accept address if it does not contain an extra domain level' do expect { result = described_class.new(srv_name) result.add_record(record)