Desescape entidades HTML em Javascript?

176

Eu tenho algum código Javascript que se comunica com um back-end XML-RPC. O XML-RPC retorna cadeias de caracteres do formulário:

<img src='myimage.jpg'>

No entanto, quando eu uso o Javascript para inserir as strings no HTML, elas são renderizadas literalmente. Não vejo uma imagem, literalmente vejo a sequência:

<img src='myimage.jpg'>

Meu palpite é que o HTML está sendo escapado pelo canal XML-RPC.

Como posso retirar a string em Javascript? Tentei as técnicas desta página sem êxito: http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/

Quais são as outras maneiras de diagnosticar o problema?

Joseph Turian
fonte
1
Tente isso: stackoverflow.com/questions/4480757/…
XP1 10/10
Possível duplicata de Como decodificar entidades HTML usando jQuery?
Lucascaro

Respostas:

176

EDIT: você deve usar a API DOMParser como Wladimir sugere ; editei minha resposta anterior, pois a função postada introduziu uma vulnerabilidade de segurança.

O seguinte trecho é o código da resposta antiga com uma pequena modificação: o uso de um em textareavez de um divreduz a vulnerabilidade do XSS, mas ainda é problemático no IE9 e no Firefox.

function htmlDecode(input){
  var e = document.createElement('textarea');
  e.innerHTML = input;
  // handle case of empty input
  return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
}

htmlDecode("&lt;img src='myimage.jpg'&gt;"); 
// returns "<img src='myimage.jpg'>"

Basicamente, crio um elemento DOM programaticamente, atribuo o HTML codificado ao innerHTML e recupero o nodeValue do nó de texto criado na inserção innerHTML. Como ele apenas cria um elemento, mas nunca o adiciona, nenhum HTML do site é modificado.

Ele funcionará em vários navegadores (incluindo navegadores antigos) e aceitará todas as entidades de caracteres HTML .

EDIT: A versão antiga deste código não funcionava no IE com entradas em branco, como evidenciado aqui no jsFiddle (exibição no IE). A versão acima funciona com todas as entradas.

UPDATE: parece que isso não funciona com cadeias grandes e também apresenta uma vulnerabilidade de segurança , consulte os comentários.

CMS
fonte
Entendi, você mudou para ', então deixe-me excluir meu comentário de volta, thx, está funcionando muito bem, +1
YOU
1
@ S.Mark: &apos;não pertence às entidades HTML 4, é por isso! w3.org/TR/html4/sgml/entities.html fishbowl.pastiche.org/2003/07/01/the_curse_of_apos
CMS
2
Veja também a nota do @ kender sobre a pouca segurança dessa abordagem.
Joseph Turian
2
Ver a minha nota para @kender sobre os pobres testes que ele fez;)
Roatin Marth
24
Esta função é um risco à segurança, o código JavaScript será executado mesmo que o elemento não seja adicionado ao DOM. Portanto, isso é apenas algo a ser usado se a sequência de entrada for confiável. Eu adicionei minha própria resposta, explicando o problema e fornecendo uma solução segura. Como efeito colateral, o resultado não será cortado se existirem vários nós de texto.
Wladimir Palant
375

A maioria das respostas fornecidas aqui tem uma enorme desvantagem: se a string que você está tentando converter não for confiável, você terá uma vulnerabilidade de Cross-Site Scripting (XSS) . Para a função na resposta aceita , considere o seguinte:

htmlDecode("<img src='dummy' onerror='alert(/xss/)'>");

A string aqui contém uma tag HTML sem escape, portanto, em vez de decodificar qualquer coisa, a htmlDecodefunção realmente executará o código JavaScript especificado dentro da string.

Isso pode ser evitado usando o DOMParser, compatível com todos os navegadores modernos :

function htmlDecode(input) {
  var doc = new DOMParser().parseFromString(input, "text/html");
  return doc.documentElement.textContent;
}

console.log(  htmlDecode("&lt;img src='myimage.jpg'&gt;")  )    
// "<img src='myimage.jpg'>"

console.log(  htmlDecode("<img src='dummy' onerror='alert(/xss/)'>")  )  
// ""

É garantido que esta função não execute nenhum código JavaScript como efeito colateral. Quaisquer tags HTML serão ignoradas, apenas o conteúdo do texto será retornado.

Nota de compatibilidade : a análise de HTML DOMParserrequer pelo menos o Chrome 30, Firefox 12, Opera 17, Internet Explorer 10, Safari 7.1 ou Microsoft Edge. Portanto, todos os navegadores sem suporte já ultrapassaram a EOL e, a partir de 2017, os únicos que ainda podem ser vistos na natureza ocasionalmente são as versões mais antigas do Internet Explorer e Safari (geralmente essas ainda não são numerosas o suficiente para incomodar).

Wladimir Palant
fonte
19
Penso que esta resposta é a melhor porque mencionou a vulnerabilidade XSS.
Jun
2
Observe que (de acordo com sua referência) DOMParsernão era compatível com o "text/html"Firefox 12.0 e ainda existem algumas versões mais recentes de navegadores que nem mesmo são compatíveisDOMParser.prototype.parseFromString() . De acordo com sua referência, DOMParserainda é uma tecnologia experimental, e os substitutos usam a innerHTMLpropriedade que, como você também apontou em resposta à minha abordagem , tem essa vulnerabilidade XSS (que deve ser corrigida pelos fornecedores de navegadores).
PointedEars 28/02
4
@PointedEars: quem se importa com o Firefox 12 em 2016? Os problemáticos são o Internet Explorer até 9.0 e o Safari até 7.0. Se alguém puder se dar ao luxo de não apoiá-los (que esperamos que todos sejam em breve), o DOMParser é a melhor escolha. Caso contrário - sim, apenas as entidades de processamento seriam uma opção.
Wladimir Palant 28/02
4
@PointedEars: as <script>tags que não estão sendo executadas não são um mecanismo de segurança; essa regra evita os problemas complicados de temporização se a configuração innerHTMLpuder executar scripts síncronos como efeito colateral. A limpeza do código HTML é um assunto complicado e innerHTMLnem sequer tenta - já porque a página da Web pode realmente pretender definir manipuladores de eventos em linha. Isso simplesmente não é um mecanismo destinado a dados inseguros, ponto final.
Wladimir Palant
1
@ ИльяЗеленько: Você planeja usar esse código em um loop restrito ou por que o desempenho é importante? Sua resposta é novamente vulnerável ao XSS, valeu mesmo a pena?
Wladimir Palant
37

Se você estiver usando jQuery:

function htmlDecode(value){ 
  return $('<div/>').html(value).text(); 
}

Caso contrário, use o Encoder Object da Strictly Software , que possui uma excelente htmlDecode()função.

Chris Fulstow
fonte
59
Não (repita NÃO) use isso para conteúdo gerado pelo usuário que não seja o conteúdo gerado por esse usuário. Se houver uma tag <script> no valor, o conteúdo do script será executado!
Malvolio
Não consigo encontrar uma licença para isso em nenhum lugar do site. Você sabe qual é a licença?
Trig
Há uma licença no cabeçalho da fonte, é a GPL.
Chris Fulstow 01/09/11
6
SIM, essa função abre caminho para o XSS: tente htmlDecode ("<script> alert (12) </script> 123 & gt;"))
Dinis Cruz
qual o significado de $ ('<div />') ?
Eco Yang
13

O truque é usar o poder do navegador para decodificar os caracteres HTML especiais, mas não permitir que o navegador execute os resultados como se fosse um html real ... Essa função usa um regex para identificar e substituir caracteres HTML codificados, um caractere de uma vez.

function unescapeHtml(html) {
    var el = document.createElement('div');
    return html.replace(/\&[#0-9a-z]+;/gi, function (enc) {
        el.innerHTML = enc;
        return el.innerText
    });
}
Ben White
fonte
O regex pode ser correspondido um pouco mais, /\&#?[0-9a-z]+;/gijá que # deve aparecer apenas como o segundo caractere, se for o caso.
TheAtomicOption
Esta é a melhor resposta. Evita a vulnerabilidade XSS e não tira as tags HTML.
Emmanuel
6

A resposta do CMS funciona bem, a menos que o HTML que você deseja remover seja muito longo, maior que 65536 caracteres. Como, no Chrome, o HTML interno é dividido em muitos nós filhos, cada um com 65536 no máximo, e você precisa concatená-los. Essa função também funciona para strings muito longas:

