Unescape de entidades HTML en Javascript?

176

Tengo un código Javascript que se comunica con un back-end XML-RPC. El XML-RPC devuelve cadenas de la forma:

<img src='myimage.jpg'>

Sin embargo, cuando uso el Javascript para insertar las cadenas en HTML, se procesan literalmente. No veo una imagen, literalmente veo la cadena:

<img src='myimage.jpg'>

Supongo que el HTML se está escapando a través del canal XML-RPC.

¿Cómo puedo eliminar la cadena en Javascript? Probé las técnicas en esta página, sin éxito: http://paulschreiber.com/blog/2008/09/20/javascript-how-to-unescape-html-entities/

¿Cuáles son otras formas de diagnosticar el problema?

Joseph Turian
fuente

Respuestas:

176

EDITAR: debe usar la API DOMParser como sugiere Wladimir , edité mi respuesta anterior ya que la función publicada introdujo una vulnerabilidad de seguridad.

El siguiente fragmento es el código de la respuesta anterior con una pequeña modificación: el uso de en textarealugar de a divreduce la vulnerabilidad de XSS, pero sigue siendo problemático en IE9 y 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'>"

Básicamente, creo un elemento DOM mediante programación, asigno el HTML codificado a su innerHTML y recupero el nodeValue del nodo de texto creado en la inserción innerHTML. Como solo crea un elemento pero nunca lo agrega, no se modifica el HTML del sitio.

Funcionará entre navegadores (incluidos los navegadores más antiguos) y aceptará todas las entidades de caracteres HTML .

EDITAR: La versión anterior de este código no funcionaba en IE con entradas en blanco, como se evidencia aquí en jsFiddle (ver en IE). La versión anterior funciona con todas las entradas.

ACTUALIZACIÓN: parece que esto no funciona con una cadena grande, y también presenta una vulnerabilidad de seguridad , vea los comentarios.

CMS
fuente
Entendido, cambiaste a ', así que déjame borrar mi comentario, gracias, funciona muy bien, +1
1
@ S.Mark: &apos;no pertenece a las entidades HTML 4, ¡por eso! w3.org/TR/html4/sgml/entities.html fishbowl.pastiche.org/2003/07/01/the_curse_of_apos
CMS
2
Consulte también la nota de @kender sobre la escasa seguridad de este enfoque.
Joseph Turian
2
Ver mi nota a @kender sobre el mal que hizo la prueba;)
Roatin Marth
24
Esta función es un peligro para la seguridad, el código JavaScript se ejecutará incluso a pesar de que el elemento no se agregue al DOM. Entonces, esto es solo algo para usar si la cadena de entrada es confiable. Agregué mi propia respuesta explicando el problema y brindando una solución segura. Como efecto secundario, el resultado no se corta si existen varios nodos de texto.
Wladimir Palant
376

La mayoría de las respuestas dadas aquí tienen una gran desventaja: si la cadena que está tratando de convertir no es confiable, terminará con una vulnerabilidad de Cross-Site Scripting (XSS) . Para la función en la respuesta aceptada , considere lo siguiente:

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

La cadena aquí contiene una etiqueta HTML sin escape, por lo que en lugar de decodificar nada, la htmlDecodefunción realmente ejecutará el código JavaScript especificado dentro de la cadena.

Esto se puede evitar utilizando DOMParser, que es compatible con todos los 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/)'>")  )  
// ""

Se garantiza que esta función no ejecutará ningún código JavaScript como efecto secundario. Se ignorarán las etiquetas HTML, solo se devolverá el contenido de texto.

Nota de compatibilidad : el análisis de HTML DOMParserrequiere al menos Chrome 30, Firefox 12, Opera 17, Internet Explorer 10, Safari 7.1 o Microsoft Edge. Por lo tanto, todos los navegadores sin soporte han superado su EOL y, a partir de 2017, los únicos que todavía se pueden ver en la naturaleza en ocasiones son versiones anteriores de Internet Explorer y Safari (por lo general, estas aún no son lo suficientemente numerosas como para molestar).

