diff kallithea/lib/webutils.py @ 8781:642fa51e0d0b

lib: move js escaping from helpers to webutils It was no real problem it lived in helpers: it was only used from templates anyway. But for completeness and correctness, move all web utils to webutils.
author Mads Kiilerich <mads@kiilerich.com>
date Sat, 07 Nov 2020 18:27:33 +0100
parents f8971422795e
children f375751fe3fa
line wrap: on
line diff
--- a/kallithea/lib/webutils.py	Fri Nov 06 21:15:20 2020 +0100
+++ b/kallithea/lib/webutils.py	Sat Nov 07 18:27:33 2020 +0100
@@ -20,6 +20,7 @@
 imported anywhere - just like the global variables can be used everywhere.
 """
 
+import json
 import logging
 import random
 
@@ -256,3 +257,52 @@
     The return value is a list of ``Message`` objects.
     """
     return [_Message(category, message) for category, message in _session_flash_messages(clear=True)]
+
+
+#
+# Generic-ish formatting and markup
+#
+
+def js(value):
+    """Convert Python value to the corresponding JavaScript representation.
+
+    This is necessary to safely insert arbitrary values into HTML <script>
+    sections e.g. using Mako template expression substitution.
+
+    Note: Rather than using this function, it's preferable to avoid the
+    insertion of values into HTML <script> sections altogether. Instead,
+    data should (to the extent possible) be passed to JavaScript using
+    data attributes or AJAX calls, eliminating the need for JS specific
+    escaping.
+
+    Note: This is not safe for use in attributes (e.g. onclick), because
+    quotes are not escaped.
+
+    Because the rules for parsing <script> varies between XHTML (where
+    normal rules apply for any special characters) and HTML (where
+    entities are not interpreted, but the literal string "</script>"
+    is forbidden), the function ensures that the result never contains
+    '&', '<' and '>', thus making it safe in both those contexts (but
+    not in attributes).
+    """
+    return literal(
+        ('(' + json.dumps(value) + ')')
+        # In JSON, the following can only appear in string literals.
+        .replace('&', r'\x26')
+        .replace('<', r'\x3c')
+        .replace('>', r'\x3e')
+    )
+
+
+def jshtml(val):
+    """HTML escapes a string value, then converts the resulting string
+    to its corresponding JavaScript representation (see `js`).
+
+    This is used when a plain-text string (possibly containing special
+    HTML characters) will be used by a script in an HTML context (e.g.
+    element.innerHTML or jQuery's 'html' method).
+
+    If in doubt, err on the side of using `jshtml` over `js`, since it's
+    better to escape too much than too little.
+    """
+    return js(escape(val))