2

I'm writing a website using VueJS which allows (selected) users to add scripts that are automatically executed upon page load. Here's a sample text that a user might upload:

<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.5/howler.js"></script>
<script>
    var sound = new howler.Howl({
        src: ['./sample.mp3']
    )}.play();
</script>

This text is stored into a string after retrieving from API backend. The problem now is: I couldn't get it to execute however I try. Is there an option in VueJS that can automatically execute javascripts in strings?

As a reference, here's my code:

var temp_arr = utils.preprocess(vm.chapterInfo.Content)
vm.display = temp_arr[0]
vm.control_script = console.log(temp_arr[1])

// None of below worked
eval(vm.control_script)
document.getElementsByTagName("head")[0].appendChild(control_script)
1
  • 3
    Although it's possible to achieve what you want, don't do it, as the main security rule of the web applications is to prevent the users from running their scripts. Commented Nov 26, 2017 at 15:49

2 Answers 2

5

The problem isn't a Vue one, but a JavaScript one.

I assume that you already understand the security implications of allowing users to run JavaScript; it's rarely a good idea. Sites like JSFiddle do it successfully, however it will take a lot of work and understanding to make it safe, so if you're not 100% sure with what you are doing, then as @WaldemarIce said, you shouldn't do it!

Right, with the warning out the way, you need to do a few things to get this to work:

1) Load the external scripts:

loadScripts() {
    return new Promise(resolve => {

      let scriptEl = document.createElement("script");
      scriptEl.src = "https://cdnjs.cloudflare.com/ajax/libs/howler/2.0.5/howler.js";
      scriptEl.type = "text/javascript";

      // Attach script to head
      document.getElementsByTagName("head")[0].appendChild(scriptEl); 
      // Wait for tag to load before promise is resolved     
      scriptEl.addEventListener('load',() => {
        resolve();
      });
    });
  }

Here I'm simply attaching the external script to the head of the document and attaching a load event, which resolves the Promise when loaded.

2) Now we have loaded the external script we can execute the remainder of the script. You will need to strip out the script tags, so you can do something like this:

executeScript() {
  // remove script tags from string (this has been declared globally)
  let script = string.replace(/<\/?script>/g,"")
  eval(script)
}

Form the Vue perspective, you can then execute this inside the created hook:

created() {
  this.loadScripts().then(() => {
    this.executeScript();
  });
},

I'll leave it to you to extract the external scripts you want to load from your user input, but here's a JSFiddle: https://jsfiddle.net/49dq563d/

Sign up to request clarification or add additional context in comments.

Comments

0

I recently came across this problem and had to extend on the answer from @craig_h. The example below allows full embed code to be sent through as string (HTML elements as well as scripts and inline JS). This is using DOMParser.

<div ref="htmlDump"></div>
<script>
import Vue from "vue";
export default {
  ...
  methods: {
    cloneAttributes(element, sourceNode) {
      let attr;
      let attributes = Array.prototype.slice.call(sourceNode.attributes);
      while(attr = attributes.pop()) {
        element.setAttribute(attr.nodeName, attr.nodeValue);
      }
    }
  },
  mounted(){
    if(this.embedString && this.embedString.length > 0)
    {
      //Parse the code given from the API into a new DOM so we can easily manipulate it
      var parser = new DOMParser();
      var htmlDoc = parser.parseFromString(this.embedString, 'text/html');
      //Get the contents of the new DOM body and loop through.
      //We want to add all HTML elements to the page and run / load all JS
      var kids = [...htmlDoc.body.children];
      let len = kids.length;
      for (var i = 0; i < len; i++) {
        var item = kids[i];
        if(item.tagName == "SCRIPT")
        {
          //If we have a 'src' attribute then we're loading in a script
          if(item.hasAttribute('src'))
          {
            //Create a new element within the current doc to trigger the script load
            let scriptEl = document.createElement("script");
            //Copy all attributes from the source element to the new one
            this.cloneAttributes(scriptEl, item);
            //Attach script to the DOM to trigger it to load
            this.$refs.htmlDump.appendChild(scriptEl);
          } else {
            //if we don't have a 'src' attribute then we have some code to run
            eval(item.innerText);
          }
        } else{
          this.$refs.htmlDump.appendChild(item);
        }
      }
    }
  }
  ...
}
</script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.