Skip to content

Commit e2e7256

Browse files
bpo-46756: Fix authorization check in urllib.request (GH-31353)
Fix a bug in urllib.request.HTTPPasswordMgr.find_user_password() and urllib.request.HTTPPasswordMgrWithPriorAuth.is_authenticated() which allowed to bypass authorization. For example, access to URI "example.org/foobar" was allowed if the user was authorized for URI "example.org/foo".
1 parent 53ecf9e commit e2e7256

File tree

3 files changed

+30
-8
lines changed

3 files changed

+30
-8
lines changed

Lib/test/test_urllib2.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ def test_password_manager(self):
164164
self.assertEqual(find_user_pass("Some Realm",
165165
"http://example.com/spam"),
166166
('joe', 'password'))
167-
168167
self.assertEqual(find_user_pass("Some Realm",
169168
"http://example.com/spam/spam"),
170169
('joe', 'password'))
@@ -173,12 +172,29 @@ def test_password_manager(self):
173172

174173
add("c", "http://example.com/foo", "foo", "ni")
175174
add("c", "http://example.com/bar", "bar", "nini")
175+
add("c", "http://example.com/foo/bar", "foobar", "nibar")
176176

177177
self.assertEqual(find_user_pass("c", "http://example.com/foo"),
178178
('foo', 'ni'))
179-
180179
self.assertEqual(find_user_pass("c", "http://example.com/bar"),
181180
('bar', 'nini'))
181+
self.assertEqual(find_user_pass("c", "http://example.com/foo/"),
182+
('foo', 'ni'))
183+
self.assertEqual(find_user_pass("c", "http://example.com/foo/bar"),
184+
('foo', 'ni'))
185+
self.assertEqual(find_user_pass("c", "http://example.com/foo/baz"),
186+
('foo', 'ni'))
187+
self.assertEqual(find_user_pass("c", "http://example.com/foobar"),
188+
(None, None))
189+
190+
add("c", "http://example.com/baz/", "baz", "ninini")
191+
192+
self.assertEqual(find_user_pass("c", "http://example.com/baz"),
193+
(None, None))
194+
self.assertEqual(find_user_pass("c", "http://example.com/baz/"),
195+
('baz', 'ninini'))
196+
self.assertEqual(find_user_pass("c", "http://example.com/baz/bar"),
197+
('baz', 'ninini'))
182198

183199
# For the same path, newer password should be considered.
184200

@@ -1658,8 +1674,9 @@ def test_basic_prior_auth_auto_send(self):
16581674
auth_prior_handler.add_password(
16591675
None, request_url, user, password, is_authenticated=True)
16601676

1661-
is_auth = pwd_manager.is_authenticated(request_url)
1662-
self.assertTrue(is_auth)
1677+
self.assertTrue(pwd_manager.is_authenticated(request_url))
1678+
self.assertTrue(pwd_manager.is_authenticated(request_url + '/nested'))
1679+
self.assertFalse(pwd_manager.is_authenticated(request_url + 'plain'))
16631680

16641681
opener = OpenerDirector()
16651682
opener.add_handler(auth_prior_handler)

Lib/urllib/request.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -889,10 +889,10 @@ def is_suburi(self, base, test):
889889
return True
890890
if base[0] != test[0]:
891891
return False
892-
common = posixpath.commonprefix((base[1], test[1]))
893-
if len(common) == len(base[1]):
894-
return True
895-
return False
892+
prefix = base[1]
893+
if prefix[-1:] != '/':
894+
prefix += '/'
895+
return test[1].startswith(prefix)
896896

897897

898898
class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr):
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix a bug in :meth:`urllib.request.HTTPPasswordMgr.find_user_password` and
2+
:meth:`urllib.request.HTTPPasswordMgrWithPriorAuth.is_authenticated` which
3+
allowed to bypass authorization. For example, access to URI
4+
``example.org/foobar`` was allowed if the user was authorized for URI
5+
``example.org/foo``.

0 commit comments

Comments
 (0)