mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-08 18:44:10 +00:00
don't confuse :// in next_url query params for a redirect hostname
This commit is contained in:
@@ -635,29 +635,32 @@ class BaseHandler(RequestHandler):
|
|||||||
next_url = next_url.replace('\\', '%5C')
|
next_url = next_url.replace('\\', '%5C')
|
||||||
proto = get_browser_protocol(self.request)
|
proto = get_browser_protocol(self.request)
|
||||||
host = self.request.host
|
host = self.request.host
|
||||||
|
if next_url.startswith("///"):
|
||||||
|
# strip more than 2 leading // down to 2
|
||||||
|
# because urlparse treats that as empty netloc,
|
||||||
|
# whereas browsers treat more than two leading // the same as //,
|
||||||
|
# so netloc is the first non-/ bit
|
||||||
|
next_url = "//" + next_url.lstrip("/")
|
||||||
|
parsed_next_url = urlparse(next_url)
|
||||||
|
|
||||||
if (next_url + '/').startswith((f'{proto}://{host}/', f'//{host}/',)) or (
|
if (next_url + '/').startswith((f'{proto}://{host}/', f'//{host}/',)) or (
|
||||||
self.subdomain_host
|
self.subdomain_host
|
||||||
and urlparse(next_url).netloc
|
and parsed_next_url.netloc
|
||||||
and ("." + urlparse(next_url).netloc).endswith(
|
and ("." + parsed_next_url.netloc).endswith(
|
||||||
"." + urlparse(self.subdomain_host).netloc
|
"." + urlparse(self.subdomain_host).netloc
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
# treat absolute URLs for our host as absolute paths:
|
# treat absolute URLs for our host as absolute paths:
|
||||||
# below, redirects that aren't strictly paths
|
# below, redirects that aren't strictly paths are rejected
|
||||||
parsed = urlparse(next_url)
|
next_url = parsed_next_url.path
|
||||||
next_url = parsed.path
|
if parsed_next_url.query:
|
||||||
if parsed.query:
|
next_url = next_url + '?' + parsed_next_url.query
|
||||||
next_url = next_url + '?' + parsed.query
|
if parsed_next_url.fragment:
|
||||||
if parsed.fragment:
|
next_url = next_url + '#' + parsed_next_url.fragment
|
||||||
next_url = next_url + '#' + parsed.fragment
|
parsed_next_url = urlparse(next_url)
|
||||||
|
|
||||||
# if it still has host info, it didn't match our above check for *this* host
|
# if it still has host info, it didn't match our above check for *this* host
|
||||||
if next_url and (
|
if next_url and (parsed_next_url.netloc or not next_url.startswith('/')):
|
||||||
'://' in next_url
|
|
||||||
or next_url.startswith('//')
|
|
||||||
or not next_url.startswith('/')
|
|
||||||
):
|
|
||||||
self.log.warning("Disallowing redirect outside JupyterHub: %r", next_url)
|
self.log.warning("Disallowing redirect outside JupyterHub: %r", next_url)
|
||||||
next_url = ''
|
next_url = ''
|
||||||
|
|
||||||
|
@@ -768,6 +768,10 @@ async def test_login_strip(app):
|
|||||||
(False, '/user/other', '/hub/user/other', None),
|
(False, '/user/other', '/hub/user/other', None),
|
||||||
(False, '/absolute', '/absolute', None),
|
(False, '/absolute', '/absolute', None),
|
||||||
(False, '/has?query#andhash', '/has?query#andhash', None),
|
(False, '/has?query#andhash', '/has?query#andhash', None),
|
||||||
|
# :// in query string or fragment
|
||||||
|
(False, '/has?repo=https/host.git', '/has?repo=https/host.git', None),
|
||||||
|
(False, '/has?repo=https://host.git', '/has?repo=https://host.git', None),
|
||||||
|
(False, '/has#repo=https://host.git', '/has#repo=https://host.git', None),
|
||||||
# next_url outside is not allowed
|
# next_url outside is not allowed
|
||||||
(False, 'relative/path', '', None),
|
(False, 'relative/path', '', None),
|
||||||
(False, 'https://other.domain', '', None),
|
(False, 'https://other.domain', '', None),
|
||||||
@@ -807,7 +811,9 @@ async def test_login_redirect(app, running, next_url, location, params):
|
|||||||
if params:
|
if params:
|
||||||
url = url_concat(url, params)
|
url = url_concat(url, params)
|
||||||
if next_url:
|
if next_url:
|
||||||
if '//' not in next_url and next_url.startswith('/'):
|
if next_url.startswith('/') and not (
|
||||||
|
next_url.startswith("//") or urlparse(next_url).netloc
|
||||||
|
):
|
||||||
next_url = ujoin(app.base_url, next_url, '')
|
next_url = ujoin(app.base_url, next_url, '')
|
||||||
url = url_concat(url, dict(next=next_url))
|
url = url_concat(url, dict(next=next_url))
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user