From 77254c102d8a9b410ddf6afa6a04286fd669b970 Mon Sep 17 00:00:00 2001 From: Joe Gregorio Date: Mon, 27 Aug 2012 14:13:22 -0400 Subject: [PATCH] Escape untrusted content before displaying it. Reviewed in https://codereview.appspot.com/6460120/. --- oauth2client/appengine.py | 17 +++++++++++++++-- tests/test_oauth2client_appengine.py | 13 ++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/oauth2client/appengine.py b/oauth2client/appengine.py index e9cb17e..5439a35 100644 --- a/oauth2client/appengine.py +++ b/oauth2client/appengine.py @@ -20,6 +20,7 @@ Utilities for making it easier to use OAuth 2.0 on Google App Engine. __author__ = 'jcgregorio@google.com (Joe Gregorio)' import base64 +import cgi import httplib2 import logging import os @@ -51,6 +52,18 @@ OAUTH2CLIENT_NAMESPACE = 'oauth2client#ns' XSRF_MEMCACHE_ID = 'xsrf_secret_key' +def _safe_html(s): + """Escape text to make it safe to display. + + Args: + s: string, The text to escape. + + Returns: + The escaped text as a string. + """ + return cgi.escape(s, quote=1).replace("'", ''') + + class InvalidClientSecretsError(Exception): """The client_secrets.json file is malformed or missing required fields.""" @@ -417,7 +430,7 @@ class OAuth2Decorator(object): def _display_error_message(self, request_handler): request_handler.response.out.write('') - request_handler.response.out.write(self._message) + request_handler.response.out.write(_safe_html(self._message)) request_handler.response.out.write('') def oauth_required(self, method): @@ -578,7 +591,7 @@ class OAuth2Decorator(object): if error: errormsg = self.request.get('error_description', error) self.response.out.write( - 'The authorization request failed: %s' % errormsg) + 'The authorization request failed: %s' % _safe_html(errormsg)) else: user = users.get_current_user() decorator._create_flow(self) diff --git a/tests/test_oauth2client_appengine.py b/tests/test_oauth2client_appengine.py index 1c2d17a..6039a0d 100644 --- a/tests/test_oauth2client_appengine.py +++ b/tests/test_oauth2client_appengine.py @@ -331,7 +331,10 @@ class DecoratorTests(unittest.TestCase): webapp2.Route(r'/bar_path//', handler=TestAwareHandler, name='bar')], debug=True) - self.app = TestApp(application) + self.app = TestApp(application, extra_environ={ + 'wsgi.url_scheme': 'http', + 'HTTP_HOST': 'localhost', + }) users.get_current_user = user_mock() self.httplib2_orig = httplib2.Http httplib2.Http = Http2Mock @@ -343,7 +346,7 @@ class DecoratorTests(unittest.TestCase): def test_required(self): # An initial request to an oauth_required decorated path should be a # redirect to start the OAuth dance. - response = self.app.get('/foo_path') + response = self.app.get('http://localhost/foo_path') self.assertTrue(response.status.startswith('302')) q = parse_qs(response.headers['Location'].split('?', 1)[1]) self.assertEqual('http://localhost/oauth2callback', q['redirect_uri'][0]) @@ -425,7 +428,7 @@ class DecoratorTests(unittest.TestCase): def test_aware(self): # An initial request to an oauth_aware decorated path should not redirect. - response = self.app.get('/bar_path/2012/01') + response = self.app.get('http://localhost/bar_path/2012/01') self.assertEqual('Hello World!', response.body) self.assertEqual('200 OK', response.status) self.assertEqual(False, self.decorator.has_credentials()) @@ -471,10 +474,10 @@ class DecoratorTests(unittest.TestCase): response = self.app.get('/bar_path/2012/01') url = self.decorator.authorize_url() response = self.app.get('/oauth2callback', { - 'error': 'BadStuffHappened' + 'error': 'BadHappened\'' }) self.assertEqual('200 OK', response.status) - self.assertTrue('BadStuffHappened' in response.body) + self.assertTrue('Bad<Stuff>Happened'' in response.body) def test_kwargs_are_passed_to_underlying_flow(self): decorator = OAuth2Decorator(client_id='foo_client_id',