Coverage for adhoc-cicd-odoo-odoo / odoo / tools / json.py: 33%
31 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 18:22 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 18:22 +0000
1# -*- coding: utf-8 -*-
2from datetime import date, datetime
3import json as json_
4import re
6import markupsafe
7from .func import lazy
8from .misc import ReadonlyDict
10JSON_SCRIPTSAFE_MAPPER = {
11 '&': r'\u0026',
12 '<': r'\u003c',
13 '>': r'\u003e',
14 '\u2028': r'\u2028',
15 '\u2029': r'\u2029'
16}
17class _ScriptSafe(str):
18 def __html__(self):
19 # replacement can be done straight in the serialised JSON as the
20 # problematic characters are not JSON metacharacters (and can thus
21 # only occur in strings)
22 return markupsafe.Markup(re.sub(
23 r'[<>&\u2028\u2029]',
24 lambda m: JSON_SCRIPTSAFE_MAPPER[m[0]],
25 self,
26 ))
27class JSON:
28 def loads(self, *args, **kwargs):
29 return json_.loads(*args, **kwargs)
30 def dumps(self, *args, **kwargs):
31 """ JSON used as JS in HTML (script tags) is problematic: <script>
32 tags are a special context which only waits for </script> but doesn't
33 interpret anything else, this means standard htmlescaping does not
34 work (it breaks double quotes, and e.g. `<` will become `<` *in
35 the resulting JSON/JS* not just inside the page).
37 However, failing to escape embedded json means the json strings could
38 contains `</script>` and thus become XSS vector.
40 The solution turns out to be very simple: use JSON-level unicode
41 escapes for HTML-unsafe characters (e.g. "<" -> "\u003C". This removes
42 the XSS issue without breaking the json, and there is no difference to
43 the end result once it's been parsed back from JSON. So it will work
44 properly even for HTML attributes or raw text.
46 Also handle U+2028 and U+2029 the same way just in case as these are
47 interpreted as newlines in javascript but not in JSON, which could
48 lead to oddities and issues.
50 .. warning::
52 except inside <script> elements, this should be escaped following
53 the normal rules of the containing format
55 Cf https://code.djangoproject.com/ticket/17419#comment:27
56 """
57 return _ScriptSafe(json_.dumps(*args, **kwargs))
58scriptsafe = JSON()
61def json_default(obj):
62 from odoo import fields # noqa: PLC0415
63 if isinstance(obj, datetime):
64 return fields.Datetime.to_string(obj)
65 if isinstance(obj, date):
66 return fields.Date.to_string(obj)
67 if isinstance(obj, lazy):
68 return obj._value
69 if isinstance(obj, ReadonlyDict):
70 return dict(obj)
71 if isinstance(obj, bytes):
72 return obj.decode()
73 if isinstance(obj, fields.Domain):
74 return list(obj)
75 return str(obj)