Markdown and code
Table of contents
Introduction
Every feature that stores written material renders it through one function, render_markdown. Cutting a stored body down to an allowlist is a security decision, and one copy of it is easier to keep right than several.
from splent_framework.markdown import render_markdown
@bp.route("/page/<slug>")
def page(slug):
page = service.get(slug)
return render_template("page.html", body_html=render_markdown(page.body_md))
The result is Markup, so your template renders it with {{ body_html }} and no filter.
Never write
| safeon a raw stored body, which runs whatever it contains. A body stored as HTML, from a rich text editor, goes through therich_textfilter instead.
What it does to a body
It reads GitHub-flavoured markdown, with tables, strikethrough and bare URLs. Raw HTML is allowed and cleaned afterwards, since imported bodies contain it. Heading anchors, definition lists, footnotes and task lists are on.
Then it sanitises everything with nh3, against nh3’s own allowlist plus a little more.
id,class,langandtitleon any element, for anchors, highlighting and imported styles. None of them can execute anything.<input>only as a disabled checkbox, which is what a task list is.- URLs only with
http,httpsormailto.
Opening external links in a new tab
render_markdown(page.body_md, external_links_new_tab=True)
Links with an absolute http or https address get target="_blank". It is off by default, since the feature that owns the material decides. Every link already carries rel="noopener noreferrer", and an author cannot choose a target, because the allowlist removes any target a body writes.
Highlighting code
Code is coloured on the server, at render time, by Pygments. No JavaScript is loaded, so code is never on screen uncoloured, and a page works printed or with JavaScript off.
<pre><code class="language-bash">vagrant init ubuntu/trusty32 <span class="tok-c1">#comentario</span>
</code></pre>
A language without a lexer renders plain, and never raises. Blocks tagged text, txt or plaintext are left alone, since colouring pasted output invents structure. Whitespace is never wrapped in markup.
Styling the colours
Token classes carry a tok- prefix, so they never clash with classes an imported body brings. Your theme maps them to a few custom properties, and a skin with a dark panel overrides them in one block.
.site {
--code-comment: #7c8797;
--code-keyword: #ff7bbf;
--code-string: #8ddb8d;
--code-number: #ffb86c;
--code-name: #c9a6ff;
--code-builtin: #79c0ff;
--code-added: #8ddb8d;
--code-removed: #ff8080;
}
Check the contrast against the panel, 4.5:1 at least. Colour only what carries meaning, and let punctuation and plain names inherit the text colour.