This module is work in progress. Feedback is very welcome. 🙂 Please open an issue if you have suggestions, ideas, bug reports or questions.
Planned todos before the first release:
- Tests with real browsers (at least manually, but prefered automated, e.g. with Playwright)
- Update the documentation, add a section to give more information about CSP and CSP reports
Thank you for your feedback!
A Nuxt module for collecting, normalizing, and persisting Content Security Policy (CSP) reports.
- 📋 Register a POST endpoint for CSP reports
- 🔄 Support both legacy CSP and Report-To format reports
- ✅ Validate and normalize reports with Zod
- 💾 Persist reports via unstorage
- 📝 Full TypeScript support with proper type exports
Install the module to your Nuxt application:
npm install nuxt-csp-reportAdd it to your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['nuxt-csp-report'],
cspReport: {
// Module options
},
})- Type:
string - Default:
/api/csp-report - Description: Optional. Path for the CSP report endpoint.
- Type:
boolean - Default:
false - Description: Optional. Adds the
Reporting-Endpointsheader to your HTML responses, using'csp-endpoint'as the key andendpointfrom the configuration as the value. This header is needed if you want to usereport-to csp-endpointin your CSP configuration.
- Type:
'summary' | 'full' | false - Default:
'summary' - Description: Optional. Log reports to console on server.
'full'will print theNormalizedCspReportobject.
- Type: See fields below.
- Description: Optional. Sets up a storage using
unstorage, which is part of Nitro and Nuxt.
- Type:
BuiltinDriverOptions - Description: Defines the driver from
unstorage. You can use the same notation and drivers as in Nuxt:
- Type:
string - Default:
csp-report - Description: Optional. Key prefix for the stored reports.
The module is ready to go with the defaults.
In most use cases simple logs are sufficient. If you want to analyze CSP reports, you can use the storage option to persist the reports in a KV store.
The Content Security Policy is set through specific headers. You can handle that yourself with Nuxt/Nitro, but I highly recommend using nuxt-security. Here is a minimal example of how to use the two moduls in combination:
export default defineNuxtConfig({
modules: ['nuxt-security', 'nuxt-csp-report'],
security: {
headers: {
contentSecurityPolicy: {
'report-uri': '/api/csp-report',
// your CSP headers
},
},
},
})Depending on your use case you might want to access the CSP reports. You can do that with useStorage:
export default defineNuxtConfig({
modules: ['nuxt-csp-report'],
cspReport: {
storage: {
driver: {
name: 'redis',
options: {
// Your redis configuration
}
}
},
},
})import { type NormalizedCspReport } from 'nuxt-csp-report'
const storage = useStorage<NormalizedCspReport>('csp-report-storage'){
"csp-report": {
"document-uri": "https://example.com",
"blocked-uri": "https://evil.com",
"violated-directive": "script-src",
"effective-directive": "script-src",
"original-policy": "script-src 'self'",
"disposition": "enforce"
}
}[
{
"type": "csp-violation",
"body": {
"documentURI": "https://example.com",
"blockedURI": "https://evil.com",
"violatedDirective": "script-src",
"effectiveDirective": "script-src",
"originalPolicy": "script-src 'self'",
"disposition": "enforce"
}
}
]interface NormalizedCspReport {
ts: number
documentURL?: string
blockedURL?: string
directive?: string
sourceFile?: string
line?: number
column?: number
disposition?: 'enforce' | 'report'
raw: unknown
}Local development
# Install dependencies
pnpm install
# Generate type stubs
pnpm run dev:prepare
# Develop with the playground
pnpm run dev
# Build the playground
pnpm run dev:build
# Run ESLint
pnpm run lint
# Run Vitest
pnpm run test
pnpm run test:watch
# Build the module
pnpm run prepack
# Release new version
pnpm run release