A defesa contra XSS
Codificar a saída conforme o contexto fecha a falha; uma CSP sem `unsafe-inline` é a rede de segurança.
A causa do XSS é sempre a mesma: dado do usuário tratado como código. O conserto principal é codificar a saída conforme o contexto: ao escrever no corpo do HTML, troque <, >, &, " pelas entidades (<, >, &, "), e assim o <img ...> do atacante vira texto visível, não uma tag. Em contexto de atributo, de script ou de URL, a codificação é outra: por isso a regra é codificar para o contexto certo. Na prática, prefira APIs que tratam tudo como texto, como element.textContent no lugar de element.innerHTML.
Validar a entrada ajuda (uma lista de permissão de tags, como num editor de texto rico), mas não substitui a codificação da saída: o mesmo dado pode ser seguro num lugar e perigoso em outro.
A segunda camada é a Content-Security-Policy (CSP), um cabeçalho que diz ao navegador de onde pode vir o script. Uma CSP bem feita bloqueia script embutido (handlers onerror, <script> inline), aceitando só scripts de origens confiáveis ou com um nonce. Mas há uma armadilha: se a política inclui 'unsafe-inline', ela volta a permitir justamente o script embutido, e deixa de proteger contra XSS. CSP é uma rede de segurança para quando o escape falha, não um substituto dele.