Escape untrusted content before displaying it.
Reviewed in https://codereview.appspot.com/6460120/.
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 @@
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
import base64
+import cgi
import httplib2
import logging
import os
@@ -51,6 +52,18 @@
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 @@
def _display_error_message(self, request_handler):
request_handler.response.out.write('<html><body>')
- request_handler.response.out.write(self._message)
+ request_handler.response.out.write(_safe_html(self._message))
request_handler.response.out.write('</body></html>')
def oauth_required(self, method):
@@ -578,7 +591,7 @@
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 @@
webapp2.Route(r'/bar_path/<year:\d{4}>/<month:\d{2}>',
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 @@
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 @@
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 @@
response = self.app.get('/bar_path/2012/01')
url = self.decorator.authorize_url()
response = self.app.get('/oauth2callback', {
- 'error': 'BadStuffHappened'
+ 'error': 'Bad<Stuff>Happened\''
})
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',