xiaohongshu
xiaohongshu
csp-config-generator
This skill should be used when the user requests to generate, create, or configure Content Security Policy (CSP) headers for Next.js applications to prevent XSS attacks and control resource loading. It analyzes the application to determine appropriate CSP directives and generates configuration vi...
Full skill instructions
To generate a strict Content Security Policy configuration for Next.js applications, follow these steps systematically.
Identify all resource types used in the application.
Use Grep to find external resource references:
Scripts:
- "<script.*src="
- "import.*from.*http"
- "next/script"
Stylesheets:
- "<link.*stylesheet"
- "@import.*url"
- "next/font"
Images:
- "<img.*src="
- "next/image"
- "background-image.*url"
Fonts:
- "@font-face"
- "next/font/google"
- "fonts.googleapis.com"
APIs and Connections:
- "fetch("
- "axios"
- "WebSocket"
Media:
- "<video"
- "<audio"
- "<iframe"
Collect all unique domains used:
Consult references/csp-directives.md for directive documentation.
Choose the appropriate CSP implementation strategy:
Most secure for Next.js apps with inline scripts:
For static content:
Least secure, only for migration:
Recommend Strategy A (nonce-based) for modern Next.js apps.
Build CSP directives based on discovered resources.
const cspDirectives = {
'default-src': ["'self'"],
'script-src': [
"'self'",
"'nonce-{NONCE}'", // Will be replaced dynamically
"'strict-dynamic'",
],
'style-src': [
"'self'",
"'nonce-{NONCE}'",
// Add specific domains if needed
],
'img-src': [
"'self'",
'data:',
'blob:',
// Add CDN domains
],
'font-src': [
"'self'",
'data:',
// Add font provider domains
],
'connect-src': [
"'self'",
// Add API domains
],
'frame-src': [
"'self'",
// Add allowed iframe sources
],
'object-src': ["'none'"],
'base-uri': ["'self'"],
'form-action': ["'self'"],
'frame-ancestors': ["'none'"],
'upgrade-insecure-requests': [],
}
For each resource type, add discovered domains:
// If using Vercel Analytics
cspDirectives['script-src'].push('https://va.vercel-scripts.com')
cspDirectives['connect-src'].push('https://vitals.vercel-insights.com')
// If using Google Fonts
cspDirectives['font-src'].push('https://fonts.gstatic.com')
cspDirectives['style-src'].push('https://fonts.googleapis.com')
// If using Supabase
cspDirectives['connect-src'].push('https://*.supabase.co')
// If using Cloudinary for images
cspDirectives['img-src'].push('https://res.cloudinary.com')
Create nonce generation and middleware.
// lib/csp/nonce.ts
import { headers } from 'next/headers'
export function generateNonce(): string {
return Buffer.from(crypto.randomUUID()).toString('base64')
}
export function getNonce(): string | undefined {
return headers().get('x-nonce') ?? undefined
}
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const cspHeader = generateCSPHeader(nonce)
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
requestHeaders.set('Content-Security-Policy', cspHeader)
const response = NextResponse.next({
request: {
headers: requestHeaders,
},
})
response.headers.set('Content-Security-Policy', cspHeader)
return response
}
function generateCSPHeader(nonce: string): string {
const csp = [
{ name: 'default-src', values: ["'self'"] },
{ name: 'script-src', values: ["'self'", `'nonce-${nonce}'`, "'strict-dynamic'"] },
{ name: 'style-src', values: ["'self'", `'nonce-${nonce}'`] },
{ name: 'img-src', values: ["'self'", 'data:', 'blob:'] },
{ name: 'font-src', values: ["'self'", 'data:'] },
{ name: 'connect-src', values: ["'self'"] },
{ name: 'frame-src', values: ["'self'"] },
{ name: 'object-src', values: ["'none'"] },
{ name: 'base-uri', values: ["'self'"] },
{ name: 'form-action', values: ["'self'"] },
{ name: 'frame-ancestors', values: ["'none'"] },
]
return csp
.map(({ name, values }) => `${name} ${values.join(' ')}`)
.join('; ')
}
export const config = {
matcher: [
{
source: '/((?!api|_next/static|_next/image|favicon.ico).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
}
// app/layout.tsx
import { getNonce } from '@/lib/csp/nonce'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
const nonce = getNonce()
return (
<html lang="en">
<head nonce={nonce}>
{/* Head content will use nonce */}
</head>
<body nonce={nonce}>{children}</body>
</html>
)
}
// Use Next.js Script component with nonce
import Script from 'next/script'
import { getNonce } from '@/lib/csp/nonce'
export function AnalyticsScript() {
const nonce = getNonce()
return (
<Script
src="https://analytics.example.com/script.js"
strategy="afterInteractive"
nonce={nonce}
/>
)
}
Alternative approach using headers in next.config.ts:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: generateCSP(),
},
],
},
]
},
}
function generateCSP(): string {
const csp = [
{ name: 'default-src', values: ["'self'"] },
{
name: 'script-src',
values: [
"'self'",
"'unsafe-eval'", // Required for React dev mode
"'unsafe-inline'", // Required for Next.js hydration
// Add specific domains
'https://va.vercel-scripts.com',
],
},
{
name: 'style-src',
values: [
"'self'",
"'unsafe-inline'", // Required for Next.js styles
'https://fonts.googleapis.com',
],
},
{
name: 'img-src',
values: ["'self'", 'data:', 'blob:', 'https:'],
},
{
name: 'font-src',
values: ["'self'", 'data:', 'https://fonts.gstatic.com'],
},
{
name: 'connect-src',
values: [
"'self'",
'https://vitals.vercel-insights.com',
'https://*.supabase.co',
],
},
{ name: 'frame-src', values: ["'self'"] },
{ name: 'object-src', values: ["'none'"] },
{ name: 'base-uri', values: ["'self'"] },
{ name: 'form-action', values: ["'self'"] },
{ name: 'frame-ancestors', values: ["'none'"] },
]
// Remove 'unsafe-inline' and 'unsafe-eval' in production
if (process.env.NODE_ENV === 'production') {
const scriptSrc = csp.find(({ name }) => name === 'script-src')
if (scriptSrc) {
scriptSrc.values = scriptSrc.values.filter(
(value) => value !== "'unsafe-eval'" && value !== "'unsafe-inline'"
)
}
}
return csp
.map(({ name, values }) => `${name} ${values.join(' ')}`)
.join('; ')
}
export default nextConfig
Create environment-specific CSP:
// lib/csp/config.ts
const isDevelopment = process.env.NODE_ENV === 'development'
export function getCSPDirectives() {
const baseDirectives = {
'default-src': ["'self'"],
'script-src': [
"'self'",
isDevelopment && "'unsafe-eval'", // React Fast Refresh
].filter(Boolean),
'style-src': [
"'self'",
isDevelopment && "'unsafe-inline'", // Development styles
].filter(Boolean),
// ... other directives
}
return baseDirectives
}
Create tests to verify CSP configuration:
// tests/csp.test.ts
import { describe, it, expect } from 'vitest'
describe('CSP Configuration', () => {
it('includes required directives', () => {
const csp = generateCSPHeader('test-nonce')
expect(csp).toContain("default-src 'self'")
expect(csp).toContain("object-src 'none'")
expect(csp).toContain('nonce-test-nonce')
})
it('does not include unsafe-inline in production', () => {
process.env.NODE_ENV = 'production'
const csp = generateCSPHeader('test-nonce')
expect(csp).not.toContain("'unsafe-inline'")
expect(csp).not.toContain("'unsafe-eval'")
})
it('allows development-specific directives', () => {
process.env.NODE_ENV = 'development'
const csp = generateCSPHeader('test-nonce')
// Development may include unsafe-eval for React Fast Refresh
expect(csp).toBeDefined()
})
})
Set up CSP violation reporting:
// app/api/csp-report/route.ts
import { NextResponse } from 'next/server'
export async function POST(request: Request) {
try {
const report = await request.json()
// Log violation
console.error('CSP Violation:', {
blockedURI: report['csp-report']['blocked-uri'],
violatedDirective: report['csp-report']['violated-directive'],
originalPolicy: report['csp-report']['original-policy'],
documentURI: report['csp-report']['document-uri'],
})
// Optionally send to monitoring service
// await sendToMonitoring(report)
return NextResponse.json({ received: true })
} catch (error) {
return NextResponse.json({ error: 'Invalid report' }, { status: 400 })
}
}
Add report-uri to CSP:
const csp = [
// ... other directives
{ name: 'report-uri', values: ['/api/csp-report'] },
{ name: 'report-to', values: ['csp-endpoint'] },
]
Create comprehensive CSP documentation using template from assets/csp-documentation-template.md:
# Content Security Policy Configuration
## Overview
This application uses a strict Content Security Policy to prevent XSS attacks.
## Implementation
CSP is implemented via [middleware/next.config] using [nonce-based/hash-based] strategy.
## Directives
- `default-src 'self'` - Only load resources from same origin
- `script-src 'self' 'nonce-{NONCE}'` - Scripts require nonce
- [etc.]
## Adding New Resources
To add a new external resource:
1. Identify the resource type
2. Add domain to appropriate directive
3. Test in development
4. Update documentation
## Troubleshooting
Common CSP violations and fixes...
Generate validation checklist:
# CSP Validation Checklist
- [ ] All external scripts have nonce or are in script-src
- [ ] All external styles are in style-src
- [ ] All API endpoints are in connect-src
- [ ] object-src is set to 'none'
- [ ] frame-ancestors is configured
- [ ] No 'unsafe-inline' in production script-src
- [ ] CSP violations are being reported
- [ ] Development mode works correctly
- [ ] Production build passes CSP checks
Throughout generation:
references/csp-directives.md for directive documentationreferences/csp-best-practices.md for security guidelinesassets/csp-config-template.tsassets/csp-documentation-template.mdGenerate files:
lib/csp/
nonce.ts
config.ts
middleware.ts (enhanced)
app/api/csp-report/
route.ts
docs/
csp-configuration.md
tests/
csp.test.ts
Before completing:
When finished:
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience