Skip to content

Instantly share code, notes, and snippets.

@kageurufu
Last active May 30, 2019 23:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kageurufu/64dbabcbf17e15fd8360caa8e44211f4 to your computer and use it in GitHub Desktop.
Save kageurufu/64dbabcbf17e15fd8360caa8e44211f4 to your computer and use it in GitHub Desktop.
Ultra-minimal Python to HTML DOM renderer, using MarkupSafe for escaping
import html
from markupsafe import Markup
class InvalidVoidTag(Exception):
pass
VOID_TAGS = {
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr",
}
def dom(tag: str, *children) -> str:
"""
>>> dom('html')
Markup('<html></html>')
>>> dom('html', ('head', ), ('body', ))
Markup('<html><head></head><body></body></html>')
>>> dom("script", 'var value = "this is a test script, we should not strip anything but </script>"')
Markup('<script>var value = "this is a test script, we should not strip anything but <\\\\/script>"</script>')
>>> dom("style", ".test-class { background-color: gray; }")
Markup('<style>.test-class { background-color: gray; }</style>')
>>> dom("body", {"class": "test-class"})
Markup('<body class="test-class"></body>')
>>> dom("p", "and we even prevent basic <script>xss</script> and <b>tag injection")
Markup('<p>and we even prevent basic &lt;script&gt;xss&lt;/script&gt; and &lt;b&gt;tag injection</p>')
>>> dom('img', ('test',))
Traceback (most recent call last):
...
e.InvalidVoidTag: Void tag img may not have child elements...
>>> dom( \
"html", \
( \
"head", \
("script", 'var value = "this is a test script, we should not strip anything but </script>"'), \
("style", ".test-class { background-color: gray; }"), \
), \
( \
"body", \
{"class": "test-class"}, \
( \
"div", \
("h1", "Heading"), \
( \
"article", \
("p", "some content goes here!"), \
("p", "some more content here"), \
( \
"p", \
"and we even prevent basic <script>xss</script> and <b>tag injection", \
("img", {"src": "https://avatars1.githubusercontent.com/u/259751"}), \
), \
), \
), \
), \
)
Markup('<html><head><script>var value = "this is a test script, we should not strip anything but <\\\\/script>"</script><style>.test-class { background-color: gray; }</style></head><body class="test-class"><div><h1>Heading</h1><article><p>some content goes here!</p><p>some more content here</p><p>and we even prevent basic &lt;script&gt;xss&lt;/script&gt; and &lt;b&gt;tag injection<img src="https://avatars1.githubusercontent.com/u/259751"></p></article></div></body></html>')
"""
html_attrs = []
if children and isinstance(children[0], dict):
attrs, *children = children
for k, v in attrs.items():
if v in (True, False):
html_attrs.append(f" {k}")
else:
html_attrs.append(f' {k}="{html.escape(v)}"')
open_tag = Markup(f"<{tag}{''.join(html_attrs)}>")
end_tag = Markup(f"</{tag}>")
if tag in VOID_TAGS:
if children:
raise InvalidVoidTag(
f"Void tag {tag} may not have child elements\n"
f"See https://www.w3.org/TR/html5/syntax.html#start-tags for more details"
)
return open_tag
return (
open_tag
+ Markup("").join(
dom(*c) # render sub-tuples to dom
if isinstance(c, tuple)
else Markup(c.replace("</script>", "<\\/script>")) # don't escape script or style bodies
if tag == "script"
else Markup(c.replace("</style>", "<\\/style>"))
if tag == "style"
else c # Use plaintext to make markupsafe escape it
for c in children
)
+ end_tag
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment