RSTR-XSS-003 — document.write with attacker-controlled DOM data
Summary
document.write (or document.writeln) is called with a string built
from window.location.*, document.referrer, document.URL, or
similar URL-bar / referrer sources — all directly controllable by an
attacker via a crafted link. The HTML is parsed in the page's origin,
yielding DOM-based XSS.
Severity
High. Indistinguishable from reflected XSS in impact.
Languages
JavaScript, TypeScript.
What rastray flags
document.write(location.search); // ← flagged
document.writeln(document.referrer); // ← flagged
document.write('<h1>' + location.hash + '</h1>'); // ← flagged
What rastray deliberately does not flag
document.write(staticString)with a literal argument.document.write(value)wherevalueis computed from a server response — those go throughRSTR-XSS-001/RSTR-XSS-002paths.document.writeof values that were first run through a sanitiser (DOMPurify,escapeHtml). The rule cannot see across calls; if the data was sanitised, suppress per-line.
How to fix it
Stop using document.write entirely. Modern alternatives:
- For text:
element.textContent = ...(safe; no HTML parsing). - For attributes:
element.setAttribute('href', ...). - For controlled markup:
element.innerHTML = DOMPurify.sanitize(html).
If you must accept HTML, sanitise it explicitly:
import DOMPurify from 'dompurify';
container.innerHTML = DOMPurify.sanitize(untrusted, { USE_PROFILES: { html: true } });
Note that document.write after the page has loaded silently wipes
the entire document — there is no production scenario where it is the
right primitive.
References
- OWASP DOM-XSS Cheat Sheet
- MDN:
document.write— section "Notes" - CWE-79