All files / src/plugins security.js

0% Statements 0/119
0% Branches 0/1
0% Functions 0/1
0% Lines 0/119

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                                                                                                                                                                                                                               
/**
 * Vue 3 Security Plugin
 *
 * Integrates client-side security monitoring with Vue.js application
 */

import securityMonitor from '../utils/security-monitor.js';
import { getApiBaseUrl } from '../config/api';
import { recordError } from '../faro';

const SecurityPlugin = {
  install(app, options = {}) {
    // Enable the security monitor
    securityMonitor.enable();

    // Make security monitor available globally
    app.config.globalProperties.$security = securityMonitor;
    app.provide('security', securityMonitor);

    // Set up Vue-specific error handling
    const originalErrorHandler = app.config.errorHandler;
    app.config.errorHandler = (err, instance, info) => {
      const componentName =
        instance?.$options?.name || instance?.$?.type?.name || 'Unknown';

      // Report Vue errors to security monitor
      securityMonitor.reportCustomEvent('vue_error', 'medium', {
        error: {
          message: err.message,
          stack: err.stack,
          name: err.name,
        },
        componentInfo: info,
        componentName,
        location: window.location.href,
      });

      // Record error metric for OTEL
      recordError('vue_error', {
        component: componentName,
        severity: 'medium',
      });

      // Call original handler if it exists
      if (originalErrorHandler) {
        originalErrorHandler(err, instance, info);
      } else {
        console.error('Vue Error:', err, info);
      }
    };

    // Set up Content Security Policy headers monitoring
    if (options.csp !== false) {
      this.setupCSPHeaders();
    }

    // Set up performance monitoring for Vue components
    if (options.performanceMonitoring !== false) {
      this.setupVuePerformanceMonitoring(app);
    }
  },

  setupCSPHeaders() {
    // Add CSP meta tag if not present
    if (!document.querySelector('meta[http-equiv="Content-Security-Policy"]')) {
      const cspMeta = document.createElement('meta');
      cspMeta.setAttribute('http-equiv', 'Content-Security-Policy');
      cspMeta.setAttribute('content', this.getCSPPolicy());
      document.head.appendChild(cspMeta);
    }
  },

  getCSPPolicy() {
    // Define a strict CSP policy
    // Use runtime API detection
    const apiUrl = getApiBaseUrl();

    const policy = [
      "default-src 'self'",
      "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://unpkg.com",
      "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net",
      "font-src 'self' https://fonts.gstatic.com",
      `img-src 'self' data: https: ${apiUrl.startsWith('http://') ? 'http://127.0.0.1:*' : ''}`,
      `connect-src 'self' ${apiUrl} https://api.github.com https://*.grafana.net ws: wss:`,
      "frame-src 'none'",
      "object-src 'none'",
      "base-uri 'self'",
      "form-action 'self'",
      "frame-ancestors 'none'",
      'block-all-mixed-content',
      'upgrade-insecure-requests',
    ].join('; ');

    return policy;
  },

  setupVuePerformanceMonitoring(app) {
    // Monitor component render times
    const originalMount = app.mount;
    app.mount = function (...args) {
      const start = performance.now();
      const result = originalMount.apply(this, args);
      const duration = performance.now() - start;

      if (duration > 1000) {
        // Slow mount (> 1 second)
        securityMonitor.reportCustomEvent('slow_component_mount', 'low', {
          duration,
          component: 'app_root',
          type: 'mount_performance',
        });
      }

      return result;
    };
  },
};

export default SecurityPlugin;