Loading django/http/request.py +21 −2 Original line number Diff line number Diff line Loading @@ -68,8 +68,11 @@ class HttpRequest(object): '<%s: %s %r>' % (self.__class__.__name__, self.method, force_str(self.get_full_path())) ) def get_host(self): """Returns the HTTP host using the environment or request headers.""" def _get_raw_host(self): """ Return the HTTP host using the environment or request headers. Skip allowed hosts protection, so may return an insecure host. """ # We try three options, in order of decreasing preference. if settings.USE_X_FORWARDED_HOST and ( 'HTTP_X_FORWARDED_HOST' in self.META): Loading @@ -82,6 +85,11 @@ class HttpRequest(object): server_port = self.get_port() if server_port != ('443' if self.is_secure() else '80'): host = '%s:%s' % (host, server_port) return host def get_host(self): """Return the HTTP host using the environment or request headers.""" host = self._get_raw_host() # There is no hostname validation when DEBUG=True if settings.DEBUG: Loading Loading @@ -138,6 +146,17 @@ class HttpRequest(object): raise return value def get_raw_uri(self): """ Return an absolute URI from variables available in this request. Skip allowed hosts protection, so may return insecure URI. """ return '{scheme}://{host}{path}'.format( scheme=self.scheme, host=self._get_raw_host(), path=self.get_full_path(), ) def build_absolute_uri(self, location=None): """ Builds an absolute URI from the location and the variables available in Loading django/views/debug.py +3 −3 Original line number Diff line number Diff line Loading @@ -669,7 +669,7 @@ TECHNICAL_500_TEMPLATE = (""" </tr> <tr> <th>Request URL:</th> <td>{{ request.build_absolute_uri|escape }}</td> <td>{{ request.get_raw_uri|escape }}</td> </tr> {% endif %} <tr> Loading Loading @@ -849,7 +849,7 @@ Environment: {% if request %} Request Method: {{ request.META.REQUEST_METHOD }} Request URL: {{ request.build_absolute_uri|escape }} Request URL: {{ request.get_raw_uri|escape }} {% endif %} Django Version: {{ django_version_info }} Python Version: {{ sys_version_info }} Loading Loading @@ -1048,7 +1048,7 @@ TECHNICAL_500_TEXT_TEMPLATE = ("""{% firstof exception_type 'Report' %}{% if req {% firstof exception_value 'No exception message supplied' %} {% if request %} Request Method: {{ request.META.REQUEST_METHOD }} Request URL: {{ request.build_absolute_uri }}{% endif %} Request URL: {{ request.get_raw_uri }}{% endif %} Django Version: {{ django_version_info }} Python Executable: {{ sys_executable }} Python Version: {{ sys_version_info }} Loading tests/logging_tests/tests.py +19 −0 Original line number Diff line number Diff line Loading @@ -352,6 +352,25 @@ class AdminEmailHandlerTest(SimpleTestCase): self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, ['manager@example.com']) @override_settings(ALLOWED_HOSTS='example.com') def test_disallowed_host_doesnt_crash(self): admin_email_handler = self.get_admin_email_handler(self.logger) old_include_html = admin_email_handler.include_html # Text email admin_email_handler.include_html = False try: self.client.get('/', HTTP_HOST='evil.com') finally: admin_email_handler.include_html = old_include_html # HTML email admin_email_handler.include_html = True try: self.client.get('/', HTTP_HOST='evil.com') finally: admin_email_handler.include_html = old_include_html class SettingsConfigTest(AdminScriptTestCase): """ Loading tests/requests/tests.py +12 −0 Original line number Diff line number Diff line Loading @@ -502,6 +502,18 @@ class RequestsTests(SimpleTestCase): with self.assertRaises(UnreadablePostError): request.FILES @override_settings(ALLOWED_HOSTS=['example.com']) def test_get_raw_uri(self): factory = RequestFactory(HTTP_HOST='evil.com') request = factory.get('////absolute-uri') self.assertEqual(request.get_raw_uri(), 'http://evil.com//absolute-uri') request = factory.get('/?foo=bar') self.assertEqual(request.get_raw_uri(), 'http://evil.com/?foo=bar') request = factory.get('/path/with:colons') self.assertEqual(request.get_raw_uri(), 'http://evil.com/path/with:colons') class HostValidationTests(SimpleTestCase): poisoned_hosts = [ Loading tests/view_tests/tests/test_debug.py +16 −0 Original line number Diff line number Diff line Loading @@ -439,6 +439,14 @@ class ExceptionReporterTests(SimpleTestCase): "Evaluation exception reason not mentioned in traceback" ) @override_settings(ALLOWED_HOSTS='example.com') def test_disallowed_host(self): "An exception report can be generated even for a disallowed host." request = self.rf.get('/', HTTP_HOST='evil.com') reporter = ExceptionReporter(request, None, None, None) html = reporter.get_traceback_html() self.assertIn("http://evil.com/", html) class PlainTextReportTests(SimpleTestCase): rf = RequestFactory() Loading Loading @@ -495,6 +503,14 @@ class PlainTextReportTests(SimpleTestCase): reporter = ExceptionReporter(None, None, "I'm a little teapot", None) reporter.get_traceback_text() @override_settings(ALLOWED_HOSTS='example.com') def test_disallowed_host(self): "An exception report can be generated even for a disallowed host." request = self.rf.get('/', HTTP_HOST='evil.com') reporter = ExceptionReporter(request, None, None, None) text = reporter.get_traceback_text() self.assertIn("http://evil.com/", text) class ExceptionReportTestMixin(object): Loading Loading
django/http/request.py +21 −2 Original line number Diff line number Diff line Loading @@ -68,8 +68,11 @@ class HttpRequest(object): '<%s: %s %r>' % (self.__class__.__name__, self.method, force_str(self.get_full_path())) ) def get_host(self): """Returns the HTTP host using the environment or request headers.""" def _get_raw_host(self): """ Return the HTTP host using the environment or request headers. Skip allowed hosts protection, so may return an insecure host. """ # We try three options, in order of decreasing preference. if settings.USE_X_FORWARDED_HOST and ( 'HTTP_X_FORWARDED_HOST' in self.META): Loading @@ -82,6 +85,11 @@ class HttpRequest(object): server_port = self.get_port() if server_port != ('443' if self.is_secure() else '80'): host = '%s:%s' % (host, server_port) return host def get_host(self): """Return the HTTP host using the environment or request headers.""" host = self._get_raw_host() # There is no hostname validation when DEBUG=True if settings.DEBUG: Loading Loading @@ -138,6 +146,17 @@ class HttpRequest(object): raise return value def get_raw_uri(self): """ Return an absolute URI from variables available in this request. Skip allowed hosts protection, so may return insecure URI. """ return '{scheme}://{host}{path}'.format( scheme=self.scheme, host=self._get_raw_host(), path=self.get_full_path(), ) def build_absolute_uri(self, location=None): """ Builds an absolute URI from the location and the variables available in Loading
django/views/debug.py +3 −3 Original line number Diff line number Diff line Loading @@ -669,7 +669,7 @@ TECHNICAL_500_TEMPLATE = (""" </tr> <tr> <th>Request URL:</th> <td>{{ request.build_absolute_uri|escape }}</td> <td>{{ request.get_raw_uri|escape }}</td> </tr> {% endif %} <tr> Loading Loading @@ -849,7 +849,7 @@ Environment: {% if request %} Request Method: {{ request.META.REQUEST_METHOD }} Request URL: {{ request.build_absolute_uri|escape }} Request URL: {{ request.get_raw_uri|escape }} {% endif %} Django Version: {{ django_version_info }} Python Version: {{ sys_version_info }} Loading Loading @@ -1048,7 +1048,7 @@ TECHNICAL_500_TEXT_TEMPLATE = ("""{% firstof exception_type 'Report' %}{% if req {% firstof exception_value 'No exception message supplied' %} {% if request %} Request Method: {{ request.META.REQUEST_METHOD }} Request URL: {{ request.build_absolute_uri }}{% endif %} Request URL: {{ request.get_raw_uri }}{% endif %} Django Version: {{ django_version_info }} Python Executable: {{ sys_executable }} Python Version: {{ sys_version_info }} Loading
tests/logging_tests/tests.py +19 −0 Original line number Diff line number Diff line Loading @@ -352,6 +352,25 @@ class AdminEmailHandlerTest(SimpleTestCase): self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, ['manager@example.com']) @override_settings(ALLOWED_HOSTS='example.com') def test_disallowed_host_doesnt_crash(self): admin_email_handler = self.get_admin_email_handler(self.logger) old_include_html = admin_email_handler.include_html # Text email admin_email_handler.include_html = False try: self.client.get('/', HTTP_HOST='evil.com') finally: admin_email_handler.include_html = old_include_html # HTML email admin_email_handler.include_html = True try: self.client.get('/', HTTP_HOST='evil.com') finally: admin_email_handler.include_html = old_include_html class SettingsConfigTest(AdminScriptTestCase): """ Loading
tests/requests/tests.py +12 −0 Original line number Diff line number Diff line Loading @@ -502,6 +502,18 @@ class RequestsTests(SimpleTestCase): with self.assertRaises(UnreadablePostError): request.FILES @override_settings(ALLOWED_HOSTS=['example.com']) def test_get_raw_uri(self): factory = RequestFactory(HTTP_HOST='evil.com') request = factory.get('////absolute-uri') self.assertEqual(request.get_raw_uri(), 'http://evil.com//absolute-uri') request = factory.get('/?foo=bar') self.assertEqual(request.get_raw_uri(), 'http://evil.com/?foo=bar') request = factory.get('/path/with:colons') self.assertEqual(request.get_raw_uri(), 'http://evil.com/path/with:colons') class HostValidationTests(SimpleTestCase): poisoned_hosts = [ Loading
tests/view_tests/tests/test_debug.py +16 −0 Original line number Diff line number Diff line Loading @@ -439,6 +439,14 @@ class ExceptionReporterTests(SimpleTestCase): "Evaluation exception reason not mentioned in traceback" ) @override_settings(ALLOWED_HOSTS='example.com') def test_disallowed_host(self): "An exception report can be generated even for a disallowed host." request = self.rf.get('/', HTTP_HOST='evil.com') reporter = ExceptionReporter(request, None, None, None) html = reporter.get_traceback_html() self.assertIn("http://evil.com/", html) class PlainTextReportTests(SimpleTestCase): rf = RequestFactory() Loading Loading @@ -495,6 +503,14 @@ class PlainTextReportTests(SimpleTestCase): reporter = ExceptionReporter(None, None, "I'm a little teapot", None) reporter.get_traceback_text() @override_settings(ALLOWED_HOSTS='example.com') def test_disallowed_host(self): "An exception report can be generated even for a disallowed host." request = self.rf.get('/', HTTP_HOST='evil.com') reporter = ExceptionReporter(request, None, None, None) text = reporter.get_traceback_text() self.assertIn("http://evil.com/", text) class ExceptionReportTestMixin(object): Loading