Interview answer drill

Use this JavaScript interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

DOM XSS Prevention in JavaScript: Dangerous Sinks, Safe APIs, and Trusted TypesFrontend interview answer

HighIntermediateJavascript
Interview focus

This JavaScript interview question tests whether you can explain you prevent DOM XSS: sources, sinks, sanitization, and Trusted Types, connect it to production trade-offs, and handle common follow-up questions.

  • you prevent DOM XSS: sources, sinks, sanitization, and Trusted Types explanation without falling back to memorized docs wording
  • Frontend Interview and Security reasoning, edge cases, and production failure modes
  • How you would answer the most likely JavaScript interview follow-up
Practice more JavaScript interview questions
Interview quick answer

Security-focused frontend interview guide to DOM XSS: attacker-controlled sources like location.hash or postMessage, dangerous DOM sinks, safe rendering patterns, URL validation, and defense in depth with CSP and Trusted Types.

Full interview answer

Definition (above the fold)

DOM XSS occurs when untrusted input reaches a DOM sink that interprets it as code or HTML. This can happen entirely on the client side without a server-side template bug, for example when location.hash, postMessage, or a profile-bio field gets pushed into innerHTML after page load. The fix is data-flow control: identify untrusted sources, block dangerous sinks, and enforce safe APIs by default.

Core mental model

Model this as source -> transform -> sink. If source is attacker-controlled and sink interprets executable content, you have risk. Break the chain by sanitizing/validating at boundaries and preferring non-executable sink APIs.

DOM XSS vs reflected/stored XSS

Reflected or stored XSS often starts when the server sends unsafe HTML back to the browser. DOM XSS starts later: client-side code reads data such as location.hash, postMessage, or API content and feeds it into a dangerous sink. Same attacker outcome, different bug location and debugging path.

Untrusted source

Example

Risk

URL data

location.hash, searchParams

Attacker can share crafted links

Cross-window messaging

postMessage payloads

Unvalidated origin/data injection

Storage/state

localStorage/sessionStorage values

Persisted attacker payload reuse

Third-party API fields

Profile bio, comments, markdown

Reflected or stored unsafe content

Treat all external/user-derived strings as untrusted by default.

Runnable example #1: dangerous sink vs safe text rendering

JAVASCRIPT
const payload = new URL(location.href).searchParams.get('q') || '';

// Dangerous: HTML interpretation
// result.innerHTML = payload;

// Safe default: text-only rendering
result.textContent = payload;
                  

Using textContent prevents markup execution and should be the default for user-controlled text.

Runnable example #2: URL sink validation allowlist

JAVASCRIPT
function setSafeLink(anchor, raw) {
  const u = new URL(raw, window.location.origin);
  const okProtocols = new Set(['http:', 'https:', 'mailto:']);
  if (!okProtocols.has(u.protocol)) {
    throw new Error('Blocked unsafe protocol');
  }
  anchor.href = u.toString();
}

setSafeLink(document.querySelector('#profile'), userInputUrl);
                  

Dangerous sink

Safer alternative

Notes

innerHTML

textContent or vetted sanitizer

Only sanitize when rich HTML is required

insertAdjacentHTML / document.write

Template rendering with trusted static markup only

Treat both as HTML-parsing sinks

String-based setTimeout

Function callback form

Avoid implicit eval behavior

element.setAttribute("href", raw)

Parsed URL + protocol allowlist

Block javascript: and data abuse

Direct script URL injection

Static script tags + CSP controls

Avoid dynamic script construction from input

Prefer APIs that do not evaluate or parse attacker-controlled code paths.

Common pitfalls

  • Assuming framework escaping protects every manual DOM operation.
  • Sanitizing once but later mutating string into another unsafe sink.
  • Missing origin checks on postMessage handlers.
  • Relying on blacklist regex rules instead of structured parsing/allowlists.

Defense in depth

Use CSP to restrict script execution and reduce exploit impact if a sink slips through. Adopt Trusted Types in large apps to force controlled creation of HTML/script URLs. Pair client controls with server-side output encoding and validation for full coverage.

Escaping vs sanitization vs Trusted Types

Escaping is context-specific output encoding. Sanitization removes unsafe markup when you intentionally allow limited HTML. Trusted Types does something different: it makes risky sinks accept only approved objects, so raw strings cannot quietly reach innerHTML or similar APIs.

Runnable example #3: sanitize once, enforce at the sink

JAVASCRIPT
const policy = trustedTypes.createPolicy('profile-html', {
  createHTML(input) {
    return DOMPurify.sanitize(input);
  }
});

profileBio.innerHTML = policy.createHTML(apiProfile.bio);
// Pair with CSP: require-trusted-types-for 'script'
                  

Interview follow-ups

Q1: Is escaping enough for all sinks? A: No, sink context matters; URL, HTML, and script contexts differ.
Q2: Why is DOM XSS tricky? A: It can be introduced entirely in client code after data leaves the server.
Q3: First practical hardening step? A: Replace dangerous sinks with safe defaults and add CSP policy reporting.

Practical scenario
A support widget reads location.hash to restore UI state and also renders agent notes from an API. If either value reaches innerHTML without sanitization or a trusted-types policy, an attacker-controlled link or stored profile note can execute in the agent dashboard.

What to do
Keep plain text on textContent, validate structured URLs separately, and gate rich HTML behind a sanitizer plus Trusted Types/CSP.

Implementation checklist / takeaway

Map sources and sinks, block dangerous DOM APIs by default, validate structured inputs (especially URLs), and enforce CSP/Trusted Types where possible. Strong interview answers focus on secure data flow, not just one sanitizer call.

Similar questions
Guides
Preparing for interviews?

Use this as one explanation rep, then continue with the JavaScript interview questions cluster or a guided prep path.