Automated CSP Testing Frameworks

Automated CSP Testing Frameworks

Implementing automated testing ensures CSP policies remain effective as applications evolve:

// CSP Testing Framework
class CSPTestFramework {
  constructor(baseUrl) {
    this.baseUrl = baseUrl;
    this.testResults = [];
  }
  
  async runTestSuite() {
    const tests = [
      this.testHeaderPresence,
      this.testPolicyStrength,
      this.testResourceLoading,
      this.testViolationReporting,
      this.testBrowserCompatibility,
      this.testPerformanceImpact
    ];
    
    for (const test of tests) {
      try {
        const result = await test.call(this);
        this.testResults.push(result);
      } catch (error) {
        this.testResults.push({
          name: test.name,
          passed: false,
          error: error.message
        });
      }
    }
    
    return this.generateReport();
  }
  
  async testHeaderPresence() {
    const response = await fetch(this.baseUrl);
    const cspHeader = response.headers.get('Content-Security-Policy');
    const reportOnlyHeader = response.headers.get('Content-Security-Policy-Report-Only');
    
    return {
      name: 'CSP Header Presence',
      passed: !!(cspHeader || reportOnlyHeader),
      details: {
        enforcing: !!cspHeader,
        reportOnly: !!reportOnlyHeader,
        policy: cspHeader || reportOnlyHeader
      }
    };
  }
  
  async testPolicyStrength() {
    const response = await fetch(this.baseUrl);
    const policy = response.headers.get('Content-Security-Policy') || '';
    
    const weaknesses = [];
    if (policy.includes("'unsafe-inline'")) {
      weaknesses.push("Uses 'unsafe-inline'");
    }
    if (policy.includes("'unsafe-eval'")) {
      weaknesses.push("Uses 'unsafe-eval'");
    }
    if (policy.includes('*')) {
      weaknesses.push('Uses wildcard sources');
    }
    if (!policy.includes('default-src')) {
      weaknesses.push('Missing default-src directive');
    }
    
    return {
      name: 'Policy Strength Analysis',
      passed: weaknesses.length === 0,
      details: {
        weaknesses,
        score: Math.max(0, 100 - (weaknesses.length * 25))
      }
    };
  }
  
  async testResourceLoading() {
    const page = await this.launchBrowser();
    const violations = [];
    
    page.on('console', msg => {
      if (msg.type() === 'error' && msg.text().includes('Content Security Policy')) {
        violations.push(msg.text());
      }
    });
    
    await page.goto(this.baseUrl);
    await page.waitForTimeout(3000);
    
    // Test specific resources
    const resources = await page.evaluate(() => {
      return {
        scripts: Array.from(document.scripts).map(s => s.src).filter(Boolean),
        styles: Array.from(document.styleSheets).map(s => s.href).filter(Boolean),
        images: Array.from(document.images).map(i => i.src).filter(Boolean)
      };
    });
    
    await page.close();
    
    return {
      name: 'Resource Loading Test',
      passed: violations.length === 0,
      details: {
        violations,
        loadedResources: resources
      }
    };
  }
  
  async testViolationReporting() {
    // Set up a mock report endpoint
    const reports = [];
    const reportServer = this.createReportServer(reports);
    
    // Test with intentional violation
    const response = await fetch(this.baseUrl, {
      headers: {
        'X-Test-CSP-Violation': 'true'
      }
    });
    
    // Wait for report
    await new Promise(resolve => setTimeout(resolve, 2000));
    reportServer.close();
    
    return {
      name: 'Violation Reporting Test',
      passed: reports.length > 0,
      details: {
        reportsReceived: reports.length,
        reportContent: reports[0]
      }
    };
  }
  
  createReportServer(reports) {
    const express = require('express');
    const app = express();
    
    app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
      reports.push(req.body);
      res.status(204).end();
    });
    
    return app.listen(3001);
  }
}

// Usage
const tester = new CSPTestFramework('https://example.com');
tester.runTestSuite().then(report => {
  console.log('CSP Test Report:', report);
});