Wladimir Palant
fuente
19
Creo que esta respuesta es la mejor porque mencionó la vulnerabilidad XSS.
Константин Ван
2
Tenga en cuenta que (según su referencia) DOMParserno era compatible "text/html"antes de Firefox 12.0, y todavía hay algunas versiones más recientes de navegadores que ni siquiera son compatiblesDOMParser.prototype.parseFromString() . Según su referencia, DOMParsersigue siendo una tecnología experimental, y los sustitutos usan la innerHTMLpropiedad que, como también señaló en respuesta a mi enfoque , tiene esta vulnerabilidad XSS (que los vendedores del navegador deberían corregir).
PointedEars
44
@PointedEars: ¿A quién le importa Firefox 12 en 2016? Los problemáticos son Internet Explorer hasta 9.0 y Safari hasta 7.0. Si uno puede permitirse el lujo de no apoyarlos (lo que esperamos sea pronto para todos), entonces DOMParser es la mejor opción. Si no, sí, las entidades de procesamiento solo serían una opción.
Wladimir Palant
44
@PointedEars: las <script>etiquetas que no se ejecutan no son un mecanismo de seguridad, esta regla simplemente evita los complicados problemas de sincronización si la configuración innerHTMLpuede ejecutar scripts síncronos como efecto secundario. Desinfectar el código HTML es un asunto complicado y innerHTMLni siquiera lo intenta, ya que la página web en realidad podría tener la intención de configurar controladores de eventos en línea. Esto simplemente no es un mecanismo destinado a datos inseguros, punto final.
Wladimir Palant
1
@ ИльяЗеленько: ¿Tiene pensado utilizar este código en un circuito cerrado o por qué es importante el rendimiento? Su respuesta es nuevamente vulnerable a XSS, ¿realmente valió la pena?
Wladimir Palant
37

Si estás usando jQuery:

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

De lo contrario, utilice el Objeto codificador de Strictly Software , que tiene una htmlDecode()función excelente .

Chris Fulstow
fuente
59
No (repita NO) use esto para contenido generado por el usuario que no sea contenido generado por este usuario. Si hay una etiqueta <script> en el valor, ¡se ejecutará el contenido del script!
Malvolio
No puedo encontrar una licencia para eso en ninguna parte del sitio. ¿Sabes cuál es la licencia?
TRiG
Hay una licencia en el encabezado de origen, es GPL.
Chris Fulstow
66
SÍ, esa función abre el camino para XSS: pruebe htmlDecode ("<script> alert (12) </script> 123 & gt;")
Dinis Cruz
¿Qué significa el $ ('<div />') ?
Echo Yang
13

El truco consiste en utilizar el poder del navegador para decodificar los caracteres HTML especiales, pero no permitir que el navegador ejecute los resultados como si fuera html real ... Esta función utiliza una expresión regular para identificar y reemplazar caracteres HTML codificados, un carácter a la 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
fuente
La expresión regular se puede combinar un poco más, /\&#?[0-9a-z]+;/giya que # solo debería aparecer como el segundo carácter, si es que lo hace.
TheAtomicOption
Esta es la mejor respuesta. Evita la vulnerabilidad XSS y no elimina las etiquetas HTML.
Emmanuel
6

La respuesta de CMS funciona bien, a menos que el HTML que desea eliminar sea muy largo, más de 65536 caracteres. Porque luego, en Chrome, el HTML interno se divide en muchos nodos secundarios, cada uno con una longitud máxima de 65536, y debe concatenarlos. Esta función también funciona para cadenas muy largas:

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 respuesta sobre la innerHTMLlongitud máxima para obtener más información: https://stackoverflow.com/a/27545633/694469

KajMagnus
fuente
3

No es una respuesta directa a su pregunta, pero ¿no sería mejor para su RPC devolver alguna estructura (ya sea XML o JSON o lo que sea) con esos datos de imagen (URL en su ejemplo) dentro de esa estructura?

Luego, podría analizarlo en su javascript y construirlo <img>usando el propio javascript.

La estructura que recibe de RPC podría verse así:

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

Creo que es mejor de esta manera, ya que inyectar un código que proviene de una fuente externa en su página no parece muy seguro. Imagine a alguien secuestrando su script XML-RPC y poniendo algo que no querría allí (incluso algunos javascript ...)

