XSS is injection's ugly cousin. The vulnerable surface is your DOM rather than your database. Stored XSS lands in your database from one user and runs in another user's browser. Reflected XSS arrives in the URL or form fields and runs immediately. DOM-based XSS happens entirely client-side when JavaScript writes attacker-controlled strings into the page.

The fix is contextual escaping. "Escape on output" is the modern wisdom: the same string that's safe in a JSON body is unsafe inside an HTML attribute, which is unsafe inside a <script> block, which is unsafe inside a URL. Modern frameworks (React, Vue, Svelte, Lit) escape by default and force you to opt into raw HTML with explicit names like dangerouslySetInnerHTML — those names exist to make code review easier.

Defenses worth layering: a strict Content Security Policy that disables inline scripts and eval, an HttpOnly cookie flag so XSS can't steal session cookies, and a SameSite cookie attribute that prevents the worst CSRF/XSS combo attacks.

Review heuristic

Search the diff for dangerouslySetInnerHTML, innerHTML =, document.write, and v-html. Each of those is an opt-out of the framework's safety. Each one needs a justification in the review and a sanitization step on the input.

External reference

CWE-79: Cross-site Scripting — the canonical industry classification for this bug class. Useful when filing tickets, writing security policies, or arguing with a static analyzer.