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:05 +0000

1# -*- coding: utf-8 -*- 

2from datetime import date, datetime 

3import json as json_ 

4import re 

5 

6import markupsafe 

7from .func import lazy 

8from .misc import ReadonlyDict 

9 

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 `&lt;` *in 

35 the resulting JSON/JS* not just inside the page). 

36 

37 However, failing to escape embedded json means the json strings could 

38 contains `</script>` and thus become XSS vector. 

39 

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. 

45 

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. 

49 

50 .. warning:: 

51 

52 except inside <script> elements, this should be escaped following 

53 the normal rules of the containing format 

54 

55 Cf https://code.djangoproject.com/ticket/17419#comment:27 

56 """ 

57 return _ScriptSafe(json_.dumps(*args, **kwargs)) 

58scriptsafe = JSON() 

59 

60 

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)