Safeguard
Application Security•

ASVS 5.0 V3.2.2: Render Text Safely With textContent

ASVS 5.0 V3.2.2 (Level 1) requires safe DOM functions for text content. Replace innerHTML with textContent, handle framework escape hatches, and test for XSS.

Safeguard Security Team
4 min read

ASVS 5.0 V3.2.2 requires that content meant to be displayed as text, not HTML, is handled with safe rendering functions such as createTextNode or textContent, so HTML or JavaScript inside it never executes. It is a Level 1 requirement.

Why it matters

DOM based cross site scripting happens in the browser, without the server ever seeing the payload. A comment, a name, or a URL fragment is written into the page with innerHTML, and the browser parses it as markup. A single img tag with an onerror handler is enough to run script with the user's session.

How to implement it

Use APIs that treat the value as text.

// Unsafe: the browser parses name as HTML
el.innerHTML = name;

// Safe: the browser shows it as characters
el.textContent = name;

// Also safe when building nodes
const li = document.createElement("li");
li.appendChild(document.createTextNode(comment));
list.appendChild(li);

Other sinks to treat with the same care: outerHTML, insertAdjacentHTML, document.write, eval, and assigning to href or src with an untrusted value. For URLs, parse them and allow only https: and http: schemes.

Frameworks escape by default, but have escape hatches: dangerouslySetInnerHTML in React, v-html in Vue and [innerHTML] in Angular. Use them only for HTML that you sanitized with a maintained library such as DOMPurify, and keep a code search for each of these names in CI. When rich text is a real need, sanitize on input and again before display, and apply a Content Security Policy as a second layer.

How to test it

Enter a probe such as an img tag with a broken source and an onerror handler into every field that appears on a page, including URL parameters and fragments. If an alert box or a request to your listener fires, the sink is unsafe. A static scan finds the sinks fast, then confirm the findings by hand.

Context matters: the sink list

Safe rendering depends on where the value lands. HTML body text is one context, attribute values another, URLs another, and script or style blocks a third. textContent solves the first. For attributes, use setAttribute with a value that is only data, and never build an event handler attribute from user text. For URLs, validate the scheme. For script and style, do not put user data there at all.

Templates and framework habits

Frameworks make the right thing the default, so the risk sits in the exceptions. Create a lint rule or a CI search that fails the build when a new dangerouslySetInnerHTML, v-html, bypassSecurityTrustHtml or raw innerHTML assignment appears without a reviewed comment. Treat every such line as a small security review.

Sanitizing when HTML is needed

Markdown previews, email viewers and rich text editors need HTML. Pass the value through a maintained sanitizer with a strict allowlist, then insert the result. Pin the library version and keep it patched, as sanitizers are attacked constantly. Add a Content Security Policy that forbids inline script, which turns many leftover bugs into non events.

Trusted Types

Chromium based browsers support Trusted Types, which forces every dangerous sink to receive a vetted object instead of a raw string. Turning it on in report only mode first shows you each place in your code that still writes strings into sinks.

Common mistakes

  • Switching to textContent in one place and leaving innerHTML in templates.
  • Escaping on the server and then inserting with innerHTML after JSON decoding.
  • Sanitizing with a hand written regex.
  • Using href with a javascript: value from user data.

Try it yourself

A static scan can list every dangerous sink in a codebase.

Click through it at your own pace, or open it full screen.

Quick check

Is it safe to use innerHTML if the server already escaped the value?

Not reliably. Escaped text inserted through innerHTML is decoded again in some flows, and the safest rule is to use textContent for text.

What should we do when HTML is genuinely required?

Sanitize it with a maintained library, restrict the allowed tags, and add a Content Security Policy.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.