๐Ÿ›ก๏ธ Trusted Types Demo

Complete Guide to XSS Prevention with Implementation Code

Interactive demonstrations with working code examples

๐Ÿงช Interactive Security Demonstrations with Code

1

HTML Injection Prevention

๐ŸŽ“ Understanding the Vulnerability

HTML injection occurs when untrusted data is inserted into the DOM without proper sanitization. The browser interprets this data as code, executing any embedded scripts.

๐Ÿ›ก๏ธ Trusted Types Defense Mechanisms:

  • โ€ข TrustedHTML Type: Only accepts sanitized HTML through policies
  • โ€ข Default Policy: Fallback sanitization for legacy code
  • โ€ข CSP Integration: Blocks violations via require-trusted-types-for
  • โ€ข Sink Protection: Guards innerHTML, outerHTML, insertAdjacentHTML, and more

๐Ÿ’ป Implementation Code

// 1. Create a sanitization policy for HTML content
const htmlSanitizationPolicy = trustedTypes.createPolicy('html-sanitizer', {
  createHTML: (dirty) => {
    // Remove dangerous elements and attributes
    const cleaned = dirty
      // Remove script tags
      .replace(/)<[^<]*)*<\/script>/gi, '')
      // Remove event handlers
      .replace(/\son\w+\s*=\s*["'][^"']*["']/gi, '')
      .replace(/\son\w+\s*=\s*[^\s>]*/gi, '')
      // Remove javascript: protocol
      .replace(/javascript:/gi, '')
      // Remove data: protocol (except images)
      .replace(/data:(?!image\/)/gi, '');
    
    console.log('Sanitized:', cleaned);
    return cleaned;
  }
});
// Safe HTML injection with Trusted Types
function injectHTMLSafely(userInput) {
  const container = document.getElementById('output');
  
  // Method 1: Using the policy directly
  const trustedHTML = htmlSanitizationPolicy.createHTML(userInput);
  container.innerHTML = trustedHTML;
  
  // Method 2: Using textContent for plain text
  // container.textContent = userInput; // No HTML rendering
  
  // Method 3: Using DOMPurify with Trusted Types
  // if (window.DOMPurify) {
  //   const clean = DOMPurify.sanitize(userInput, {
  //     RETURN_TRUSTED_TYPE: true
  //   });
  //   container.innerHTML = clean;
  // }
}

// Event handler for the demo
document.getElementById('injectBtn').addEventListener('click', () => {
  const userInput = document.getElementById('htmlInput').value;
  injectHTMLSafely(userInput);
});
// โŒ UNSAFE - Direct innerHTML assignment
function unsafeInject(userInput) {
  // This will be blocked by Trusted Types!
  element.innerHTML = userInput; // Throws TypeError
  element.outerHTML = userInput; // Also blocked
  element.insertAdjacentHTML('beforeend', userInput); // Blocked
}

// โœ… SAFE - Using Trusted Types
function safeInject(userInput) {
  // Create trusted HTML through policy
  const trustedHTML = policy.createHTML(userInput);
  element.innerHTML = trustedHTML; // Safe!
}

// โœ… SAFE - Using textContent for plain text
function setTextContent(element, userInput) {
  element.textContent = userInput; // No HTML rendering
}

// โœ… SAFE - Using DOM methods
function createSafeElement(tagName, textContent) {
  const element = document.createElement(tagName);
  element.textContent = textContent;
  return element;
}

// โœ… SAFE - Escaping HTML entities
function escapeHTML(str) {
  const div = document.createElement('div');
  div.textContent = str;
  return div.innerHTML;
}

// Use textContent for plain text
setTextContent(element, userInput);

Sanitized Output:

2

Dynamic Script Security

๐ŸŽ“ Script Injection Vectors

Attackers exploit dynamic script creation to execute arbitrary code. Common targets include eval(), Function(), setTimeout/setInterval with strings, and dynamic script elements.

๐Ÿ›ก๏ธ TrustedScript & TrustedScriptURL:

  • โ€ข TrustedScript: For inline script content and eval-like functions
  • โ€ข TrustedScriptURL: For external script sources and worker URLs
  • โ€ข Policy Validation: Scripts must pass through createScript/createScriptURL
  • โ€ข Import Maps: Protection extends to ES6 module imports

๐Ÿ’ป Implementation Code

