A DOM sink is an API that interprets a string as markup, script, or another active format. Putting a case note into textContent displays its characters; putting the same note into innerHTML asks the browser to parse tags and attributes. The latter can create script execution paths if untrusted input is not correctly sanitized for that context. Framework escaping helps for ordinary text interpolation but can be bypassed by raw-HTML features. A content policy and Trusted Types can add enforcement in supporting browsers, yet neither makes an unsafe sanitizer safe. Start by asking whether markup is needed at all.
DOM Sinks and Trusted Content
Working case
Reviewer 29 writes a note containing angle brackets to describe a damaged <pipe> label. The note is later shown in a live review panel. If the client assigns it to innerHTML, the browser may treat it as markup, and a malicious collaborator can craft a note with active content. The correct ordinary-note path assigns text. If editors later need a restricted rich-text field, separate that field from plain notes, parse and sanitize with a maintained allowlist, and review links and embedded media deliberately.
Implementation boundary
function renderCaseNote(noteRegion, caseNote) {
noteRegion.textContent = caseNote;
}
renderCaseNote(document.querySelector("#case-note"), "Damaged <pipe> at level 47");The example never asks the browser to interpret the note as HTML. A production component should also keep the note's visible line breaks and length limits consistent with the storage contract. Do not decode entities and then pass the result to a markup sink. If rich HTML is an approved product need, use a maintained sanitizer, a narrow set of elements and attributes, and a Trusted Types policy where supported; test links, URLs, and nested content. Treat any bypass method in a rendering framework as an explicit review point.
Cost and boundaries
Text rendering is simple and avoids sanitizer maintenance. Rich text adds parsing, policy review, and compatibility costs: a sanitizer update can change stored content presentation, while an overly broad policy can allow active URLs or event handlers. A strict browser policy can surface unsafe legacy code during rollout, requiring inventory and migration. The performance cost is usually small for short notes, but sanitizing long documents on every render is avoidable; define where the approved transformation occurs and cache the safe representation only under a versioned policy.
Failure trace
A developer replaces textContent with innerHTML so line breaks appear automatically. A note that once displayed literally now creates an element with an event handler. A successful unit test using ordinary text does not catch it. Test hostile input through the full save-and-render path, including a reload and live update. Restore text rendering and use CSS or explicit text nodes for line breaks. Do not mark the note 'trusted' merely because it came back from the application's own database.
Verification
- Save and reload HTML-looking note text; confirm it remains text in the DOM.
- Audit raw-markup escape hatches and document every approved use.
- Test allowed and rejected constructs when a rich-text field is introduced.
Practice drill
Insert plain angle brackets, an HTML-looking link, an event-handler attribute, and an encoded entity into a note. Save it, reload, and inspect the DOM: each should remain text. Then search the codebase for markup sinks and list the few routes that genuinely need rich content. For those, document the sanitizer policy and test the exact allowed and rejected constructs. A browser policy report can help find missed sinks, but it is not an excuse to ship one permissive default policy.
Decision note
Default to text. Permit rich markup only through a named, reviewed content boundary with tests for the actual allowed subset.
Common Mistakes
- Using innerHTML for simple line breaks.
- Trusting content because it came from the application's database.
- Creating a Trusted Types policy that simply returns unsanitized input.
Connected lessons
Browser Security and Data Stewardship; Embedding and Browser Capability Headers; Dependency Integrity and Update Window; Telemetry Minimization and Retention; Untrusted output: escape by context and constrain scripts; Safe File Upload Pipeline; Web Components and Shadow Boundaries.
Apply and check
Build Project: private page trust review and review Web Development: platform and trust contracts quiz.
Further connections
Paste Import, Sanitization, and Link Policy.
Further connections
Browser Encryption Threat Model and Key Custody.
Further connections
Retrieved Content, Instructions, and Tool Permission.
