--- a/Allura/allura/lib/patches.py
+++ b/Allura/allura/lib/patches.py
@@ -15,10 +15,14 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import re
+
 import webob
 import tg.decorators
 from decorator import decorator
 from pylons import request
+import mock
+import simplejson
 
 from allura.lib import helpers as h
 
@@ -78,6 +82,25 @@
         return func(*args, **kwargs)
 
 
+    # http://blog.watchfire.com/wfblog/2011/10/json-based-xss-exploitation.html
+    # change < to its unicode escape when rendering JSON out of turbogears
+    # This is to avoid IE9 and earlier, which don't know the json content type
+    # and may attempt to render JSON data as HTML if the URL ends in .html
+    
+    original_tg_jsonify_GenericJSON_encode = tg.jsonify.GenericJSON.encode
+    escape_pattern_with_lt = re.compile(simplejson.encoder.ESCAPE.pattern.rstrip(']') + '<' + ']')
+
+    @h.monkeypatch(tg.jsonify.GenericJSON)
+    def encode(self, o):
+        # ensure_ascii=False forces encode_basestring() to be called instead of
+        # encode_basestring_ascii() and encode_basestring_ascii may likely be c-compiled
+        # and thus not monkeypatchable
+        with h.push_config(self, ensure_ascii=False), \
+             h.push_config(simplejson.encoder, ESCAPE=escape_pattern_with_lt), \
+             mock.patch.dict(simplejson.encoder.ESCAPE_DCT, {'<': r'\u003C'}):
+            return original_tg_jsonify_GenericJSON_encode(self, o)
+
+
 # must be saved outside the newrelic() method so that multiple newrelic()
 # calls (e.g. during tests) don't cause the patching to get applied to itself
 # over and over