Skip to content

Commit 115d166

Browse files
committed
test for json sanitization
1 parent 9543afe commit 115d166

File tree

4 files changed

+35
-5
lines changed

4 files changed

+35
-5
lines changed

Diff for: packages/python/plotly/plotly/io/_json.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,18 @@ def coerce_to_strict(const):
5757
return const
5858

5959

60-
_swap = (
60+
_swap_json = (
6161
("<", "\\u003c"),
6262
(">", "\\u003e"),
6363
("/", "\\u002f"),
64+
)
65+
_swap_orjson = _swap_json + (
6466
("\u2028", "\\u2028"),
6567
("\u2029", "\\u2029"),
6668
)
6769

6870

69-
def _safe(json_str):
71+
def _safe(json_str, _swap):
7072
out = json_str
7173
for unsafe_char, safe_char in _swap:
7274
if unsafe_char in out:
@@ -137,7 +139,9 @@ def to_json_plotly(plotly_object, pretty=False, engine=None):
137139

138140
from _plotly_utils.utils import PlotlyJSONEncoder
139141

140-
return _safe(json.dumps(plotly_object, cls=PlotlyJSONEncoder, **opts))
142+
return _safe(
143+
json.dumps(plotly_object, cls=PlotlyJSONEncoder, **opts), _swap_json
144+
)
141145
elif engine == "orjson":
142146
JsonConfig.validate_orjson()
143147
opts = orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY
@@ -153,7 +157,9 @@ def to_json_plotly(plotly_object, pretty=False, engine=None):
153157

154158
# Try without cleaning
155159
try:
156-
return _safe(orjson.dumps(plotly_object, option=opts).decode("utf8"))
160+
return _safe(
161+
orjson.dumps(plotly_object, option=opts).decode("utf8"), _swap_orjson
162+
)
157163
except TypeError:
158164
pass
159165

@@ -163,7 +169,7 @@ def to_json_plotly(plotly_object, pretty=False, engine=None):
163169
datetime_allowed=True,
164170
modules=modules,
165171
)
166-
return _safe(orjson.dumps(cleaned, option=opts).decode("utf8"))
172+
return _safe(orjson.dumps(cleaned, option=opts).decode("utf8"), _swap_orjson)
167173

168174

169175
def to_json(fig, validate=True, pretty=False, remove_uids=True, engine=None):

Diff for: packages/python/plotly/plotly/tests/test_io/test_to_from_plotly_json.py

+22
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,25 @@ def test_mixed_string_nonstring_key(engine, pretty):
221221
value = build_test_dict({0: 1, "a": 2})
222222
result = pio.to_json_plotly(value, engine=engine)
223223
check_roundtrip(result, engine=engine, pretty=pretty)
224+
225+
226+
def test_sanitize_json(engine):
227+
layout = {"title": {"text": "</script>\u2028\u2029"}}
228+
fig = go.Figure(layout=layout)
229+
fig_json = pio.to_json_plotly(fig, engine=engine)
230+
layout_2 = json.loads(fig_json)["layout"]
231+
del layout_2["template"]
232+
233+
assert layout == layout_2
234+
235+
replacements = {
236+
"<": "\\u003c",
237+
">": "\\u003e",
238+
"/": "\\u002f",
239+
"\u2028": "\\u2028",
240+
"\u2029": "\\u2029",
241+
}
242+
243+
for bad, good in replacements.items():
244+
assert bad not in fig_json
245+
assert good in fig_json

Diff for: packages/python/plotly/test_requirements/requirements_37_optional.txt

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ matplotlib==2.2.3
1919
scikit-image==0.14.4
2020
psutil==5.7.0
2121
kaleido
22+
orjson==3.8.12

Diff for: packages/python/plotly/test_requirements/requirements_39_optional.txt

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ matplotlib==2.2.3
1919
scikit-image==0.18.1
2020
psutil==5.7.0
2121
kaleido
22+
orjson==3.8.12

0 commit comments

Comments
 (0)