Strict-Dynamic Implementation

Strict-Dynamic Implementation

The 'strict-dynamic' keyword represents a paradigm shift in CSP, allowing scripts to load additional scripts dynamically while maintaining security:

// Strict-Dynamic CSP Implementation
class StrictDynamicCSP {
  constructor() {
    this.trustedScripts = new Set();
    this.config = {
      useFallback: true,
      fallbackSources: ["'self'", 'https:']
    };
  }
  
  generateStrictDynamicPolicy(nonce) {
    // Modern browsers supporting strict-dynamic
    const modernPolicy = {
      'default-src': ["'none'"],
      'script-src': [`'nonce-${nonce}'`, "'strict-dynamic'"],
      'style-src': ["'self'", `'nonce-${nonce}'`],
      'img-src': ["'self'", 'data:', 'https:'],
      'font-src': ["'self'"],
      'connect-src': ["'self'"],
      'frame-ancestors': ["'none'"],
      'base-uri': ["'none'"]
    };
    
    // Fallback for older browsers
    if (this.config.useFallback) {
      modernPolicy['script-src'].push(...this.config.fallbackSources, "'unsafe-inline'");
    }
    
    return this.formatPolicy(modernPolicy);
  }
  
  // Trusted Types integration for strict-dynamic
  implementTrustedTypes() {
    return `
      // Create Trusted Types policies
      if (window.trustedTypes && window.trustedTypes.createPolicy) {
        const scriptPolicy = window.trustedTypes.createPolicy('default', {
          createScript: (script) => {
            // Validate script content
            if (this.validateScriptContent(script)) {
              return script;
            }
            throw new Error('Script validation failed');
          },
          createScriptURL: (url) => {
            // Validate script URL
            if (this.validateScriptURL(url)) {
              return url;
            }
            throw new Error('Script URL validation failed');
          }
        });
        
        // Override dynamic script creation
        const originalCreateElement = document.createElement;
        document.createElement = function(tagName) {
          const element = originalCreateElement.call(document, tagName);
          
          if (tagName.toLowerCase() === 'script') {
            // Monitor script creation
            console.log('Dynamic script created');
          }
          
          return element;
        };
      }
    `;
  }
  
  // Loader for strict-dynamic environments
  createSecureScriptLoader() {
    return `
      class SecureScriptLoader {
        constructor(nonce) {
          this.nonce = nonce;
          this.loadedScripts = new Set();
        }
        
        async loadScript(src, options = {}) {
          // Prevent duplicate loading
          if (this.loadedScripts.has(src)) {
            return Promise.resolve();
          }
          
          return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            
            // Inherit nonce from parent (strict-dynamic behavior)
            script.nonce = this.nonce;
            
            // Set script attributes
            script.src = src;
            script.async = options.async !== false;
            script.defer = options.defer || false;
            
            // Set integrity if provided
            if (options.integrity) {
              script.integrity = options.integrity;
              script.crossOrigin = 'anonymous';
            }
            
            script.onload = () => {
              this.loadedScripts.add(src);
              resolve();
            };
            
            script.onerror = () => {
              reject(new Error(\`Failed to load script: \${src}\`));
            };
            
            // Append to document
            (document.head || document.documentElement).appendChild(script);
          });
        }
        
        async loadScripts(scripts) {
          return Promise.all(
            scripts.map(script => 
              typeof script === 'string' 
                ? this.loadScript(script) 
                : this.loadScript(script.src, script)
            )
          );
        }
      }
      
      // Initialize global loader
      window.scriptLoader = new SecureScriptLoader(document.currentScript.nonce);
    `;
  }
  
  formatPolicy(policy) {
    return Object.entries(policy)
      .map(([directive, sources]) => `${directive} ${sources.join(' ')}`)
      .join('; ');
  }
}

// React application with strict-dynamic
function StrictDynamicReactApp() {
  const [nonce] = useState(() => window.__CSP_NONCE__);
  
  useEffect(() => {
    // Dynamically load dependencies with strict-dynamic
    window.scriptLoader.loadScripts([
      {
        src: 'https://cdn.example.com/analytics.js',
        integrity: 'sha384-...',
        async: true
      },
      {
        src: '/js/feature-detection.js',
        defer: true
      }
    ]).then(() => {
      console.log('Dynamic scripts loaded securely');
    });
  }, []);
  
  return (
    <div>
      <script nonce={nonce} dangerouslySetInnerHTML={{
        __html: `
          // This inline script is allowed due to nonce
          console.log('Strict-dynamic allows this script');
          
          // And this script can load more scripts
          import('/js/dynamic-module.js').then(module => {
            console.log('Dynamic import successful');
          });
        `
      }} />
    </div>
  );
}