function unencodeHtmlContent(escapedHtml) {
  var elem = document.createElement('div');
  elem.innerHTML = escapedHtml;
  var result = '';
  // Chrome splits innerHTML into many child nodes, each one at most 65536.
  // Whereas FF creates just one single huge child node.
  for (var i = 0; i < elem.childNodes.length; ++i) {
    result = result + elem.childNodes[i].nodeValue;
  }
  return result;
}

Consulte esta resposta sobre o innerHTMLcomprimento máximo para obter mais informações: https://stackoverflow.com/a/27545633/694469

KajMagnus
fonte
3

Não é uma resposta direta à sua pergunta, mas não seria melhor para o seu RPC retornar alguma estrutura (XML ou JSON ou qualquer outra coisa) com esses dados de imagem (URLs no seu exemplo) dentro dessa estrutura?

Então você pode simplesmente analisá-lo em seu javascript e criar o <img>próprio javascript em uso.

A estrutura que você recebe do RPC pode ter a seguinte aparência:

{"img" : ["myimage.jpg", "myimage2.jpg"]}

Eu acho que é melhor assim, pois a injeção de um código que vem de fonte externa para a sua página não parece muito segura. Imaginando alguém sequestrando seu script XML-RPC e colocando algo que você não gostaria lá (até mesmo algum javascript ...)

kender
fonte
A abordagem do @CMS acima tem essa falha de segurança?
Joseph Turian
Acabei de verificar o seguinte argumento passado para o htmlDecode: htmlDecode ("& lt; img src = 'myimage.jpg' & gt; & lt; script & gt; document.write ('xxxxx'); & lt; / script & gt;") e cria o elemento <script> </script> que pode ser ruim, imho. E ainda acho que é melhor retornar uma estrutura em vez do texto a ser inserido; você pode lidar com erros de maneira agradável, por exemplo.
kender
1
Eu apenas tentei htmlDecode("&lt;img src='myimage.jpg'&gt;&lt;script&gt;alert('xxxxx');&lt;/script&gt;")e nada aconteceu. Eu recebi a string html decodificada de volta, conforme o esperado.
Roatin Marth
2

A resposta de Chris é agradável e elegante, mas falha se o valor for indefinido . Apenas uma simples melhoria torna sólida:

function htmlDecode(value) {
   return (typeof value === 'undefined') ? '' : $('<div/>').html(value).text();
}
nerijus
fonte
Se melhorar, faça:return (typeof value !== 'string') ? '' : $('<div/>').html(value).text();
SynCap 26/06
2

Você é bem-vindo ... apenas um mensageiro ... o crédito total vai para ourcodeworld.com, link abaixo.

window.htmlentities = {
        /**
         * Converts a string to its html characters completely.
         *
         * @param {String} str String with unescaped HTML characters
         **/
        encode : function(str) {
            var buf = [];

            for (var i=str.length-1;i>=0;i--) {
                buf.unshift(['&#', str[i].charCodeAt(), ';'].join(''));
            }

            return buf.join('');
        },
        /**
         * Converts an html characterSet into its original character.
         *
         * @param {String} str htmlSet entities
         **/
        decode : function(str) {
            return str.replace(/&#(\d+);/g, function(match, dec) {
                return String.fromCharCode(dec);
            });
        }
    };

Crédito completo: https://ourcodeworld.com/articles/read/188/encode-and-decode-html-entities-using-pure-javascript

decoder7283
fonte
2

Esta é a solução mais abrangente que eu tentei até agora:

const STANDARD_HTML_ENTITIES = {
    nbsp: String.fromCharCode(160),
    amp: "&",
    quot: '"',
    lt: "<",
    gt: ">"
};

const replaceHtmlEntities = plainTextString => {
    return plainTextString
        .replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec))
        .replace(
            /&(nbsp|amp|quot|lt|gt);/g,
            (a, b) => STANDARD_HTML_ENTITIES[b]
        );
};
Daniel
fonte
"O mais abrangente"? Você já tentou executá-lo em um conjunto de testes realmente abrangente ?
Dan Dascalescu 01/07
1

Eu era louco o suficiente para fazer essa função que deveria ser bonita, se não completamente, exaustiva:

