An XSS lab should teach one rule: encode untrusted data for the exact place it is written, such as an HTML body, an attribute or a script string. Then show the escape hatches in modern frameworks that switch that protection off, and let learners find them in their own code.
Lab setup
Provide a small page that reflects a query parameter and a profile field. Ask learners to make a script run in another user's browser using only the page's inputs. Keep the setup local, as described in our note on running training apps safely.
Teach the context rule
The OWASP XSS Prevention Cheat Sheet states the core rule: treat every variable as untrusted, validate it, then encode or sanitize it for the exact context where it is rendered. It lists contexts and what each needs:
- HTML body: entity encode the characters &, less-than, greater-than, double quote and single quote.
- HTML attribute: quote the value and use an attribute encoder. Encoding does not make URLs, CSS or JavaScript inside attributes safe.
- JavaScript: insert data only inside a quoted string. Never insert code, and note that encoded values passed to eval remain unsafe.
- URL: percent-encode parameter values, not whole URLs, and allow only http and https schemes.
Have learners fix three different outputs on the page, each in a different context. The point is that one blanket "escape everything" function cannot be right for all three.
Prefer safe sinks
The same guide recommends safe sinks such as textContent, insertAdjacentText and setAttribute with a fixed name, and says to avoid innerHTML unless its input is sanitized. Add a front-end widget in the lab that uses innerHTML, let learners exploit it, then fix it by switching to textContent.
Framework escape hatches
Modern frameworks encode by default, but each has a bypass. The cheat sheet names React dangerouslySetInnerHTML without sanitization, Angular bypassSecurityTrustAs functions, and Lit unsafeHTML, among others. Ask learners to search their repositories for these names. It takes minutes and often turns up the first real finding of the session.
It also says React 19 blocks javascript: URLs in src and href, but that this does not replace app-specific URL validation.
Defense in depth is not the fix
The guide treats CSP, cookie attributes and Trusted Types as extra layers and says not to rely on CSP alone. It also says WAFs are unreliable against XSS. Show a payload that a simple filter misses and let the group discuss why filters lose.
Debrief
- Where do we render user data in more than one context?
- Which components use an escape hatch, and who approved it?
- If we must accept user HTML, which sanitizer do we use, and is it patched?
Quick check
Does a modern framework make XSS impossible?
No. Default encoding helps, but escape hatches, unvalidated URLs and outdated components can reintroduce it.
Sources
Last reviewed: 19 October 2026.