In a template engine that escapes interpolated values by default, what does a raw-output marker disable, and what must you then guarantee?
answer
- safe by default, with one documented hole
- the marker covers one interpolation only
- encoding happens at write time
- position decides the correct encoding
- sanitize, then narrow the marker
basics
~20 sThe raw marker skips the encoding the engine would apply to that one interpolated value, so the string is written as markup. The guarantee becomes yours: the value must come from your own code or a sanitizer.
solid answer
~50 sBy default an auto-escaping engine encodes every interpolated value as it writes it, so a string containing markup characters renders as visible text rather than becoming part of the document. A raw-output marker says "write these bytes through untouched" for that one placeholder, which means the value is now treated as trusted markup. Two guarantees replace the engine's: the value must be produced by code you control or by a sanitizer that emits an allow-listed subset, and the marker must be scoped to the narrowest expression rather than wrapped around a block or a whole layout. It also matters *where* the placeholder sits — some engines pick the encoding from the surrounding position, others apply one HTML-style encoding everywhere, and only the first kind protects a value that lands inside an attribute or a script block.
go deeper
Recall that the engine encodes interpolated values for you by default, and that the raw marker switches that off for one value which you are then responsible for.
Explain the mechanics: encoding happens at write time per placeholder, the correct encoding depends on where the placeholder sits, and the marker is scoped to a single interpolation rather than the file.
Demonstrate the review discipline — sanitize with a parser-based allow-list, keep the marker narrow and greppable, refuse global disabling, and recognise double-encoding as a duplication bug rather than a reason to go raw.
Argue the platform choice: a trust-carrying value type versus call-site markers, whether rich text should be stored as markup at all, and how you make every exception countable across a codebase.
Auto-escaping is the single feature that makes server-side templating safe by default, and the raw-output marker is the documented hole in it. Understanding exactly what the marker switches off — and what it does not — is the difference between a deliberate, reviewed exception and a script-injection hole. ## What auto-escaping actually does When the engine reaches a placeholder it evaluates the expression, converts the result to text, and then **encodes** that text for the output it is writing before appending it to the response buffer. Encoding replaces the characters that would otherwise be read as markup syntax with their inert representations, so a value containing angle brackets, quotes or ampersands renders as those visible characters instead of becoming structure. Two properties are worth stating precisely: - Escaping happens **at write time, per placeholder**, not to the model and not to the stored data. The same value can be written twice in one page and encoded differently each time. - Escaping is **position-dependent**. Text between elements, a quoted attribute value, an unquoted attribute value, a URL-valued attribute and a script or style block are parsed by different rules, so the correct encoding is not one fixed transformation. ## Contextual versus single-mode engines | Engine behaviour | What it encodes | What it still gets wrong | |---|---|---| | Contextual auto-escaping | picks the encoding from the position the placeholder sits in | little, while the surrounding markup is static and parseable at compile time | | Single-mode auto-escaping | one document-level encoding everywhere | values placed in script blocks, unquoted attributes or URL-valued attributes | | No auto-escaping (opt-in) | nothing unless the template asks | everything a template author forgets, which is the failure mode of "remember to escape" | This is the honest generic statement: frameworks differ here — some engines track the position and vary the encoding, others apply a single encoding and leave the rest to the author, and a few still default to no escaping and require an explicit call. Knowing which kind you are on decides how much the default is really buying you. ## What the raw marker turns off, precisely The marker applies to **one interpolation**. It does not disable escaping for the template, the layout, or the included fragments — it says that this particular string is already markup and should be appended verbatim. Consequences: - Whatever produced the string is now the security boundary. If any part of it derives from user input without transformation, the user can contribute markup and script. - The marker composes badly with concatenation. Building a string out of a trusted wrapper plus an untrusted value and then marking the whole thing raw is the most common way the hole opens; the untrusted half is carried through the marker. - Some engines represent trust as a **type** rather than a marker: a value of a "safe markup" type is written unescaped and any plain string is escaped. That is strictly better, because trust travels with the value and survives being passed into a fragment instead of being a property of the call site. ## Making a raw output defensible When the requirement is genuine — stored rich text, a preformatted mail body, markup assembled by your own component code — the following discipline keeps it reviewable: 1. **Sanitize on the way to the page**, with a parser-based sanitizer that emits an allow-list of elements and attributes, rather than trying to remove the dangerous parts of a string with pattern matching. 2. **Narrow the marker** to the smallest expression. `raw(article.body)` is auditable; a raw block wrapping a header, a nav and a body is not. 3. **Do not place raw output in a position the sanitizer did not assume.** A sanitizer that produces safe element content produces nothing you can drop unquoted into an attribute or into a script block. 4. **Make it greppable.** A single well-known spelling of the marker means a reviewer or a lint rule can enumerate every exception in the codebase; several spellings and a global switch mean nobody can. ## Where the hatch leaks in practice - **Global disable.** Turning auto-escaping off for a directory or a build "because the templates are reviewed" converts every placeholder into an exception and removes the property you were relying on. - **Double-encoding workarounds.** A value encoded once by application code and again by the engine renders visible entity text; the wrong fix is to mark it raw, and the right fix is to stop encoding it early and let the engine do it once. - **Rich text stored as markup.** If a value is stored as markup, every consumer must treat it as markup — a second page that renders the same field escaped shows the tags, and a third that renders it raw without sanitizing inherits the hole. - **Values reaching a template through a fragment parameter.** A call-site marker says nothing about what happens two fragments deeper; trust-carrying types survive that journey, bare markers do not.
- Why is a trust-carrying value type better than a raw marker at the call site?A marker is a property of one template location, so trust cannot travel: pass the value into a fragment and the fragment has no idea whether it was vetted. A type says "this string is already markup" and follows the value wherever it goes, which lets the engine escape plain strings and pass safe ones through without the author repeating a decision at every call site.
- A user-supplied value renders as visible entity text instead of the intended characters. What is the right fix?Find where it is being encoded twice — usually application code encoded it before putting it in the model, and the engine encoded it again at write time. Remove the early encoding and let the engine encode once at the point of output. Marking the placeholder raw also makes the symptom disappear, but it removes the protection instead of fixing the duplication.
- Does auto-escaping cover a value written inside a script block?Only on engines that choose the encoding from the surrounding position. A single-mode engine applies a document-level encoding that is wrong inside a script block, where the parsing rules differ, so a value can break out even with escaping nominally on. Keep dynamic values out of script blocks and pass them through data attributes or a fetched data endpoint instead.
saying these in an interview costs you the question
- Claims auto-escaping makes a value safe wherever it appears in the page
- Uses the raw marker so text containing angle brackets displays correctly
- Concatenates a trusted wrapper with untrusted input, then marks the result raw
- Turns auto-escaping off globally because templates are code-reviewed
- Thinks the raw marker on one placeholder disables escaping for the whole template
- Fixes doubled entity text by marking the placeholder raw