// @Docs https://content-security-policy.com/ import { NextRequest, NextResponse } from 'next/server' export class CSP { private readonly request: any private readonly on: boolean private readonly request_headers?: Headers private readonly csp_with_nonce?: string private next_response?: NextResponse constructor (request: any, on?: boolean) { this.request = request this.on = on ?? true if (this.on) { const nonce = Buffer.from(crypto.randomUUID()).toString('base64') this.csp_with_nonce = CSP.contentSecurityPolicyHeaderValue(nonce) this.request_headers = new Headers(this.request.headers) this.request_headers.set('x-nonce', nonce) this.request_headers.set('x-xxx', '123') this.request_headers.set('Content-Security-Policy', this.csp_with_nonce) } } private static contentSecurityPolicyHeaderValue (nonce: string): string { //style-src 'self' 'nonce-${nonce}'; return ` default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data: avatars.githubusercontent.com *.googleusercontent.com; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests`.replace(/\s{2,}/g, ' ').trim() } next (middleware?: (request: NextRequest) => NextResponse): NextResponse { if (!this.on) { return middleware ? middleware(this.request) : NextResponse.next() } if (middleware) { const reqNext = new NextRequest(this.request, { headers: this.request_headers }) this.next_response = middleware(reqNext) } else { this.next_response = NextResponse.next({ request: { headers: this.request_headers }, }) } this.next_response.headers.set('Content-Security-Policy', this.csp_with_nonce as string) return this.next_response } }