function removeEncoding(string) {
    return string.replace(/&Agrave;/g, "À").replace(/&Aacute;/g, "Á").replace(/&Acirc;/g, "Â").replace(/&Atilde;/g, "Ã").replace(/&Auml;/g, "Ä").replace(/&Aring;/g, "Å").replace(/&agrave;/g, "à").replace(/&acirc;/g, "â").replace(/&atilde;/g, "ã").replace(/&auml;/g, "ä").replace(/&aring;/g, "å").replace(/&AElig;/g, "Æ").replace(/&aelig;/g, "æ").replace(/&szlig;/g, "ß").replace(/&Ccedil;/g, "Ç").replace(/&ccedil;/g, "ç").replace(/&Egrave;/g, "È").replace(/&Eacute;/g, "É").replace(/&Ecirc;/g, "Ê").replace(/&Euml;/g, "Ë").replace(/&egrave;/g, "è").replace(/&eacute;/g, "é").replace(/&ecirc;/g, "ê").replace(/&euml;/g, "ë").replace(/&#131;/g, "ƒ").replace(/&Igrave;/g, "Ì").replace(/&Iacute;/g, "Í").replace(/&Icirc;/g, "Î").replace(/&Iuml;/g, "Ï").replace(/&igrave;/g, "ì").replace(/&iacute;/g, "í").replace(/&icirc;/g, "î").replace(/&iuml;/g, "ï").replace(/&Ntilde;/g, "Ñ").replace(/&ntilde;/g, "ñ").replace(/&Ograve;/g, "Ò").replace(/&Oacute;/g, "Ó").replace(/&Ocirc;/g, "Ô").replace(/&Otilde;/g, "Õ").replace(/&Ouml;/g, "Ö").replace(/&ograve;/g, "ò").replace(/&oacute;/g, "ó").replace(/&ocirc;/g, "ô").replace(/&otilde;/g, "õ").replace(/&ouml;/g, "ö").replace(/&Oslash;/g, "Ø").replace(/&oslash;/g, "ø").replace(/&#140;/g, "Œ").replace(/&#156;/g, "œ").replace(/&#138;/g, "Š").replace(/&#154;/g, "š").replace(/&Ugrave;/g, "Ù").replace(/&Uacute;/g, "Ú").replace(/&Ucirc;/g, "Û").replace(/&Uuml;/g, "Ü").replace(/&ugrave;/g, "ù").replace(/&uacute;/g, "ú").replace(/&ucirc;/g, "û").replace(/&uuml;/g, "ü").replace(/&#181;/g, "µ").replace(/&#215;/g, "×").replace(/&Yacute;/g, "Ý").replace(/&#159;/g, "Ÿ").replace(/&yacute;/g, "ý").replace(/&yuml;/g, "ÿ").replace(/&#176;/g, "°").replace(/&#134;/g, "†").replace(/&#135;/g, "‡").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&#177;/g, "±").replace(/&#171;/g, "«").replace(/&#187;/g, "»").replace(/&#191;/g, "¿").replace(/&#161;/g, "¡").replace(/&#183;/g, "·").replace(/&#149;/g, "•").replace(/&#153;/g, "™").replace(/&copy;/g, "©").replace(/&reg;/g, "®").replace(/&#167;/g, "§").replace(/&#182;/g, "¶").replace(/&Alpha;/g, "Α").replace(/&Beta;/g, "Β").replace(/&Gamma;/g, "Γ").replace(/&Delta;/g, "Δ").replace(/&Epsilon;/g, "Ε").replace(/&Zeta;/g, "Ζ").replace(/&Eta;/g, "Η").replace(/&Theta;/g, "Θ").replace(/&Iota;/g, "Ι").replace(/&Kappa;/g, "Κ").replace(/&Lambda;/g, "Λ").replace(/&Mu;/g, "Μ").replace(/&Nu;/g, "Ν").replace(/&Xi;/g, "Ξ").replace(/&Omicron;/g, "Ο").replace(/&Pi;/g, "Π").replace(/&Rho;/g, "Ρ").replace(/&Sigma;/g, "Σ").replace(/&Tau;/g, "Τ").replace(/&Upsilon;/g, "Υ").replace(/&Phi;/g, "Φ").replace(/&Chi;/g, "Χ").replace(/&Psi;/g, "Ψ").replace(/&Omega;/g, "Ω").replace(/&alpha;/g, "α").replace(/&beta;/g, "β").replace(/&gamma;/g, "γ").replace(/&delta;/g, "δ").replace(/&epsilon;/g, "ε").replace(/&zeta;/g, "ζ").replace(/&eta;/g, "η").replace(/&theta;/g, "θ").replace(/&iota;/g, "ι").replace(/&kappa;/g, "κ").replace(/&lambda;/g, "λ").replace(/&mu;/g, "μ").replace(/&nu;/g, "ν").replace(/&xi;/g, "ξ").replace(/&omicron;/g, "ο").replace(/&piρ;/g, "ρ").replace(/&rho;/g, "ς").replace(/&sigmaf;/g, "ς").replace(/&sigma;/g, "σ").replace(/&tau;/g, "τ").replace(/&phi;/g, "φ").replace(/&chi;/g, "χ").replace(/&psi;/g, "ψ").replace(/&omega;/g, "ω").replace(/&bull;/g, "•").replace(/&hellip;/g, "…").replace(/&prime;/g, "′").replace(/&Prime;/g, "″").replace(/&oline;/g, "‾").replace(/&frasl;/g, "⁄").replace(/&weierp;/g, "℘").replace(/&image;/g, "ℑ").replace(/&real;/g, "ℜ").replace(/&trade;/g, "™").replace(/&alefsym;/g, "ℵ").replace(/&larr;/g, "←").replace(/&uarr;/g, "↑").replace(/&rarr;/g, "→").replace(/&darr;/g, "↓").replace(/&barr;/g, "↔").replace(/&crarr;/g, "↵").replace(/&lArr;/g, "⇐").replace(/&uArr;/g, "⇑").replace(/&rArr;/g, "⇒").replace(/&dArr;/g, "⇓").replace(/&hArr;/g, "⇔").replace(/&forall;/g, "∀").replace(/&part;/g, "∂").replace(/&exist;/g, "∃").replace(/&empty;/g, "∅").replace(/&nabla;/g, "∇").replace(/&isin;/g, "∈").replace(/&notin;/g, "∉").replace(/&ni;/g, "∋").replace(/&prod;/g, "∏").replace(/&sum;/g, "∑").replace(/&minus;/g, "−").replace(/&lowast;/g, "∗").replace(/&radic;/g, "√").replace(/&prop;/g, "∝").replace(/&infin;/g, "∞").replace(/&OEig;/g, "Œ").replace(/&oelig;/g, "œ").replace(/&Yuml;/g, "Ÿ").replace(/&spades;/g, "♠").replace(/&clubs;/g, "♣").replace(/&hearts;/g, "♥").replace(/&diams;/g, "♦").replace(/&thetasym;/g, "ϑ").replace(/&upsih;/g, "ϒ").replace(/&piv;/g, "ϖ").replace(/&Scaron;/g, "Š").replace(/&scaron;/g, "š").replace(/&ang;/g, "∠").replace(/&and;/g, "∧").replace(/&or;/g, "∨").replace(/&cap;/g, "∩").replace(/&cup;/g, "∪").replace(/&int;/g, "∫").replace(/&there4;/g, "∴").replace(/&sim;/g, "∼").replace(/&cong;/g, "≅").replace(/&asymp;/g, "≈").replace(/&ne;/g, "≠").replace(/&equiv;/g, "≡").replace(/&le;/g, "≤").replace(/&ge;/g, "≥").replace(/&sub;/g, "⊂").replace(/&sup;/g, "⊃").replace(/&nsub;/g, "⊄").replace(/&sube;/g, "⊆").replace(/&supe;/g, "⊇").replace(/&oplus;/g, "⊕").replace(/&otimes;/g, "⊗").replace(/&perp;/g, "⊥").replace(/&sdot;/g, "⋅").replace(/&lcell;/g, "⌈").replace(/&rcell;/g, "⌉").replace(/&lfloor;/g, "⌊").replace(/&rfloor;/g, "⌋").replace(/&lang;/g, "⟨").replace(/&rang;/g, "⟩").replace(/&loz;/g, "◊").replace(/&#039;/g, "'").replace(/&amp;/g, "&").replace(/&quot;/g, "\"");
}

Usado assim:

let decodedText = removeEncoding("Ich hei&szlig;e David");
console.log(decodedText);

Impressões: Ich Heiße David

PS: demorou uma hora e meia para fazer.

David Chopin
fonte
0

Para desescapar entidades HTML * em JavaScript, você pode usar a pequena biblioteca html-escaper :npm install html-escaper

import {unescape} from 'html-escaper';

unescape('escaped string');

Ou unescapefunção de Lodash ou sublinhado , se você estiver usando-o.


*) Por favor, note que estas funções não cobrem todas as entidades HTML, mas apenas as mais comuns, ou seja &, <, >, ', ". Para unescape todas as entidades HTML, você pode usar ele biblioteca.

