Transitioning from Report-Only to Enforcement

Transitioning from Report-Only to Enforcement

The transition from Report-Only to enforcement mode requires careful planning and gradual implementation:

// CSP Migration Manager
class CSPMigrationManager {
  constructor(config) {
    this.config = config;
    this.stages = [
      'initial-report-only',
      'refined-report-only',
      'partial-enforcement',
      'full-enforcement'
    ];
    this.currentStage = 0;
  }
  
  async evaluateReadiness() {
    const metrics = await this.gatherMetrics();
    const readiness = {
      violationRate: this.evaluateViolationRate(metrics),
      policyStability: this.evaluatePolicyStability(metrics),
      criticalPaths: this.evaluateCriticalPaths(metrics),
      userImpact: this.evaluateUserImpact(metrics)
    };
    
    const overallScore = this.calculateReadinessScore(readiness);
    return {
      ready: overallScore >= this.config.readinessThreshold,
      score: overallScore,
      details: readiness,
      recommendations: this.generateMigrationRecommendations(readiness)
    };
  }
  
  async migrateToNextStage() {
    const readiness = await this.evaluateReadiness();
    
    if (!readiness.ready) {
      throw new Error(`Not ready for migration: ${JSON.stringify(readiness.details)}`);
    }
    
    this.currentStage++;
    const nextStage = this.stages[this.currentStage];
    
    switch (nextStage) {
      case 'refined-report-only':
        return this.applyRefinedReportOnly();
      case 'partial-enforcement':
        return this.applyPartialEnforcement();
      case 'full-enforcement':
        return this.applyFullEnforcement();
      default:
        throw new Error(`Unknown stage: ${nextStage}`);
    }
  }
  
  applyRefinedReportOnly() {
    // Apply refined policy based on violation analysis
    return {
      headers: {
        'Content-Security-Policy-Report-Only': this.buildRefinedPolicy()
      },
      monitoring: {
        duration: '2 weeks',
        successCriteria: {
          maxViolationRate: 0.1, // per 1000 page views
          criticalPathViolations: 0
        }
      }
    };
  }
  
  applyPartialEnforcement() {
    // Enforce for logged-out users, report-only for logged-in
    return {
      middleware: (req, res, next) => {
        const policy = this.buildRefinedPolicy();
        
        if (req.user) {
          // Logged-in users get report-only
          res.setHeader('Content-Security-Policy-Report-Only', policy);
        } else {
          // Logged-out users get enforcement
          res.setHeader('Content-Security-Policy', policy);
        }
        
        next();
      },
      monitoring: {
        duration: '1 week',
        rollbackTriggers: {
          errorRateIncrease: 0.05,
          supportTicketIncrease: 0.1
        }
      }
    };
  }
  
  applyFullEnforcement() {
    // Full enforcement with gradual rollout
    return {
      rolloutStrategy: 'percentage',
      stages: [
        { percentage: 1, duration: '1 day' },
        { percentage: 5, duration: '2 days' },
        { percentage: 25, duration: '3 days' },
        { percentage: 50, duration: '3 days' },
        { percentage: 100, duration: 'permanent' }
      ],
      middleware: (req, res, next) => {
        const rolloutPercentage = this.getCurrentRolloutPercentage();
        const userHash = this.hashUser(req);
        
        if (userHash % 100 < rolloutPercentage) {
          res.setHeader('Content-Security-Policy', this.buildRefinedPolicy());
        } else {
          res.setHeader('Content-Security-Policy-Report-Only', this.buildRefinedPolicy());
        }
        
        next();
      }
    };
  }
}