0
votes
var str = '<script>alert(1)</script>';
var decoded = $("<div/>").html(str).text();


 //document.write(decoded); THIS WORKS, but I want to avoid using document.write

var para = document.createElement("script");
var node = document.createTextNode(decoded);
para.appendChild(node);
var element = document.getElementById("div1");
element.appendChild(para);

jsfiddle.net/1bz9mL7k/2

The problem is that due to presence of script tag, this createElement method won't work. Closing script tag in the encoded str will cause syntax error (Uncaught SyntaxError: Unexpected token '<'). Document.write works all perfect but I want to avoid due to its disadvantages of parser-block etc.

In real use case, the encoded string will be of some ads which will be entered by users. Please note we can't remove script tags from code they enter and are saving it in database by using htmlentities($adcode).

User input is trusted, so no issue of any xss vulnerability which might happen by solution proposed.

One of the sample user input :

            <script async="async" src="someURL.js"></script>
            <script>
            var googletag = googletag || {};
            googletag.cmd = googletag.cmd || [];
            </script>
            <div id="xyzid">
            <script type="text/javascript">
            googletag.cmd.push(function() {
            googletag.pubads().display("Mobile_ATF_300x250", [300,250],"div-gpt-ad-0");
            });
            </script>
            </div> 

There can be various different version of ad set with unknown pattern.

Additional not so relevant info : We will be further using something like <script> if(var==1) { codeblock1 } else { codeblock2} </script> (here codeblock is 1st code blockset). So any soln which includes use decoded user input directly won't be useful as the script block will break in if-else.

2
Can you share a jsfiddle.net, codePen or something with an example ?. - Carlos1232
jsfiddle.net/1bz9mL7k/1 I can't replicate the exact ad code as it won't run on jsfiddle, but this dummy example will be able to display current issue. - pulkit8
Should you not resolve this on the server side, and just spit out the HTML as a response to an HTTP request? - trincot
@pulkit8 Solved, wish that helps you ! . - Carlos1232
@Carlos1232 not exactly, I replied to your answer - pulkit8

2 Answers

0
votes

So for answering this question I had to change first the ad2 since it had inside the script tag, wich is no longer necessary,what you can do is to create an element called Script, of type text/javascript and insert the text, then insert it into the body.

var ad2 = `alert(12)`;
var decoded = decodeHtml(ad2);
decoded = decoded.replace(/(<([^>]+)>)/ig,'')
var para = document.createElement("script");
para.type = "text/javascript";
para.text = decoded
var element = document.getElementById("div12")
element.appendChild(para)

Here's a working demo in jsfiddle on how to do It.

https://jsfiddle.net/59g24do1/

EDIT For multiples scripts use this instead; what we do here is first we create an array of the tags,after that we create one by one with the previous function, doing a for loop in order to execute them all. = >

function getParagraphs(htmlString) {
  const div = document.createElement("div");
  div.insertAdjacentHTML("beforeend", htmlString);
  
  return Array.from(div.querySelectorAll("script"))                 
              .map(script => script.outerHTML);
}


for(let i = 0;i<decoded.length;i++){
  if(decoded[i].includes("src")){   
            let srcString = decoded[i].match('src="(.*)"')
        var para = document.createElement("script");
        let aux = decoded[i].replace(/(<([^>]+)>)/ig,'')
        para.type = "text/javascript";
        para.src = srcString[1]
        para.text = aux
        var element = document.getElementById("div12")
        element.appendChild(para)
  }else{
        let aux = decoded[i].replace(/(<([^>]+)>)/ig,'')
        let para = document.createElement("script");
      para.type = "text/javascript";
      para.text = aux
      let element = document.getElementById("div12")
            element.appendChild(para)
  }
        
  }

For complete example use there's a jsfiddle https://jsfiddle.net/f2Leu74g/2/

-1
votes

I was able to resolve it by below code, thought to update if someone in future is stuck on the same problem.

var ad = "whatever encoded here script code";

function decodeHtml(html) {
            return $("<textarea/>").html(html).text();
        }

function fetch(id, html) {
  var elm = document.getElementById(id);  
  elm.innerHTML = html;  
  var scripts = elm.getElementsByTagName("script");
  var scriptsClone = [];
  for (var i = 0; i < scripts.length; i++) {
    scriptsClone.push(scripts[i]);
  }
  
for (var i = 0; i < scriptsClone.length; i++) {
    var currentScript = scriptsClone[i];
    var s = document.createElement("script");
    // Copy all the attributes from the original script
    for (var j = 0; j < currentScript.attributes.length; j++) {
      var a = currentScript.attributes[j];
      s.setAttribute(a.name, a.value);
    }
    s.appendChild(document.createTextNode(currentScript.innerHTML));
    currentScript.parentNode.replaceChild(s, currentScript);
  }
}

var decoded = decodeHtml(ad);
fetch('YourDivID',decoded);