Merge lp:~stevenk/launchpad/handle-invalid-unicode-in-query-string into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 16461
Proposed branch: lp:~stevenk/launchpad/handle-invalid-unicode-in-query-string
Merge into: lp:launchpad
Diff against target: 77 lines (+23/-4)
2 files modified
lib/lp/services/webapp/servers.py (+10/-1)
lib/lp/services/webapp/tests/test_servers.py (+13/-3)
To merge this branch: bzr merge lp:~stevenk/launchpad/handle-invalid-unicode-in-query-string
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+146029@code.launchpad.net

Commit message

If we fail to decode the query string parameters, forcibly decode it to utf-8 with replacements.

Description of the change

If BrowserRequest._decode() fails to decode a string using the users charsets, it will leave it as a string. Which means that an invalid utf-8 bare string can be tossed into a TextWidget, which expects unicode, and blows up with a OOPS when it attempts to decode it.

Now, if we get back a string from BrowserRequest._decode(), we will forcibly decode it to utf-8 with replacements.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/services/webapp/servers.py'
--- lib/lp/services/webapp/servers.py 2013-01-07 02:40:55 +0000
+++ lib/lp/services/webapp/servers.py 2013-02-01 02:59:23 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Definition of the internet servers that Launchpad uses."""4"""Definition of the internet servers that Launchpad uses."""
@@ -657,6 +657,15 @@
657 """As per zope.publisher.browser.BrowserRequest._createResponse"""657 """As per zope.publisher.browser.BrowserRequest._createResponse"""
658 return LaunchpadBrowserResponse()658 return LaunchpadBrowserResponse()
659659
660 def _decode(self, text):
661 text = super(LaunchpadBrowserRequest, self)._decode(text)
662 if isinstance(text, str):
663 # BrowserRequest._decode failed to do so with the user-specified
664 # charsets, so decode as UTF-8 with replacements, since we always
665 # want unicode.
666 text = unicode(text, 'utf-8', 'replace')
667 return text
668
660 @cachedproperty669 @cachedproperty
661 def form_ng(self):670 def form_ng(self):
662 """See ILaunchpadBrowserApplicationRequest."""671 """See ILaunchpadBrowserApplicationRequest."""
663672
=== modified file 'lib/lp/services/webapp/tests/test_servers.py'
--- lib/lp/services/webapp/tests/test_servers.py 2013-01-07 02:40:55 +0000
+++ lib/lp/services/webapp/tests/test_servers.py 2013-02-01 02:59:23 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4__metaclass__ = type4__metaclass__ = type
@@ -50,6 +50,7 @@
50 EventRecorder,50 EventRecorder,
51 TestCase,51 TestCase,
52 )52 )
53from lp.testing.layers import FunctionalLayer
5354
5455
55class SetInWSGIEnvironmentTestCase(TestCase):56class SetInWSGIEnvironmentTestCase(TestCase):
@@ -230,8 +231,7 @@
230231
231 for method in denied_methods:232 for method in denied_methods:
232 env = self.wsgi_env(self.non_api_path, method)233 env = self.wsgi_env(self.non_api_path, method)
233 self.assert_(self.factory.canHandle(env),234 self.assert_(self.factory.canHandle(env), "Sanity check")
234 "Sanity check")
235 # Returns a tuple of (request_factory, publication_factory).235 # Returns a tuple of (request_factory, publication_factory).
236 rfactory, pfactory = self.factory.checkRequest(env)236 rfactory, pfactory = self.factory.checkRequest(env)
237 self.assert_(rfactory is not None,237 self.assert_(rfactory is not None,
@@ -352,6 +352,8 @@
352class TestBasicLaunchpadRequest(TestCase):352class TestBasicLaunchpadRequest(TestCase):
353 """Tests for the base request class"""353 """Tests for the base request class"""
354354
355 layer = FunctionalLayer
356
355 def test_baserequest_response_should_vary(self):357 def test_baserequest_response_should_vary(self):
356 """Test that our base response has a proper vary header."""358 """Test that our base response has a proper vary header."""
357 request = LaunchpadBrowserRequest(StringIO.StringIO(''), {})359 request = LaunchpadBrowserRequest(StringIO.StringIO(''), {})
@@ -387,6 +389,14 @@
387 request = LaunchpadBrowserRequest(StringIO.StringIO(''), env)389 request = LaunchpadBrowserRequest(StringIO.StringIO(''), env)
388 self.assertEquals(u'fnord/trunk\ufffd', request.getHeader('PATH_INFO'))390 self.assertEquals(u'fnord/trunk\ufffd', request.getHeader('PATH_INFO'))
389391
392 def test_request_with_invalid_query_string_recovers(self):
393 # When the query string has invalid utf-8, it is decoded with
394 # replacement.
395 env = {'QUERY_STRING': 'field.title=subproc\xe9s '}
396 request = LaunchpadBrowserRequest(StringIO.StringIO(''), env)
397 self.assertEquals(
398 [u'subproc\ufffds '], request.query_string_params['field.title'])
399
390400
391class TestFeedsBrowserRequest(TestCase):401class TestFeedsBrowserRequest(TestCase):
392 """Tests for `FeedsBrowserRequest`."""402 """Tests for `FeedsBrowserRequest`."""