// Create policies for script content and URLs
const scriptPolicy = trustedTypes.createPolicy('script-loader', {
  createScript: (script) => {
    // Validate script content
    if (script.includes('eval') || script.includes('Function(')) {
      throw new Error('Dangerous functions not allowed');
    }
    return script;
  },
  
  createScriptURL: (url) => {
    // Whitelist trusted domains
    const trustedDomains = [
      'https://cdn.jsdelivr.net',
      'https://unpkg.com',
      'https://cdnjs.cloudflare.com',
      window.location.origin
    ];
    
    const urlObj = new URL(url, window.location.origin);
    
    if (!trustedDomains.some(domain => urlObj.href.startsWith(domain))) {
      throw new Error(`Untrusted script source: ${url}`);
    }
    
    return url;
  }
});
// Safe dynamic script loading
function loadScriptSafely(scriptUrl) {
  const script = document.createElement('script');
  
  // Use the policy to validate the URL
  script.src = scriptPolicy.createScriptURL(scriptUrl);
  
  script.onerror = () => {
    console.error('Script failed to load:', scriptUrl);
  };
  
  script.onload = () => {
    console.log('Script loaded successfully:', scriptUrl);
  };
  
  document.head.appendChild(script);
}

// Safe inline script execution
function executeInlineScript(code) {
  const script = document.createElement('script');
  
  // Use the policy to validate the script content
  script.textContent = scriptPolicy.createScript(code);
  
  document.body.appendChild(script);
  document.body.removeChild(script); // Clean up
}

// Example usage
loadScriptSafely('https://cdn.jsdelivr.net/npm/lodash@4/lodash.min.js');
executeInlineScript('console.log("Safe script execution");');
// โŒ UNSAFE - Using eval (blocked by Trusted Types)
function unsafeEval(userCode) {
  eval(userCode); // Throws TypeError with Trusted Types
}

// โŒ UNSAFE - Using Function constructor
function unsafeFunction(userCode) {
  new Function(userCode)(); // Also blocked
}

// โŒ UNSAFE - setTimeout with string
function unsafeTimeout(userCode) {
  setTimeout(userCode, 0); // Blocked
}

// โœ… SAFE - Use JSON.parse for data
function safeJSONParse(jsonString) {
  try {
    const data = JSON.parse(jsonString);
    return data;
  } catch (e) {
    console.error('Invalid JSON:', e);
  }
}

// โœ… SAFE - Use Function references
function safeTimeout(callback) {
  setTimeout(callback, 0); // Pass function, not string
}

// โœ… SAFE - Use Web Workers for isolation
function safeWorker(code) {
  const blob = new Blob([code], { type: 'application/javascript' });
  const worker = new Worker(URL.createObjectURL(blob));
  return worker;
}

Script Execution Log:

3

Event Handler Safety

๐ŸŽ“ Event Handler Attack Surface

Inline event handlers are a major XSS vector. Attributes like onclick, onload, onerror, and over 70 other event handlers can execute JavaScript directly from HTML.

๐Ÿ›ก๏ธ Safe Event Handling Patterns:

  • โ€ข addEventListener: Programmatic event binding (preferred)
  • โ€ข Event Delegation: Single listener for multiple elements
  • โ€ข Data Attributes: Store data separately from handlers
  • โ€ข CSP unsafe-inline: Block all inline handlers when possible

๐Ÿ’ป Implementation Code

// โœ… SAFE: Using addEventListener
function createSafeButton(text, message) {
  const button = document.createElement('button');
  button.textContent = text;
  button.className = 'btn btn-primary';
  
  // Safe event handler attachment
  button.addEventListener('click', function(event) {
    // Access data from closure or data attributes
    console.log('Button clicked:', message);
    alert(message);
  });
  
  return button;
}

// โœ… SAFE: Multiple event listeners
function attachMultipleHandlers(element) {
  element.addEventListener('mouseenter', () => {
    element.classList.add('hover');
  });
  
  element.addEventListener('mouseleave', () => {
    element.classList.remove('hover');
  });
  
  element.addEventListener('click', (e) => {
    e.preventDefault();
    handleClick(element);
  });
}

// โŒ UNSAFE: Never use inline handlers
// element.innerHTML = ``;

// โœ… SAFE: Create elements programmatically
const container = document.getElementById('buttonContainer');
const safeBtn = createSafeButton('Click Me', 'Hello, World!');
container.appendChild(safeBtn);
// Event Delegation Pattern - Single listener for multiple elements
class ButtonManager {
  constructor(container) {
    this.container = container;
    this.buttons = new Map();
    
    // Single event listener for all buttons
    this.container.addEventListener('click', this.handleClick.bind(this));
  }
  
