This guide covers the implementation, configuration, and management of Content Security Policy (CSP) in the WorkloadWizard application.
Content Security Policy is a security feature that helps prevent Cross-Site Scripting (XSS) attacks by controlling which resources can be loaded and executed by the browser. Our implementation supports both report-only and enforce modes, allowing for safe rollout and monitoring.
- CSP Builder (
src/lib/security/csp.ts) - Core CSP policy generation - Allowlist Configuration (
config/security/csp.allowlist.ts) - Environment-specific overrides - Middleware (
src/middleware.ts) - Header injection and nonce generation - Report Handler (
src/app/api/csp-report/route.ts) - Violation report collection - Admin Dashboard (
src/app/admin/csp/page.tsx) - Violation monitoring and analysis
Browser → CSP Policy → Violation → Report Handler → Convex DB → Admin Dashboard
| Variable | Description | Default | Values |
|---|---|---|---|
CSP_MODE |
CSP enforcement mode | report-only |
report-only, enforce |
| Environment | CSP Mode | Purpose |
|---|---|---|
| Development | report-only |
Safe testing with violation reporting |
| Preview | report-only |
Pre-production validation |
| Staging | report-only |
Production-like testing |
| Production | enforce |
Full security enforcement |
# Switch to report-only mode
npm run csp:mode:report
# Switch to enforce mode
npm run csp:mode:enforce
# Check current CSP configuration
npm run csp:check- Navigate to Vercel Dashboard
- Go to your project → Settings → Environment Variables
- Set
CSP_MODEtoenforcefor Production environment - Redeploy the application
-
Admin Dashboard: Navigate to
/admin/csp(requires sysadmin/developer role) -
Key Metrics:
- Total violations by time period
- Top violated directives
- Most blocked URIs
- Recent violation details
-
Filtering Options:
- Time range (1 hour, 24 hours, 7 days)
- Directive type
- Search by URI, source file, or user agent
- Export to CSV
- Edit Configuration: Modify
config/security/csp.allowlist.ts - Add Sources: Add domains to appropriate directives
- Environment-Specific: Use development/production/test overrides
- Redeploy: Changes take effect on next deployment
Example:
export const CSP_ALLOWLIST_OVERRIDES: Record<string, CSPAllowlistOverride> = {
scriptSrc: {
production: ['https://cdn.example.com', 'https://*.analytics.com'],
},
imgSrc: {
development: ['http://localhost:*'],
},
};default-src 'self'- Default source for all resource typesscript-src- JavaScript sources (with nonce support)style-src- CSS sources (with nonce support)img-src- Image sourcesconnect-src- XHR, fetch, WebSocket sourcesfont-src- Font sourcesframe-src- Frame/iframe sources
- Nonce-based Scripts: All inline scripts require a nonce
- Strict Dynamic: Allows nonce-based scripts to load additional scripts
- HTTPS Enforcement: Upgrades insecure requests in production
- Frame Ancestors: Prevents clickjacking attacks
The CSP policy automatically includes allowlists for:
- Convex: Database and real-time subscriptions
- WorkOS: Authentication and user management
- Statsig: Feature flags and experimentation
- Vercel: Analytics and speed insights
- PostHog: Product analytics
- Sanity: Content management
- Google Fonts: Typography
- Featurebase: Customer feedback widget
- Deploy with Report-Only: Set
CSP_MODE=report-only - Monitor Violations: Check
/admin/cspregularly - Analyze Patterns: Identify common violation sources
- Adjust Allowlists: Add legitimate sources to configuration
- Test Functionality: Ensure all features work correctly
- Final Review: Verify no critical violations remain
- Switch to Enforce: Set
CSP_MODE=enforce - Monitor Closely: Watch for any functionality issues
- Quick Rollback: Be ready to switch back to report-only
If issues arise after switching to enforce mode:
- Immediate: Set
CSP_MODE=report-onlyin Vercel - Redeploy: Trigger a new deployment
- Investigate: Use admin dashboard to identify issues
- Fix: Update allowlist configuration
- Retry: Switch back to enforce mode when ready
| Violation | Cause | Fix |
|---|---|---|
'unsafe-inline' |
Inline scripts without nonce | Add nonce to script tag |
'unsafe-eval' |
Dynamic code evaluation | Refactor to avoid eval() |
| External script blocked | Missing domain in allowlist | Add domain to scriptSrc |
| Violation | Cause | Fix |
|---|---|---|
| Inline styles blocked | CSS-in-JS without nonce | Add nonce to style tag |
| External stylesheet blocked | Missing domain | Add domain to styleSrc |
| Violation | Cause | Fix |
|---|---|---|
| External image blocked | Missing domain | Add domain to imgSrc |
| Data URI blocked | Missing data: |
Add data: to imgSrc |
| Violation | Cause | Fix |
|---|---|---|
| API call blocked | Missing domain | Add domain to connectSrc |
| WebSocket blocked | Missing protocol | Add wss: to connectSrc |
# Check CSP configuration
npm run csp:check
# Test in report-only mode
npm run csp:mode:report
npm run dev
# Test in enforce mode
npm run csp:mode:enforce
npm run devThe CI pipeline automatically validates:
- Correct header presence based on mode
- Policy structure and required directives
- Nonce generation and inclusion
- Reporting directives in report-only mode
- Browser DevTools: Check Console for CSP violations
- Network Tab: Verify all resources load correctly
- Admin Dashboard: Confirm violations are being reported
- Functionality: Test all application features
-
Scripts Not Loading
- Check if nonce is properly applied
- Verify script source is in allowlist
- Ensure no
unsafe-inlinedependencies
-
Styles Not Applying
- Check if nonce is properly applied
- Verify style source is in allowlist
- Ensure CSS-in-JS is nonce-compatible
-
Images Not Loading
- Check image source domains
- Verify
imgSrcallowlist includes required domains - Check for data URI requirements
-
API Calls Failing
- Check
connectSrcallowlist - Verify API domain is included
- Check for WebSocket requirements
- Check
Enable detailed CSP logging in development:
// In src/lib/security/csp.ts
const isDevelopment = env.NODE_ENV === 'development';
if (isDevelopment) {
console.log('CSP Policy:', cspPolicy);
console.log('CSP Mode:', cspMode);
}- Principle of Least Privilege: Only allow necessary sources
- Regular Review: Periodically audit allowlist entries
- Time-Boxed Exceptions: Add review dates for temporary allowances
- Documentation: Document why each allowlist entry exists
| Risk Level | Directive | Impact |
|---|---|---|
| High | script-src |
XSS, code injection |
| High | connect-src |
Data exfiltration |
| Medium | style-src |
CSS injection |
| Medium | img-src |
Information disclosure |
| Low | font-src |
Resource loading |
- Violation Trends: Watch for sudden spikes in violations
- New Sources: Monitor for unexpected domains
- Policy Changes: Track allowlist modifications
- Performance: Ensure CSP doesn't impact page load times
- Weekly: Review violation reports
- Monthly: Audit allowlist entries
- Quarterly: Review and update service integrations
- Annually: Full security review and policy update
- Remove unused allowlist entries
- Archive old violation reports
- Update service integrations as needed
- Review and update documentation