|
| 1 | +From 612a8aa90e3d4c3eb2f7e6f24d52eac8fd440df3 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Hiroshi SHIBATA <hsbt@ruby-lang.org> |
| 3 | +Date: Sat, 12 Jul 2025 11:51:31 +0900 |
| 4 | +Subject: [PATCH] Clear user info totally at setting any of authority info |
| 5 | + |
| 6 | +CVE-2025-61594: URI Credential Leakage Bypass over CVE-2025-27221 |
| 7 | + |
| 8 | +Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> |
| 9 | +Upstream-reference: https://github.com/ruby/uri/commit/20157e3e29b125ff41f1d9662e2e3b1d066f5902 |
| 10 | +--- |
| 11 | + lib/uri/generic.rb | 29 +++++++++++++++++++++-------- |
| 12 | + test/uri/test_generic.rb | 15 ++++++++++----- |
| 13 | + 2 files changed, 31 insertions(+), 13 deletions(-) |
| 14 | + |
| 15 | +diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb |
| 16 | +index 2c0a88d..c893ed1 100644 |
| 17 | +--- a/lib/uri/generic.rb |
| 18 | ++++ b/lib/uri/generic.rb |
| 19 | +@@ -186,18 +186,18 @@ module URI |
| 20 | + |
| 21 | + if arg_check |
| 22 | + self.scheme = scheme |
| 23 | +- self.userinfo = userinfo |
| 24 | + self.hostname = host |
| 25 | + self.port = port |
| 26 | ++ self.userinfo = userinfo |
| 27 | + self.path = path |
| 28 | + self.query = query |
| 29 | + self.opaque = opaque |
| 30 | + self.fragment = fragment |
| 31 | + else |
| 32 | + self.set_scheme(scheme) |
| 33 | +- self.set_userinfo(userinfo) |
| 34 | + self.set_host(host) |
| 35 | + self.set_port(port) |
| 36 | ++ self.set_userinfo(userinfo) |
| 37 | + self.set_path(path) |
| 38 | + self.query = query |
| 39 | + self.set_opaque(opaque) |
| 40 | +@@ -511,7 +511,7 @@ module URI |
| 41 | + user, password = split_userinfo(user) |
| 42 | + end |
| 43 | + @user = user |
| 44 | +- @password = password if password |
| 45 | ++ @password = password |
| 46 | + |
| 47 | + [@user, @password] |
| 48 | + end |
| 49 | +@@ -522,7 +522,7 @@ module URI |
| 50 | + # See also URI::Generic.user=. |
| 51 | + # |
| 52 | + def set_user(v) |
| 53 | +- set_userinfo(v, @password) |
| 54 | ++ set_userinfo(v, nil) |
| 55 | + v |
| 56 | + end |
| 57 | + protected :set_user |
| 58 | +@@ -574,6 +574,12 @@ module URI |
| 59 | + @password |
| 60 | + end |
| 61 | + |
| 62 | ++ # Returns the authority info (array of user, password, host and |
| 63 | ++ # port), if any is set. Or returns +nil+. |
| 64 | ++ def authority |
| 65 | ++ return @user, @password, @host, @port if @user || @password || @host || @port |
| 66 | ++ end |
| 67 | ++ |
| 68 | + # Returns the user component after URI decoding. |
| 69 | + def decoded_user |
| 70 | + URI.decode_uri_component(@user) if @user |
| 71 | +@@ -615,6 +621,13 @@ module URI |
| 72 | + end |
| 73 | + protected :set_host |
| 74 | + |
| 75 | ++ # Protected setter for the authority info (+user+, +password+, +host+ |
| 76 | ++ # and +port+). If +port+ is +nil+, +default_port+ will be set. |
| 77 | ++ # |
| 78 | ++ protected def set_authority(user, password, host, port = nil) |
| 79 | ++ @user, @password, @host, @port = user, password, host, port || self.default_port |
| 80 | ++ end |
| 81 | ++ |
| 82 | + # |
| 83 | + # == Args |
| 84 | + # |
| 85 | +@@ -639,6 +652,7 @@ module URI |
| 86 | + def host=(v) |
| 87 | + check_host(v) |
| 88 | + set_host(v) |
| 89 | ++ set_userinfo(nil) |
| 90 | + v |
| 91 | + end |
| 92 | + |
| 93 | +@@ -729,6 +743,7 @@ module URI |
| 94 | + def port=(v) |
| 95 | + check_port(v) |
| 96 | + set_port(v) |
| 97 | ++ set_userinfo(nil) |
| 98 | + port |
| 99 | + end |
| 100 | + |
| 101 | +@@ -1121,7 +1136,7 @@ module URI |
| 102 | + |
| 103 | + base = self.dup |
| 104 | + |
| 105 | +- authority = rel.userinfo || rel.host || rel.port |
| 106 | ++ authority = rel.authority |
| 107 | + |
| 108 | + # RFC2396, Section 5.2, 2) |
| 109 | + if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query |
| 110 | +@@ -1134,9 +1149,7 @@ module URI |
| 111 | + |
| 112 | + # RFC2396, Section 5.2, 4) |
| 113 | + if authority |
| 114 | +- base.set_userinfo(rel.userinfo) |
| 115 | +- base.set_host(rel.host) |
| 116 | +- base.set_port(rel.port || base.default_port) |
| 117 | ++ base.set_authority(*authority) |
| 118 | + base.set_path(rel.path) |
| 119 | + elsif base.path && rel.path |
| 120 | + base.set_path(merge_path(base.path, rel.path)) |
| 121 | +diff --git a/test/uri/test_generic.rb b/test/uri/test_generic.rb |
| 122 | +index 1a70dd4..dc41b54 100644 |
| 123 | +--- a/test/uri/test_generic.rb |
| 124 | ++++ b/test/uri/test_generic.rb |
| 125 | +@@ -272,6 +272,9 @@ class URI::TestGeneric < Test::Unit::TestCase |
| 126 | + u0 = URI.parse('http://new.example.org/path') |
| 127 | + u1 = u.merge('//new.example.org/path') |
| 128 | + assert_equal(u0, u1) |
| 129 | ++ u0 = URI.parse('http://other@example.net') |
| 130 | ++ u1 = u.merge('//other@example.net') |
| 131 | ++ assert_equal(u0, u1) |
| 132 | + end |
| 133 | + |
| 134 | + def test_route |
| 135 | +@@ -737,17 +740,18 @@ class URI::TestGeneric < Test::Unit::TestCase |
| 136 | + def test_set_component |
| 137 | + uri = URI.parse('http://foo:bar@baz') |
| 138 | + assert_equal('oof', uri.user = 'oof') |
| 139 | +- assert_equal('http://oof:bar@baz', uri.to_s) |
| 140 | ++ assert_equal('http://oof@baz', uri.to_s) |
| 141 | + assert_equal('rab', uri.password = 'rab') |
| 142 | + assert_equal('http://oof:rab@baz', uri.to_s) |
| 143 | + assert_equal('foo', uri.userinfo = 'foo') |
| 144 | +- assert_equal('http://foo:rab@baz', uri.to_s) |
| 145 | ++ assert_equal('http://foo@baz', uri.to_s) |
| 146 | + assert_equal(['foo', 'bar'], uri.userinfo = ['foo', 'bar']) |
| 147 | + assert_equal('http://foo:bar@baz', uri.to_s) |
| 148 | + assert_equal(['foo'], uri.userinfo = ['foo']) |
| 149 | +- assert_equal('http://foo:bar@baz', uri.to_s) |
| 150 | ++ assert_equal('http://foo@baz', uri.to_s) |
| 151 | + assert_equal('zab', uri.host = 'zab') |
| 152 | +- assert_equal('http://foo:bar@zab', uri.to_s) |
| 153 | ++ assert_equal('http://zab', uri.to_s) |
| 154 | ++ uri.userinfo = ['foo', 'bar'] |
| 155 | + uri.port = "" |
| 156 | + assert_nil(uri.port) |
| 157 | + uri.port = "80" |
| 158 | +@@ -757,7 +761,8 @@ class URI::TestGeneric < Test::Unit::TestCase |
| 159 | + uri.port = " 080 " |
| 160 | + assert_equal(80, uri.port) |
| 161 | + assert_equal(8080, uri.port = 8080) |
| 162 | +- assert_equal('http://foo:bar@zab:8080', uri.to_s) |
| 163 | ++ assert_equal('http://zab:8080', uri.to_s) |
| 164 | ++ uri = URI.parse('http://foo:bar@zab:8080') |
| 165 | + assert_equal('/', uri.path = '/') |
| 166 | + assert_equal('http://foo:bar@zab:8080/', uri.to_s) |
| 167 | + assert_equal('a=1', uri.query = 'a=1') |
| 168 | +-- |
| 169 | +2.45.4 |
| 170 | + |
0 commit comments