  handleClick(event) {
    // Check if clicked element is a button
    const button = event.target.closest('[data-action]');
    if (!button) return;
    
    const action = button.dataset.action;
    const id = button.dataset.id;
    
    // Execute appropriate handler
    switch(action) {
      case 'delete':
        this.handleDelete(id);
        break;
      case 'edit':
        this.handleEdit(id);
        break;
      case 'view':
        this.handleView(id);
        break;
    }
  }
  
  addButton(id, action, text) {
    const button = document.createElement('button');
    button.textContent = text;
    button.dataset.action = action;
    button.dataset.id = id;
    button.className = 'delegated-btn';
    
    this.container.appendChild(button);
    this.buttons.set(id, button);
  }
  
  handleDelete(id) {
    console.log('Deleting:', id);
    const button = this.buttons.get(id);
    if (button) {
      button.remove();
      this.buttons.delete(id);
    }
  }
  
  handleEdit(id) {
    console.log('Editing:', id);
  }
  
  handleView(id) {
    console.log('Viewing:', id);
  }
}

// Usage
const manager = new ButtonManager(document.getElementById('container'));
manager.addButton('1', 'delete', 'Delete Item 1');
manager.addButton('2', 'edit', 'Edit Item 2');
manager.addButton('3', 'view', 'View Item 3');
// Using data attributes to store information safely
function createInteractiveElement(config) {
  const element = document.createElement('div');
  element.className = 'interactive-card';
  
  // Store data in data attributes (automatically escaped)
  element.dataset.userId = config.userId;
  element.dataset.action = config.action;
  element.dataset.metadata = JSON.stringify(config.metadata);
  
  // Safe HTML content
  element.innerHTML = `
    

${escapeHtml(config.title)}

${escapeHtml(config.description)}

`; // Attach event handler that reads from data attributes const button = element.querySelector('.action-btn'); button.addEventListener('click', function() { const userId = element.dataset.userId; const action = element.dataset.action; const metadata = JSON.parse(element.dataset.metadata); performAction(userId, action, metadata); }); return element; } // HTML escaping function function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Action handler function performAction(userId, action, metadata) { console.log('Performing action:', { userId, action, metadata }); // Send to server or handle locally fetch('/api/action', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId, action, metadata }) }); } // Usage const card = createInteractiveElement({ userId: '123', action: 'update', title: 'User Profile', description: 'Click to update profile', metadata: { timestamp: Date.now(), source: 'web' } }); document.getElementById('container').appendChild(card);

Safely Created Buttons:

4

URL Injection Protection

๐ŸŽ“ Dangerous URL Schemes

URLs can execute code through various schemes: javascript:, data:, vbscript: (IE), and even jar: protocols can be exploited.

๐Ÿ›ก๏ธ URL Validation Strategies:

  • โ€ข Protocol Whitelist: Only allow http://, https://, mailto:
  • โ€ข URL Constructor: Parse and validate URL structure
  • โ€ข Same-Origin Checks: Restrict to same domain when needed
  • โ€ข Sanitize Parameters: Clean query strings and fragments

๐Ÿ’ป Implementation Code

// Comprehensive URL validation
class URLValidator {
  constructor() {
    // Allowed protocols
    this.allowedProtocols = ['http:', 'https:', 'mailto:'];
    
    // Blocked patterns
    this.blockedPatterns = [
      /javascript:/i,
      /data:(?!image\/)/i,  // Allow data: URLs only for images
      /vbscript:/i,
      /file:/i,
      /jar:/i
    ];
  }
  
  validate(url) {
    // Check for blocked patterns first
    for (const pattern of this.blockedPatterns) {
      if (pattern.test(url)) {
        throw new Error(`Blocked URL pattern: ${url}`);
      }
    }
    
    try {
      // Use URL constructor for parsing
      const urlObj = new URL(url, window.location.origin);
      
      // Check protocol
      if (!this.allowedProtocols.includes(urlObj.protocol)) {
        throw new Error(`Invalid protocol: ${urlObj.protocol}`);
      }
      
      // Additional checks
      this.checkForOpenRedirect(urlObj);
      this.checkForIDN(urlObj);
      
      return urlObj.href;
    } catch (e) {
      console.error('URL validation failed:', e);
      return null;
    }
  }
  
  checkForOpenRedirect(urlObj) {
    // Check for common open redirect patterns
    const params = urlObj.searchParams;
    const redirectParams = ['url', 'redirect', 'return', 'next', 'goto'];
    
    for (const param of redirectParams) {
      const value = params.get(param);
      if (value && !this.isSameOrigin(value)) {
        console.warn('Potential open redirect:', value);
      }
    }
  }
  
  checkForIDN(urlObj) {
    // Check for IDN homograph attacks
    if (urlObj.hostname.includes('xn--')) {
      console.warn('IDN domain detected:', urlObj.hostname);
    }
  }
  
  isSameOrigin(url) {
    try {
      const urlObj = new URL(url, window.location.origin);
      return urlObj.origin === window.location.origin;
    } catch {
      return false;
    }
  }
}

// Usage
const validator = new URLValidator();
const safeUrl = validator.validate(userProvidedUrl);
// URL Sanitization for different contexts
class URLSanitizer {
  // For anchor tags
  sanitizeForHref(url) {
    // Remove javascript: and data: protocols
    if (/^(javascript|data|vbscript):/i.test(url)) {
      return '#blocked';
    }
    
    try {
      const urlObj = new URL(url, window.location.origin);
      
      // For external links, add rel="noopener noreferrer"
      if (urlObj.origin !== window.location.origin) {
        return {
          href: urlObj.href,
          rel: 'noopener noreferrer',
          target: '_blank'
        };
      }
      
      return { href: urlObj.href };
    } catch {
      return { href: '#invalid' };
    }
  }
  
  // For image sources
  sanitizeForImg(url) {
    // Allow data URLs for images
    if (/^data:image\//i.test(url)) {
      return url;
    }
    
    // Block other data URLs
    if (/^data:/i.test(url)) {
      return '/placeholder.png';
    }
    
    // Validate other URLs
    try {
      const urlObj = new URL(url, window.location.origin);
      if (urlObj.protocol === 'https:' || urlObj.protocol === 'http:') {
        return urlObj.href;
      }
    } catch {
      return '/placeholder.png';
    }
    
    return '/placeholder.png';
  }
  
  // For iframe sources
  sanitizeForIframe(url) {
    // Very strict - only allow same-origin or trusted domains
    const trustedDomains = [
      'https://www.youtube.com',
      'https://player.vimeo.com'
    ];
    
    try {
      const urlObj = new URL(url);
      
      // Check if same-origin
      if (urlObj.origin === window.location.origin) {
        return url;
      }
      
      // Check trusted domains
      if (trustedDomains.some(domain => url.startsWith(domain))) {
        return url;
      }
    } catch {
      return 'about:blank';
    }
    
    return 'about:blank';
  }
}

// Usage
const sanitizer = new URLSanitizer();

// Create safe link
function createSafeLink(url, text) {
  const a = document.createElement('a');
  const sanitized = sanitizer.sanitizeForHref(url);
  
  if (typeof sanitized === 'object') {
    Object.assign(a, sanitized);
  } else {
    a.href = sanitized;
  }
  
  a.textContent = text;
  return a;
}
// Safe navigation and redirection
class SafeNavigator {
  // Safe window.location assignment
  navigateTo(url) {
    const validated = this.validateURL(url);
    if (validated) {
      window.location.href = validated;
    } else {
      console.error('Invalid navigation URL:', url);
      this.handleInvalidNavigation();
    }
  }
  
  // Safe window.open
  openWindow(url, target = '_blank') {
    const validated = this.validateURL(url);
    if (validated) {
      const newWindow = window.open(validated, target, 'noopener,noreferrer');
      return newWindow;
    }
    return null;
  }
  
  // Safe form action
  setFormAction(form, url) {
    const validated = this.validateURL(url);
    if (validated) {
      form.action = validated;
      // Add CSRF token if needed
      this.addCSRFToken(form);
    }
  }
  
  // Validate and sanitize URL
  validateURL(url) {
    try {
      const urlObj = new URL(url, window.location.origin);
      
      // Block dangerous protocols
      if (!['http:', 'https:'].includes(urlObj.protocol)) {
        return null;
      }
      
      // For production: implement additional checks
      // - Domain whitelist
      // - Path validation
      // - Parameter sanitization
      
      return urlObj.href;
    } catch {
      return null;
    }
  }
  
  // Handle invalid navigation attempts
  handleInvalidNavigation() {
    // Log security event
    console.error('Blocked navigation attempt');
    
    // Show user-friendly error
    alert('The link you clicked appears to be invalid.');
    
    // Optionally report to server
    this.reportSecurityEvent('invalid_navigation');
  }
  
  // Add CSRF protection
  addCSRFToken(form) {
    const token = document.querySelector('meta[name="csrf-token"]')?.content;
    if (token) {
      const input = document.createElement('input');
      input.type = 'hidden';
      input.name = 'csrf_token';
      input.value = token;
      form.appendChild(input);
    }
  }
  
  // Report security events
  reportSecurityEvent(event) {
    fetch('/api/security/report', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        event,
        timestamp: Date.now(),
        url: window.location.href
      })
    });
  }
}

