Skip to content
Lesson 14 of 20

Step 1 of 5 · Reading · ~3 min

Read

Production Concerns

HTML Escaping

Markdown's output is HTML, and its input is whatever a user typed. Somewhere between those two facts sits every Markdown-powered security incident ever filed.

Which characters have to change?

CharacterBecomesWhy
<&lt;otherwise it opens a tag
>&gt;symmetry, and it closes one
&&amp;otherwise it opens an entity
"&quot;it terminates an attribute value

Escaping is a render-time job, not a parse-time one. You escape when you write a character into the HTML output — which means the contents of code spans and code blocks get escaped too. A <script> tag inside backticks must display as text, not execute.

Why is the ampersand the hard one?

Because it is the one character whose replacement contains itself. Replace blindly and you escape your own escapes.

python

Rows two and three are the bug: an author who already wrote an entity gets it mangled into visible &amp; noise. The fix is a lookahead — before escaping an ampersand, check whether it begins a well-formed entity reference, and if so copy it through untouched.

An "entity" here means one of three shapes: & plus letters plus ;, or &# plus decimal digits plus ;, or &#x plus hex digits plus ;. Anything else — &&, & c, an unterminated &amp — is a bare ampersand and gets escaped normally.

There is one more ordering rule hiding in the naive version: & must be replaced first. Escape < first and its replacement's own ampersand gets caught by the later pass, turning &lt; into &amp;lt;.

Is escaping enough to be safe?

No. CommonMark deliberately permits raw HTML blocks, so a conforming parser will happily pass <script> straight through when it appears at block level. Several widely used Markdown libraries have shipped cross-site-scripting advisories over the years for exactly this reason. For untrusted input the rule is: render with raw HTML disabled if your library supports it, and run the resulting HTML through a dedicated sanitizer regardless.

Your exercise

Escape a document, entity-aware.

The mistake the grader catches is double-escaping a valid entity. Visible test four feeds Already &amp; escaped and expects it back byte-for-byte; a plain str.replace returns &amp;amp; and fails. Hidden tests push further with &copy;, &#x41; and &#65;, so your detector needs all three entity shapes, not just the named one.

The other trap is the newline, though not in the way you might expect. This exercise writes with sys.stdout.write rather than print so the output mirrors the input exactly — the habit you want when a later stage will concatenate your result with something else. The grader itself is forgiving at the very end: its comparator trims trailing whitespace, so one stray newline after the last character is still accepted. Everywhere else the comparison is literal, so a newline dropped or added in the MIDDLE of a document is a real failure — which is why escape must copy through like any other character rather than working line by line.

Up nextBackslash EscapesProduction Concerns

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…