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?
| Character | Becomes | Why |
|---|---|---|
< | < | otherwise it opens a tag |
> | > | symmetry, and it closes one |
& | & | otherwise it opens an entity |
" | " | 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.
Rows two and three are the bug: an author who already wrote an entity gets it
mangled into visible & 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 & — 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 < into &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 & escaped and expects it back byte-for-byte; a
plain str.replace returns &amp; and fails. Hidden tests push further
with ©, A and A, 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.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…