// Usage
const navigator = new SafeNavigator();

// Safe navigation
document.getElementById('safeLink').addEventListener('click', (e) => {
  e.preventDefault();
  navigator.navigateTo(e.target.href);
});

// Safe form submission
const form = document.getElementById('myForm');
navigator.setFormAction(form, '/api/submit');

Validated Links:

๐Ÿ“œ Security Console

Real-time security events and policy violations:

โœ… Trusted Types initialized. Security policies are active.

๐Ÿ›ก๏ธ XSS Prevention Strategies

Input Validation

  • โ€ข Validate all user input on both client and server
  • โ€ข Use allowlists for expected input patterns
  • โ€ข Reject or sanitize suspicious content
  • โ€ข Implement proper length restrictions

Output Encoding

  • โ€ข HTML entity encoding for HTML contexts
  • โ€ข JavaScript encoding for script contexts
  • โ€ข URL encoding for URL parameters
  • โ€ข CSS encoding for style contexts

Content Security Policy

  • โ€ข Implement strict CSP headers
  • โ€ข Use nonces or hashes for inline scripts
  • โ€ข Restrict script sources to trusted domains
  • โ€ข Enable Trusted Types enforcement

Framework Security

  • โ€ข Use framework's built-in XSS protection
  • โ€ข Avoid dangerous APIs (innerHTML, eval)
  • โ€ข Keep frameworks and libraries updated
  • โ€ข Use security linters and analyzers

