cf.completefrontend

Write the codeMedium

Write an html tagged template that escapes every interpolated value.

Before you start

What's HTML escaping, and why does it matter?

When you build HTML as a string, some characters in it are instructions to the browser, not text: < starts a tag, and & starts a character reference. A character reference is a short code that stands for one character, such as &lt; for < and &amp; for &; they're often called HTML entities. The browser shows the character a reference stands for as text, so &lt; appears on the page as < but never starts a tag. HTML escaping means replacing the special characters in a piece of text with references, so the browser shows the text exactly as it was written.

It matters most when the text comes from someone else: a user's name, a comment, a search query, data from an API. Put into HTML as it is, any markup in it becomes part of your page, and markup can run JavaScript. That's called cross-site scripting (XSS): an attacker's code runs in your page with the same powers as your own, so it can read what the page shows and send requests as the signed-in user. Say a user sets their name to <img src=x onerror=alert(1)>, and the page greets them with "<p>Hello, " + name + "</p>":

The HTML the page buildsWhat the browser does
<p>Hello, <img src=x onerror=alert(1)></p>Creates an <img> element. x isn't a real image, so loading it fails, and the browser runs the code in onerror. An attacker would put something worse than alert(1) there.
<p>Hello, &lt;img src=x onerror=alert(1)&gt;</p>The name was escaped. The page shows the text Hello, <img src=x onerror=alert(1)>, as the user typed it, and nothing runs.

A <script> element in the name would run too, when the HTML is built on the server and sent as the page. Scripts added with innerHTML don't run, which is why attacks there use event attributes such as onerror instead.

Which characters matter depends on where the text goes:

CharacterWhere it mattersWhat it does there
<In an element's contentStarts a tag, such as <img, or an end tag, such as </p>.
&In content and in attribute valuesStarts a character reference, so a user who typed &lt; would see < instead.
"In an attribute value wrapped in "Ends the value early, and the rest becomes new attributes: a title of x" onmouseover="alert(1) adds an onmouseover handler.
'In an attribute value wrapped in 'Ends the value early, in the same way.
>Nowhere in content or in quoted attribute valuesIt only ends a tag that's already open. Escaping it anyway is the convention, and it does no harm.

Escaping protects text in an element's content and in a quoted attribute value. Other places, such as a URL in an href or code inside a <script>, need different protection. Day to day, you often don't escape by hand: setting textContent puts text into the page as text, and React and Vue treat values you put in with {name} and {{ name }} as text, unless you opt out with dangerouslySetInnerHTML or v-html. You need escaping when you build an HTML string yourself, for innerHTML or in a server-side template.

Use html as a template tag. It returns the template as one string: the literal parts exactly as written, and each interpolated value escaped.

To escape a value, replace each of these characters with its HTML entity: & with &amp;, < with &lt;, > with &gt;, " with &quot;, and ' with &#39;.

null and undefined become empty strings. Any other value is converted with String first, so 0 becomes "0".

Write this
function html(strings, ...values) {
  // your code
}
 
// const name = "<img src=x onerror=alert(1)>";
// html`<p>Hello, ${name}!</p>`;
// "<p>Hello, &lt;img src=x onerror=alert(1)&gt;!</p>"
// const title = '"Tom" & Jerry';
// html`<a title="${title}">${null}${0}</a>`;
// '<a title="&quot;Tom&quot; &amp; Jerry">0</a>'

The answer

Write it yourself first. Then compare it with a reference solution and the checks it passes.

Checking your sign-in…