Merge lp:~wgrant/lazr.restful/bug-684430 into lp:lazr.restful

Proposed by William Grant
Status: Merged
Approved by: Robert Collins
Approved revision: 190
Merged at revision: 188
Proposed branch: lp:~wgrant/lazr.restful/bug-684430
Merge into: lp:lazr.restful
Diff against target: 79 lines (+12/-4)
5 files modified
setup.py (+1/-1)
src/lazr/restful/NEWS.txt (+3/-0)
src/lazr/restful/_resource.py (+2/-1)
src/lazr/restful/docs/webservice.txt (+5/-1)
versions.cfg (+1/-1)
To merge this branch: bzr merge lp:~wgrant/lazr.restful/bug-684430
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+55678@code.launchpad.net

Commit message

The webservice:json TALES function now produces HTML-escaping-resilient JSON.

Description of the change

It is impossible to correctly insert the output of the webservice:json TALES function into a script tag in an HTML document. HTML (not XHTML) specifies <script>'s contents as CDATA, which doesn't expand entities but can be terminated by any closing tag. So you can't turn <, >, & into entities, as the JSON parser will see the undecoded entities. And if you don't escape them at all then malicious JSON can break out of the element. So we need to escape them some other way.

simplejson now provides a JSONEncoderForHTML, which replaces <, > and & with their corresponding Unicode escape sequences. Its output is resilient to escaping, allowing it to be safely inserted into a document.

I've changed ResourceJSONEncoder to inherit JSONEncoderForHTML instead of JSONEncoder. This is a little too broad: it will perform the extra escaping on content returned normally as application/json, not just text/html. But this is OK: the encodings are equivalent, decoding to identical strings. The only downsides are reduced readability and minor representation bloat.

JSONEncoderForHTML is new in simplejson 2.1.0, so I incremented the requirement.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'setup.py'
2--- setup.py 2011-03-21 19:54:33 +0000
3+++ setup.py 2011-03-31 04:54:29 +0000
4@@ -65,7 +65,7 @@
5 'martian==0.11',
6 'pytz',
7 'setuptools',
8- 'simplejson>=2.0.9',
9+ 'simplejson>=2.1.0',
10 'van.testing',
11 'wsgiref',
12 'zope.app.pagetemplate',
13
14=== modified file 'src/lazr/restful/NEWS.txt'
15--- src/lazr/restful/NEWS.txt 2011-03-25 11:37:15 +0000
16+++ src/lazr/restful/NEWS.txt 2011-03-31 04:54:29 +0000
17@@ -10,6 +10,9 @@
18 The object modification event will not be fired if a client sends an
19 empty changeset via PATCH.
20
21+The webservice:json TALES function now returns JSON that will survive
22+HTML escaping.
23+
24 0.18.0 (2011-03-23)
25 ===================
26
27
28=== modified file 'src/lazr/restful/_resource.py'
29--- src/lazr/restful/_resource.py 2011-03-25 11:40:57 +0000
30+++ src/lazr/restful/_resource.py 2011-03-31 04:54:29 +0000
31@@ -34,6 +34,7 @@
32 import copy
33 import os
34 import simplejson
35+import simplejson.encoder
36 import time
37
38 # Import SHA in a way compatible with both Python 2.4 and Python 2.6.
39@@ -199,7 +200,7 @@
40 pass
41
42
43-class ResourceJSONEncoder(simplejson.JSONEncoder):
44+class ResourceJSONEncoder(simplejson.encoder.JSONEncoderForHTML):
45 """A JSON encoder for JSON-exposable resources like entry resources.
46
47 This class works with simplejson to encode objects as JSON if they
48
49=== modified file 'src/lazr/restful/docs/webservice.txt'
50--- src/lazr/restful/docs/webservice.txt 2011-03-02 19:33:48 +0000
51+++ src/lazr/restful/docs/webservice.txt 2011-03-31 04:54:29 +0000
52@@ -2121,9 +2121,13 @@
53 False
54
55 The json() function converts generic Python data structures to JSON,
56-as well as objects that can be adapted to IEntry.
57+as well as objects that can be adapted to IEntry. It converts markup
58+characters (<, >, &) into their respective Unicode escape sequences,
59+since entities within <script> tags are not expanded.
60
61 >>> test_tales("context/webservice:json", context="foobar")
62 '"foobar"'
63 >>> test_tales("context/webservice:json", context=A1)
64 '{"name": ...}'
65+ >>> test_tales("context/webservice:json", context="<foo&>")
66+ '"\\u003cfoo\\u0026\\u003e"'
67
68=== modified file 'versions.cfg'
69--- versions.cfg 2011-03-21 19:54:33 +0000
70+++ versions.cfg 2011-03-31 04:54:29 +0000
71@@ -25,7 +25,7 @@
72 martian = 0.11
73 pytz = 2010h
74 setuptools = 0.6c11
75-simplejson = 2.0.9
76+simplejson = 2.1.3
77 transaction = 1.0.0
78 van.testing = 2.0.1
79 wsgi-intercept = 0.4

Subscribers

People subscribed via source and target branches