๐Ÿ“– Standards & Specifications

W3C Trusted Types Specification

The official W3C specification defining the Trusted Types API for preventing DOM XSS attacks.

View W3C Specification โ†’

Content Security Policy Level 3

CSP3 specification including require-trusted-types-for directive.

View CSP3 Specification โ†’

OWASP XSS Prevention Cheat Sheet

Comprehensive guide for preventing XSS vulnerabilities in web applications.

View OWASP Guide โ†’

MDN Web Docs - Trusted Types

Developer-friendly documentation with examples and browser compatibility.

View MDN Documentation โ†’

๐Ÿš€ Advanced Topics

Migration Strategies

Migrating existing applications to Trusted Types:

  • โ€ข Start with report-only mode to identify violations
  • โ€ข Create default policies for gradual migration
  • โ€ข Refactor dangerous sink usage incrementally
  • โ€ข Use automated tools to find violations
  • โ€ข Test thoroughly in staging environments

Performance Considerations

Optimizing Trusted Types implementation:

  • โ€ข Cache policy creation to avoid overhead
  • โ€ข Minimize policy complexity for better performance
  • โ€ข Use efficient sanitization libraries
  • โ€ข Profile and benchmark policy execution
  • โ€ข Consider lazy loading for non-critical policies

Browser Support & Polyfills

Current implementation status across major browsers:

Browser Version Support
Chrome/Edge 83+ โœ… Full
Firefox In Development ๐Ÿšง Tracking
Safari 15.4+ โœ… Full
Polyfill Available โšก Limited

๐Ÿ“ Note: Firefox implementation is actively being developed. Track progress on Bug 1508286 . Consider using feature detection and progressive enhancement for cross-browser compatibility.

Integration with Frameworks

Using Trusted Types with modern frameworks:

  • โ€ข React: Use DOMPurify with dangerouslySetInnerHTML
  • โ€ข Angular: Built-in Trusted Types support in v11+
  • โ€ข Vue: Custom directives for safe HTML binding
  • โ€ข Svelte: Compile-time XSS prevention
  • โ€ข Web Components: Shadow DOM security boundaries

๐Ÿ”’ Privacy & Analytics

Our Commitment to Privacy

This demo uses privacy-friendly analytics to understand usage patterns and improve the educational experience. We respect your privacy and follow these principles:

  • โ€ข No personal data collection - We don't collect names, emails, or any PII
  • โ€ข IP anonymization - IP addresses are anonymized before processing
  • โ€ข No cross-site tracking - Analytics are limited to this demo only
  • โ€ข No advertising - Data is never used for advertising purposes
  • โ€ข Cookie-free option - Analytics work without storing cookies

What We Track

We collect anonymous usage data to improve the demo:

  • โ€ข Page views and navigation patterns
  • โ€ข Demo feature interactions (which examples are tested)
  • โ€ข Code copying events (to understand useful examples)
  • โ€ข Browser and device types (for compatibility)
  • โ€ข General geographic region (country level only)

Your Control

You have full control over analytics on this site. You can change your preference at any time: