yo-next-auth/lib/CSP.ts

61 lines
1.9 KiB
TypeScript

// @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<unknown>): NextResponse<unknown> {
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
}
}