1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-18 00:00:00 +00:00

[utils] Update is_safe_url

This commit is contained in:
zhengxie
2016-03-04 16:01:15 +08:00
parent c28fe89d4e
commit 4d0ac16a47
2 changed files with 83 additions and 35 deletions

View File

@@ -1,5 +1,3 @@
"""Copied from latest django/utils/http.py::is_safe_url
"""
import unicodedata
import urlparse
import json
@@ -20,39 +18,6 @@ class BadRequestException(_HTTPException):
class RequestForbbiddenException(_HTTPException):
pass
def is_safe_url(url, host=None):
"""
Return ``True`` if the url is a safe redirection (i.e. it doesn't point to
a different host and uses a safe scheme).
Always returns ``False`` on an empty url.
"""
if url is not None:
url = url.strip()
if not url:
return False
# Chrome treats \ completely as /
url = url.replace('\\', '/')
# Chrome considers any URL with more than two slashes to be absolute, but
# urlparse is not so flexible. Treat any url with three slashes as unsafe.
if url.startswith('///'):
return False
url_info = urlparse.urlparse(url)
# Forbid URLs like http:///example.com - with a scheme, but without a hostname.
# In that URL, example.com is not the hostname but, a path component. However,
# Chrome will still consider example.com to be the hostname, so we must not
# allow this syntax.
if not url_info.netloc and url_info.scheme:
return False
# Forbid URLs that start with control characters. Some browsers (like
# Chrome) ignore quite a few control characters at the start of a
# URL and might consider the URL as scheme relative.
if unicodedata.category(url[0])[0] == 'C':
return False
return ((not url_info.netloc or url_info.netloc == host) and
(not url_info.scheme or url_info.scheme in ['http', 'https']))
JSON_CONTENT_TYPE = 'application/json; charset=utf-8'
def json_response(func):
@wraps(func)
@@ -78,3 +43,39 @@ def int_param(request, key):
return int(v)
except ValueError:
raise BadRequestException()
def is_safe_url(url, host=None):
"""
https://github.com/django/django/blob/fc6d147a63f89795dbcdecb0559256470fff4380/django/utils/http.py
Return ``True`` if the url is a safe redirection (i.e. it doesn't point to
a different host and uses a safe scheme).
Always returns ``False`` on an empty url.
"""
if url is not None:
url = url.strip()
if not url:
return False
# Chrome treats \ completely as / in paths but it could be part of some
# basic auth credentials so we need to check both URLs.
return _is_safe_url(url, host) and _is_safe_url(url.replace('\\', '/'), host)
def _is_safe_url(url, host):
# Chrome considers any URL with more than two slashes to be absolute, but
# urlparse is not so flexible. Treat any url with three slashes as unsafe.
if url.startswith('///'):
return False
url_info = urlparse.urlparse(url)
# Forbid URLs like http:///example.com - with a scheme, but without a hostname.
# In that URL, example.com is not the hostname but, a path component. However,
# Chrome will still consider example.com to be the hostname, so we must not
# allow this syntax.
if not url_info.netloc and url_info.scheme:
return False
# Forbid URLs that start with control characters. Some browsers (like
# Chrome) ignore quite a few control characters at the start of a
# URL and might consider the URL as scheme relative.
if unicodedata.category(url[0])[0] == 'C':
return False
return ((not url_info.netloc or url_info.netloc == host) and
(not url_info.scheme or url_info.scheme in ['http', 'https']))

View File

@@ -0,0 +1,47 @@
from django.conf import settings
from django.core.urlresolvers import reverse
from seahub.test_utils import BaseTestCase
class LoginTest(BaseTestCase):
def test_can_login(self):
resp = self.client.post(
reverse('auth_login'), {'login': self.user.username,
'password': self.user_password}
)
self.assertEqual(302, resp.status_code)
self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL)
def test_redirect_to_after_success_login(self):
resp = self.client.post(
reverse('auth_login') + '?next=/foo/',
{'login': self.user.username,
'password': self.user_password}
)
self.assertEqual(302, resp.status_code)
self.assertRegexpMatches(resp['Location'], r'http://testserver/foo/')
def test_bad_redirect_to_after_success_login(self):
from django.utils.http import urlquote
resp = self.client.post(
reverse('auth_login') + '?next=' + urlquote('http://testserver\@example.com'),
{'login': self.user.username,
'password': self.user_password}
)
self.assertEqual(302, resp.status_code)
self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL)
def test_redirect_to_other_host_after_success_login(self):
from django.utils.http import urlquote
resp = self.client.post(
reverse('auth_login') + '?next=' + urlquote('http://foo.com'),
{'login': self.user.username,
'password': self.user_password}
)
self.assertEqual(302, resp.status_code)
self.assertRegexpMatches(resp['Location'], r'http://testserver%s' % settings.LOGIN_REDIRECT_URL)