flawopen.com/XSS in JavaScript/Vue v-html
Is Vue's v-html safe to use?
Vue's {{ comment }} interpolation is like displaying a quoted note behind glass: its angle brackets remain text. v-html asks the browser to open the note as HTML markup. If that markup is user-controlled, the browser may treat it as page structure or active content, creating an XSS risk.
<p v-html="comment"></p>
// comment is parsed as real markup
<script setup>
import { computed } from 'vue'
import DOMPurify from 'dompurify'
const props = defineProps({ comment: String })
const safeComment = computed(() =>
DOMPurify.sanitize(props.comment ?? '', {
ALLOWED_TAGS: ['p', 'strong', 'em'],
ALLOWED_ATTR: []
})
)
</script>
<template>
<div v-html="safeComment"></div>
</template>
Sanitize rich HTML before v-html
Prefer Vue's {{ }} interpolation when content is meant to be text. If a product feature truly needs user-authored rich HTML, sanitize it with a maintained library such as DOMPurify using a narrow allowlist, then bind only the sanitized result to v-html. Keep the sanitizer updated and test the allowed markup policy. Sanitization reduces risk but does not make arbitrary HTML universally safe; use a sandboxed iframe when content must remain untrusted and active. Vue does not compile v-html content as Vue templates.
Audit v-html and trace the data flow
Search every v-html binding. For each one, identify the source, the sanitizer and configuration, and every viewer who can see the rendered content. Flag direct user input, stored comments, CMS fields, and API responses; verify that ordinary text uses interpolation instead.
grep -rn "v-html" --include="*.vue" .Common questions
They both bypass the framework's ordinary text-escaping path and insert HTML into the DOM. Use a maintained sanitizer for necessary rich HTML, and do not treat sanitized output as universally safe.
References
- Vue.js docs — Security, HTML Content
- DOMPurify — HTML sanitizer documentation
- OWASP Cross Site Scripting Prevention Cheat Sheet
- flawopen.com — XSS in JavaScript