A defesa contra SSTI
A entrada do usuário entra como dado no contexto, nunca na linguagem do template.
A causa da SSTI é sempre a mesma: a entrada do usuário virou template. O conserto, então, é não deixar isso acontecer. O template é fixo, escrito pelo programador; os valores do usuário entram pelo contexto, como dados, e o motor os mostra como texto.
Compare: render('<div>' + bio + '</div>', {}) cola a entrada no template (vulnerável); render('<div>{{ bio }}</div>', { bio: bio }) passa a entrada como dado (seguro). No segundo caso, um {{7*7}} dentro de bio aparece como texto, nunca avaliado.
Quando o conteúdo do usuário precisa mesmo conter marcação, as camadas extras são: motor em modo sandbox (corta o acesso a objetos perigosos), escape automático da saída, e segredos fora do contexto. Lista negra de palavras não conta como conserto.