Use Vue's built-in security
It's an interesting problem, but there is a simple solution that leverages Vue's built-in security.
If we split the description at carriage returns we can then use v-for
with a template to output each text fragment appended with a <br>
break.
<p class="description" v-if="process.description">
<template v-for="line in process.description?.split('\r\n')">
{{line}}<br>
</template>
</p>
Which produces rendered output:
<p class="description">
This is a description for Process<br>
Here is no line break<br>
</p>
Further Reading
- OWASP XSS Filter Evasion Cheat Sheet
- Vue Security Best Practices
Snippet
For this test, a XSS alert has been embedded in the description. Vue escapes the alert and displays it as text. Yet, if we were to use v-html to render the description it would trigger the XSS alert.
const {
createApp
} = Vue
createApp({
data() {
return {
process: {
description: 'This is a description for Process\r\nHere is no line break\r\n<svg/onload=alert("XSS")>'
}
}
}
}).mount('#app')
body {
font-family: sans-serif;
}
.description {
border: 1px solid blue;
border-radius: 0.5rem;
padding: 1rem;
}
<div id="app">
<p class="description" v-if="process.description">
<template v-for="line in process.description?.split('\r\n')">
{{line}}<br>
</template>
</p>
<!-- v-html will trigger a XSS alert
<div v-html="process.description"></div>
-->
<!-- v-text will NOT trigger XSS alert, but no line breaks
<div v-text="process.description"></div>
-->
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>