Skip to content

Commit cdcaac3

Browse files
miss-islingtonserhiy-storchaka
authored andcommitted
bpo-46756: Fix authorization check in urllib.request (pythonGH-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". (cherry picked from commit e2e7256) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 3789096 commit cdcaac3

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
@@ -162,7 +162,6 @@ def test_password_manager(self):
162162
self.assertEqual(find_user_pass("Some Realm",
163163
"http://example.com/spam"),
164164
('joe', 'password'))
165-
166165
self.assertEqual(find_user_pass("Some Realm",
167166
"http://example.com/spam/spam"),
168167
('joe', 'password'))
@@ -171,12 +170,29 @@ def test_password_manager(self):
171170

172171
add("c", "http://example.com/foo", "foo", "ni")
173172
add("c", "http://example.com/bar", "bar", "nini")
173+
add("c", "http://example.com/foo/bar", "foobar", "nibar")
174174

175175
self.assertEqual(find_user_pass("c", "http://example.com/foo"),
176176
('foo', 'ni'))
177-
178177
self.assertEqual(find_user_pass("c", "http://example.com/bar"),
179178
('bar', 'nini'))
179+
self.assertEqual(find_user_pass("c", "http://example.com/foo/"),
180+
('foo', 'ni'))
181+
self.assertEqual(find_user_pass("c", "http://example.com/foo/bar"),
182+
('foo', 'ni'))
183+
self.assertEqual(find_user_pass("c", "http://example.com/foo/baz"),
184+
('foo', 'ni'))
185+
self.assertEqual(find_user_pass("c", "http://example.com/foobar"),
186+
(None, None))
187+
188+
add("c", "http://example.com/baz/", "baz", "ninini")
189+
190+
self.assertEqual(find_user_pass("c", "http://example.com/baz"),
191+
(None, None))
192+
self.assertEqual(find_user_pass("c", "http://example.com/baz/"),
193+
('baz', 'ninini'))
194+
self.assertEqual(find_user_pass("c", "http://example.com/baz/bar"),
195+
('baz', 'ninini'))
180196

181197
# For the same path, newer password should be considered.
182198

@@ -1656,8 +1672,9 @@ def test_basic_prior_auth_auto_send(self):
16561672
auth_prior_handler.add_password(
16571673
None, request_url, user, password, is_authenticated=True)
16581674

1659-
is_auth = pwd_manager.is_authenticated(request_url)
1660-
self.assertTrue(is_auth)
1675+
self.assertTrue(pwd_manager.is_authenticated(request_url))
1676+
self.assertTrue(pwd_manager.is_authenticated(request_url + '/nested'))
1677+
self.assertFalse(pwd_manager.is_authenticated(request_url + 'plain'))
16611678

16621679
opener = OpenerDirector()
16631680
opener.add_handler(auth_prior_handler)

Lib/urllib/request.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -887,10 +887,10 @@ def is_suburi(self, base, test):
887887
return True
888888
if base[0] != test[0]:
889889
return False
890-
common = posixpath.commonprefix((base[1], test[1]))
891-
if len(common) == len(base[1]):
892-
return True
893-
return False
890+
prefix = base[1]
891+
if prefix[-1:] != '/':
892+
prefix += '/'
893+
return test[1].startswith(prefix)
894894

895895

896896
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)