Łukasz K
fonte
-1

Uso isso no meu projeto: inspirado em outras respostas, mas com um parâmetro extra seguro, pode ser útil quando você lida com caracteres decorados

var decodeEntities=(function(){

    var el=document.createElement('div');
    return function(str, safeEscape){

        if(str && typeof str === 'string'){

            str=str.replace(/\</g, '&lt;');

            el.innerHTML=str;
            if(el.innerText){

                str=el.innerText;
                el.innerText='';
            }
            else if(el.textContent){

                str=el.textContent;
                el.textContent='';
            }

            if(safeEscape)
                str=str.replace(/\</g, '&lt;');
        }
        return str;
    }
})();

E é utilizável como:

var label='safe <b> character &eacute;ntity</b>';
var safehtml='<div title="'+decodeEntities(label)+'">'+decodeEntities(label, true)+'</div>';
tmx976
fonte
-1

Todas as outras respostas aqui têm problemas.

Os métodos document.createElement ('div') (incluindo aqueles que usam jQuery) executam qualquer javascript passado (um problema de segurança) e o método DOMParser.parseFromString () apara os espaços em branco. Aqui está uma solução javascript pura que não tem nenhum problema:

function htmlDecode(html) {
    var textarea = document.createElement("textarea");
    html= html.replace(/\r/g, String.fromCharCode(0xe000)); // Replace "\r" with reserved unicode character.
    textarea.innerHTML = html;
    var result = textarea.value;
    return result.replace(new RegExp(String.fromCharCode(0xe000), 'g'), '\r');
}

TextArea é usado especificamente para evitar o código executig js. Passa estes:

htmlDecode('&lt;&amp;&nbsp;&gt;'); // returns "<& >" with non-breaking space.
htmlDecode('  '); // returns "  "
htmlDecode('<img src="dummy" onerror="alert(\'xss\')">'); // Does not execute alert()
htmlDecode('\r\n') // returns "\r\n", doesn't lose the \r like other solutions.
EricP
fonte
1
Não, o uso de uma tag diferente não resolve o problema. Ainda é uma vulnerabilidade XSS, tente htmlDecode("</textarea><img src=x onerror=alert(1)>"). Você postou isso depois que eu já apontei esse problema na resposta de Sergio Belevskij.
Wladimir Palant
Não consigo reproduzir o problema que você descreve. Eu tenho seu código neste JsFiddle e nenhum alerta é exibido durante a execução. jsfiddle.net/edsjt15g/1 Você pode dar uma olhada? Qual navegador você está usando?
EricP
2
Estou usando o Firefox. O Chrome realmente lida com esse cenário de maneira diferente, portanto o código não é executado - não é algo em que você deva confiar.
Wladimir Palant
-1
var encodedStr = 'hello &amp; world';

var parser = new DOMParser;
var dom = parser.parseFromString(
    '<!doctype html><body>' + encodedStr,
    'text/html');
var decodedString = dom.body.textContent;

console.log(decodedString);
jagjeet
fonte
@Wladimir Palant (autor do AdBlock Plus) já deu a resposta do DOMParser quatro anos antes. Você leu as respostas anteriores antes de postar as suas?
Dan Dascalescu 01/07
-7

Existe uma variante 80% mais produtiva do que as respostas no topo.

Consulte o benchmark: https://jsperf.com/decode-html12345678/1

teste de performance

console.log(decodeEntities('test: &gt'));

function decodeEntities(str) {
  // this prevents any overhead from creating the object each time
  const el = decodeEntities.element || document.createElement('textarea')

  // strip script/html tags
  el.innerHTML = str
    .replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '')
    .replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, '');

  return el.value;
}

Se você precisar deixar tags, remova as duas .replace(...)chamadas (você pode deixar a primeira se não precisar de scripts).

Илья Зеленько
fonte
6
Parabéns, você conseguiu ocultar a vulnerabilidade com uma lógica falsa de higienização, tudo por uma vitória no desempenho que não importa na prática. Tente ligar decodeEntities("</textarea '><img src=x onerror=alert(1) \">")para o Firefox. Pare de tentar limpar o código HTML com expressões regulares.
Wladimir Palant