kender
fuente
¿El enfoque @CMS anterior tiene este defecto de seguridad?
Joseph Turian
Acabo de comprobar el siguiente argumento pasado a la función htmlDecode: htmlDecode ("& lt; img src = 'myimage.jpg' & gt; & lt; script & gt; document.write ('xxxxx'); & lt; / script & gt;") y crea el <script> </script> elemento que puede ser malo, en mi humilde opinión. Y sigo pensando que es mejor devolver una estructura en lugar de texto para insertar, puede manejar los errores muy bien, por ejemplo.
kender
1
Lo intenté htmlDecode("&lt;img src='myimage.jpg'&gt;&lt;script&gt;alert('xxxxx');&lt;/script&gt;")y no pasó nada. Obtuve la cadena html decodificada de nuevo como se esperaba.
Roatin Marth
2

La respuesta de Chris es agradable y elegante, pero falla si el valor no está definido . Solo una mejora simple lo hace sólido:

function htmlDecode(value) {
   return (typeof value === 'undefined') ? '' : $('<div/>').html(value).text();
}
nerijus
fuente
Si mejora, entonces:return (typeof value !== 'string') ? '' : $('<div/>').html(value).text();
SynCap
2

De nada ... solo un mensajero ... el crédito completo va a ourcodeworld.com, enlace a continuación.

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

decodificador7283
fuente
2

Esta es la solución más completa que he probado hasta ahora:

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
fuente
¿"El más completo"? ¿Has intentado ejecutarlo contra un conjunto de pruebas realmente completo ?
Dan Dascalescu
1

Estaba lo suficientemente loco como para pasar y hacer esta función que debería ser bonita, si no completamente, exhaustiva:

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 así:

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

Huellas dactilares: Ich Heiße David

PD: esto tomó como una hora y media para hacer.

David Chopin
fuente
0

Para eliminar las entidades HTML * en JavaScript, puede usar la pequeña biblioteca html-escaper :npm install html-escaper

import {unescape} from 'html-escaper';

unescape('escaped string');

O unescapefunciona desde Lodash o Underscore , si lo estás usando.


*) Tenga en cuenta que estas funciones no cubren todas las entidades HTML, pero sólo los más comunes, es decir &, <, >, ', ". Para unescape todas las entidades HTML que puede utilizar él biblioteca.

Łukasz K
fuente
-1

Lo uso en mi proyecto: inspirado en otras respuestas pero con un parámetro extra seguro, puede ser útil cuando se trata de personajes 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;
    }
})();

Y se puede usar como:

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

Todas las otras respuestas aquí tienen problemas.

Los métodos document.createElement ('div') (incluidos los que usan jQuery) ejecutan cualquier javascript pasado (un problema de seguridad) y el método DOMParser.parseFromString () recorta los espacios en blanco. Aquí hay una solución javascript pura que no tiene ningún 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 se usa específicamente para evitar ejecutar código js. Pasa estos:

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
fuente
1
No, usar una etiqueta diferente no resuelve el problema. Esto sigue siendo una vulnerabilidad XSS, intente htmlDecode("</textarea><img src=x onerror=alert(1)>"). Usted publicó esto después de que ya señalé este problema en la respuesta de Sergio Belevskij.
Wladimir Palant
No puedo reproducir el problema que describe. Tengo su código en este JsFiddle, y no aparece ninguna alerta cuando se ejecuta. jsfiddle.net/edsjt15g/1 ¿Puedes echar un vistazo? ¿Qué navegador estás usando?
EricP
2
Estoy usando Firefox De hecho, Chrome maneja este escenario de manera diferente, por lo que el código no se ejecuta, no es algo en lo que deba 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
fuente
@Wladimir Palant (autor de AdBlock Plus) ya dio la respuesta DOMParser 4 años antes. ¿Has leído las respuestas anteriores antes de publicar las tuyas?
Dan Dascalescu
-7

Hay una variante que es 80% tan productiva como las respuestas en la parte superior.

Vea el punto de referencia: https://jsperf.com/decode-html12345678/1

prueba de rendimiento

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;
}

Si necesita dejar etiquetas, elimine las dos .replace(...)llamadas (puede dejar la primera si no necesita scripts).

Илья Зеленько
fuente
66
Felicitaciones, logró ocultar la vulnerabilidad con una lógica de desinfección falsa, todo por una victoria de rendimiento que no importará en la práctica. Intenta llamar decodeEntities("</textarea '><img src=x onerror=alert(1) \">")a Firefox. Deje de intentar desinfectar el código HTML con expresiones regulares.
Wladimir Palant