Securing Vue.js Against XSS

Vue.js provides automatic XSS protection through template escaping, but v-html usage and dynamic attribute binding can reintroduce cross-site scripting risks.

Introduction

Your Vue.js application renders user-generated content throughout its interface—comments, profile bios, and custom descriptions. The development team trusts Vue's automatic escaping until a security researcher demonstrates that attackers are injecting malicious scripts through a rich text editor that uses v-html. These scripts steal session tokens from other users, leading to account compromises across your platform.

Vue.js automatically escapes content rendered through interpolation ({{ }}), making basic XSS attacks ineffective. However, several patterns reintroduce XSS risks: using v-html to render HTML content, dynamically binding attributes like href, and improperly handling URL parameters. This guide covers Vue.js-specific XSS prevention techniques and how AI-agentic testing validates frontend security.

Understanding the Risk

Cross-site scripting allows attackers to execute JavaScript in victims' browsers, stealing credentials, hijacking sessions, or performing actions on users' behalf. Vue.js mitigates many XSS vectors by default, but developers can inadvertently disable these protections.

The v-html Directive: Vue's v-html renders raw HTML, bypassing automatic escaping:

<template>
  <!-- VULNERABLE: Renders HTML without sanitization -->
  <div v-html="userContent"></div>
</template>
 
<script>
export default {
  data() {
    return {
      // Attacker provides: <img src=x onerror="alert(document.cookie)">
      userContent: this.loadUserContent()
    }
  }
}
</script>

Dynamic Attribute Binding: Binding user input to certain attributes creates injection opportunities:

<template>
  <!-- VULNERABLE: javascript: URLs execute code -->
  <a :href="userLink">Click here</a>
</template>

Prevention Best Practices

Avoid v-html When Possible

Use text interpolation instead of v-html whenever you don't need HTML rendering:

<template>
  <!-- SAFE: Automatically escaped -->
  <p>{{ userComment }}</p>
  
  <!-- For multi-line text, use CSS -->
  <p class="preserve-whitespace">{{ userBio }}</p>
</template>
 
<style>
.preserve-whitespace {
  white-space: pre-wrap;
}
</style>

Sanitize HTML Before Using v-html

When HTML rendering is necessary, sanitize content with DOMPurify:

<template>
  <div v-html="sanitizedContent"></div>
</template>
 
<script>
import DOMPurify from 'dompurify';
 
export default {
  props: { rawContent: String },
  computed: {
    sanitizedContent() {
      return DOMPurify.sanitize(this.rawContent, {
        ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'a'],
        ALLOWED_ATTR: ['href'],
        ALLOW_DATA_ATTR: false
      });
    }
  }
}
</script>

Validate URL Bindings

Prevent javascript: and data: URLs in href attributes:

<template>
  <a :href="safeUrl">{{ linkText }}</a>
</template>
 
<script>
export default {
  props: { userUrl: String, linkText: String },
  computed: {
    safeUrl() {
      const url = this.userUrl?.toLowerCase().trim() || '';
      
      if (url.startsWith('javascript:') || url.startsWith('data:')) {
        return '#';
      }
      
      if (url && !url.startsWith('http://') && !url.startsWith('https://') && 
          !url.startsWith('/') && !url.startsWith('#')) {
        return 'https://' + url;
      }
      
      return this.userUrl;
    }
  }
}
</script>

Implement Content Security Policy

Configure CSP headers to mitigate XSS impact:

// Nuxt.js example in nuxt.config.js
export default {
  render: {
    csp: {
      policies: {
        'default-src': ["'self'"],
        'script-src': ["'self'"],
        'style-src': ["'self'", "'unsafe-inline'"],
        'img-src': ["'self'", 'data:', 'https:'],
        'frame-ancestors': ["'none'"]
      }
    }
  }
}

Secure Router Navigation

Validate route parameters and query strings:

import { createRouter, createWebHistory } from 'vue-router';
 
const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/user/:id',
      component: UserProfile,
      beforeEnter: (to, from, next) => {
        if (!/^\d+$/.test(to.params.id)) {
          next({ name: 'NotFound' });
          return;
        }
        next();
      }
    }
  ]
});

Use Vue 3 Security Features

Vue 3 provides additional security through Composition API patterns:

<script setup>
import { computed } from 'vue';
import DOMPurify from 'dompurify';
 
const props = defineProps({ userHtml: String });
 
const cleanHtml = computed(() => {
  if (!props.userHtml) return '';
  return DOMPurify.sanitize(props.userHtml, { USE_PROFILES: { html: true } });
});
</script>
 
<template>
  <div v-html="cleanHtml"></div>
</template>

Why Traditional Pentesting Falls Short

Vue.js applications present unique testing challenges. XSS vulnerabilities may only manifest in specific component states, with certain prop combinations, or after particular user interactions. Manual testers typically check obvious input fields but may miss XSS in dynamically rendered content, deeply nested components, or state-dependent rendering logic.

How AI-Agentic Testing Solves It

RedVeil's AI agents analyze Vue.js applications dynamically, interacting with components and testing XSS vectors across the application's state space. The platform identifies dangerous patterns like v-html usage with unsanitized input, validates that CSP headers are properly configured, and tests dynamic attribute bindings for injection opportunities.

On-demand testing allows security validation after deploying new components or features that handle user content. RedVeil provides evidence of exploitable XSS, showing exactly how attackers could inject scripts.

Conclusion

Vue.js provides strong default XSS protection, but developers can bypass these safeguards through v-html, dynamic bindings, and URL handling. Safe patterns—avoiding v-html, sanitizing when necessary, validating URLs, and implementing CSP—maintain security while enabling rich functionality.

AI-agentic testing from RedVeil validates that your Vue.js application resists XSS attacks across its component hierarchy and interaction flows.

Secure your Vue.js application against XSS—start testing with RedVeil.

Ready to run your own test?

Start your first RedVeil pentest in minutes.