Understanding CSP Violation Reports

Understanding CSP Violation Reports

CSP violation reports provide crucial insights into policy effectiveness and potential security threats. Understanding how to interpret these reports forms the foundation of effective CSP debugging. Each violation report contains specific information that helps identify the source and nature of the policy breach.

A typical CSP violation report structure:

{
  "csp-report": {
    "blocked-uri": "https://evil-script.com/malicious.js",
    "column-number": 12,
    "disposition": "enforce",
    "document-uri": "https://example.com/page.html",
    "effective-directive": "script-src",
    "line-number": 8,
    "original-policy": "default-src 'self'; script-src 'self' https://trusted.com",
    "referrer": "https://example.com/",
    "script-sample": "eval('malicious code')",
    "source-file": "https://example.com/app.js",
    "status-code": 0,
    "violated-directive": "script-src"
  }
}

Building a comprehensive violation monitoring system:

// CSP Violation Logger and Analyzer
class CSPViolationAnalyzer {
  constructor(options = {}) {
    this.violations = [];
    this.patterns = new Map();
    this.thresholds = options.thresholds || {
      sameViolation: 10,
      timeWindow: 300000 // 5 minutes
    };
  }
  
  analyzeViolation(report) {
    const violation = report['csp-report'];
    const key = this.generateViolationKey(violation);
    
    // Track violation patterns
    if (!this.patterns.has(key)) {
      this.patterns.set(key, {
        count: 0,
        firstSeen: Date.now(),
        lastSeen: Date.now(),
        samples: []
      });
    }
    
    const pattern = this.patterns.get(key);
    pattern.count++;
    pattern.lastSeen = Date.now();
    pattern.samples.push(violation);
    
    // Analyze for potential attacks
    return {
      isLikelyAttack: this.detectPotentialAttack(pattern),
      isKnownResource: this.checkKnownResources(violation),
      severity: this.calculateSeverity(violation, pattern),
      recommendation: this.generateRecommendation(violation)
    };
  }
  
  generateViolationKey(violation) {
    // Create unique key for grouping similar violations
    return `${violation['violated-directive']}:${violation['blocked-uri']}:${violation['source-file']}`;
  }
  
  detectPotentialAttack(pattern) {
    // High frequency of violations in short time window
    const timeWindow = Date.now() - pattern.firstSeen;
    const rate = pattern.count / (timeWindow / 1000); // violations per second
    
    return rate > 0.1 || pattern.count > this.thresholds.sameViolation;
  }
  
  checkKnownResources(violation) {
    const knownDomains = [
      'googleapis.com',
      'googletagmanager.com',
      'google-analytics.com',
      'cloudflare.com',
      'jsdelivr.net',
      'unpkg.com'
    ];
    
    const blockedUri = violation['blocked-uri'];
    return knownDomains.some(domain => blockedUri.includes(domain));
  }
  
  calculateSeverity(violation, pattern) {
    let severity = 'low';
    
    if (violation['violated-directive'].includes('script-src')) {
      severity = 'high';
    } else if (violation['violated-directive'].includes('connect-src')) {
      severity = 'medium';
    }
    
    if (pattern.count > 100) {
      severity = 'critical';
    }
    
    return severity;
  }
  
  generateRecommendation(violation) {
    const directive = violation['violated-directive'];
    const blockedUri = violation['blocked-uri'];
    
    if (this.checkKnownResources(violation)) {
      return {
        action: 'consider-allowing',
        suggestion: `Add '${new URL(blockedUri).origin}' to ${directive}`,
        risk: 'low'
      };
    }
    
    if (blockedUri.includes('eval') || blockedUri.includes('inline')) {
      return {
        action: 'refactor-code',
        suggestion: 'Replace inline scripts with external files or use nonces',
        risk: 'high'
      };
    }
    
    return {
      action: 'investigate',
      suggestion: 'Review the source and determine if this resource is legitimate',
      risk: 'medium'
    };
  }
}