From 23a9ade9b7a7445615d6850b6af5efd33fa169fd Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@ruby-lang.org>
Date: Sat, 12 Jul 2025 11:51:31 +0900
Subject: [PATCH] Clear user info totally at setting any of authority info

Fix CVE-2025-27221.
https://hackerone.com/reports/3221142
---
 lib/uri/generic.rb       | 10 ++++++----
 test/uri/test_generic.rb | 15 ++++++++++-----
 2 files changed, 16 insertions(+), 9 deletions(-)

--- a/lib/uri/generic.rb
+++ b/lib/uri/generic.rb
@@ -187,18 +187,18 @@ module URI
 
       if arg_check
         self.scheme = scheme
-        self.userinfo = userinfo
         self.hostname = host
         self.port = port
+        self.userinfo = userinfo
         self.path = path
         self.query = query
         self.opaque = opaque
         self.fragment = fragment
       else
         self.set_scheme(scheme)
-        self.set_userinfo(userinfo)
         self.set_host(host)
         self.set_port(port)
+        self.set_userinfo(userinfo)
         self.set_path(path)
         self.query = query
         self.set_opaque(opaque)
@@ -512,7 +512,7 @@ module URI
         user, password = split_userinfo(user)
       end
       @user     = user
-      @password = password if password
+      @password = password
 
       [@user, @password]
     end
@@ -523,7 +523,7 @@ module URI
     # See also URI::Generic.user=.
     #
     def set_user(v)
-      set_userinfo(v, @password)
+      set_userinfo(v, nil)
       v
     end
     protected :set_user
@@ -630,6 +630,7 @@ module URI
     def host=(v)
       check_host(v)
       set_host(v)
+      set_userinfo(nil)
       v
     end
 
@@ -720,6 +721,7 @@ module URI
     def port=(v)
       check_port(v)
       set_port(v)
+      set_userinfo(nil)
       port
     end
 
--- a/test/uri/test_generic.rb
+++ b/test/uri/test_generic.rb
@@ -258,6 +258,9 @@ class URI::TestGeneric < Test::Unit::Tes
     u0 = URI.parse('http://new.example.org/path')
     u1 = u.merge('//new.example.org/path')
     assert_equal(u0, u1)
+    u0 = URI.parse('http://other@example.net')
+    u1 = u.merge('//other@example.net')
+    assert_equal(u0, u1)
   end
 
   def test_route
@@ -723,17 +726,18 @@ class URI::TestGeneric < Test::Unit::Tes
   def test_set_component
     uri = URI.parse('http://foo:bar@baz')
     assert_equal('oof', uri.user = 'oof')
-    assert_equal('http://oof:bar@baz', uri.to_s)
+    assert_equal('http://oof@baz', uri.to_s)
     assert_equal('rab', uri.password = 'rab')
     assert_equal('http://oof:rab@baz', uri.to_s)
     assert_equal('foo', uri.userinfo = 'foo')
-    assert_equal('http://foo:rab@baz', uri.to_s)
+    assert_equal('http://foo@baz', uri.to_s)
     assert_equal(['foo', 'bar'], uri.userinfo = ['foo', 'bar'])
     assert_equal('http://foo:bar@baz', uri.to_s)
     assert_equal(['foo'], uri.userinfo = ['foo'])
-    assert_equal('http://foo:bar@baz', uri.to_s)
+    assert_equal('http://foo@baz', uri.to_s)
     assert_equal('zab', uri.host = 'zab')
-    assert_equal('http://foo:bar@zab', uri.to_s)
+    assert_equal('http://zab', uri.to_s)
+    uri.userinfo = ['foo', 'bar']
     uri.port = ""
     assert_nil(uri.port)
     uri.port = "80"
@@ -743,7 +747,8 @@ class URI::TestGeneric < Test::Unit::Tes
     uri.port = " 080 "
     assert_equal(80, uri.port)
     assert_equal(8080, uri.port = 8080)
-    assert_equal('http://foo:bar@zab:8080', uri.to_s)
+    assert_equal('http://zab:8080', uri.to_s)
+    uri = URI.parse('http://foo:bar@zab:8080')
     assert_equal('/', uri.path = '/')
     assert_equal('http://foo:bar@zab:8080/', uri.to_s)
     assert_equal('a=1', uri.query = 'a=1')
