Sanitizing HTML in Vue.js
As part of my goal to learn more about Vue (and, honestly, find things to blog about), I came across this interesting StackOverflow post: How to Sanitize HTML Received from an API Call in Vue.js. I did a quick Google search and came across a nice little library that makes this easy - vue-sanitize. I thought it would be nice to give it a try (especially since I was suggesting it as a solution) so I whipped up a quick demo.
Before I start though, it's good to remember how Vue treats HTML in data in general. Consider the following data:
message:` My <strong>milkshake</strong> brings all the boys to the yard<br/> And <i>they're</i> like, it's better than yours `
This is a string with three HTML tags in it. Nothing scary, but let's see what happens if you try to output it:
<template> <div> {{ message }} </div> </template>
This will return:
My <strong>milkshake</strong> brings all the boys to the yard<br/> And <i>they're</i> like, it's better than yours
As you can see, the HTML is escaped. Not ideal, right? If you know you can trust the data, you can use the
v-html directive:
<template> <div> <span v-</span> </div> </template>
This will return what you expect. Cool! But... it's very black and white. You either escape all HTML or allow all HTML. What if you want something in between? This is where vue-sanitize comes in. Not only will it allow you to use a whitelist of "safe" HTML tags, it will remove disallowed tags rather than escaping them.
Using it is pretty simple and covered in the docs. Add the NPM package, and once done, you can then add it to your Vue.js code. From what I can see there's no support for "script tag Vue", so you'll need to have a proper Vue application.
Outside of that, there's only one main API,
this.$sanitize(someVariable). This will return a string with unsafe HTML tags removed. You still need to use
v-html to render the safe HTML of course.
The docs don't mention the defaults, but as the library wraps another library, sanitize-html, you can check their docs for the defaults:
Let me demonstrate an example before I show how you can customize the defaults. First, my main.js, which just loads in the library.
import Vue from "vue"; import App from "./App.vue"; import VueSanitize from "vue-sanitize"; Vue.use(VueSanitize); Vue.config.productionTip = false; new Vue({ render: h => h(App) }).$mount("#app");
And now my test:
<template> <div> Escaped: {{ message }} <p/> <span v-</span> <hr/> <span v-</span> </div> </template> <script> export default { name: "App", data() { return { message:` My <strong>milkshake</strong> brings all the boys to the yard<br/> And <i>they're</i> like, it's better than yours ` } }, computed:{ cleanMessage() { return this.$sanitize(this.message); } } }; </script>
So I begin with two simple tests related to what I said before - the default behavior in Vue and the use of
v-html. I don't use the sanitize code until
cleanMessage. I've got that bound to a computed value that returns the sanitized version. The output is:
In this case, there's no difference between the built-in version and the sanitize version. I only used three simple HTML tags. Let's see what happens when we change the defaults.
In order to change the defaults, you create your own object containing the defaults you would like. The main sanitize-html site has some good examples on how to slightly modify the built in defaults. For my testing, I wanted to allow everything the defaults allowed, except for the
<strong> tag. This is how I did it.
import Vue from "vue"; import App from "./App.vue"; import VueSanitize from "vue-sanitize"; let defaults = VueSanitize.defaults; defaults.allowedTags = defaults.allowedTags.filter(t => { return t !== 'strong'; }); Vue.use(VueSanitize,defaults); Vue.config.productionTip = false; new Vue({ render: h => h(App) }).$mount("#app");
Basically - loop through the array of
allowedTags and remove when the tag name is
strong. It's easier if you just want to define a short list of tags you want - just pass an array of strings.
The result is as you expect:
Notice though that the
<strong> tag wasn't escaped, it was removed. That's much better than escaping it (typically). I could see this being really useful for allowing all the format tags but removing
<a> for example. (And
<iframe> and probably other's I've forgotten.)
Anyway, I hope this is helpful. I've got a CodeSandbox with this running and you can play with it below.
Header photo by Oliver Hale on Unsplash