initial commit on Federted servers
release gate / check (push) Failing after 8m35s

This commit is contained in:
Andrew S Erwin
2026-10-19 16:23:55 -05:00
commit b9721ff13f
217 changed files with 31534 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
import { NextResponse, type NextRequest } from 'next/server'
// Review CR-10: the production nginx template used to send a static
// `script-src 'self'` CSP. The App Router renders inline bootstrap scripts
// (flight-data pushes) that a nonce-less policy blocks: HTML arrives 200 but
// hydration never completes and the UI stays inert. The CSP is therefore
// owned here: every response gets a fresh nonce, and Next.js — which reads
// the CSP from the forwarded request headers — attaches it to the inline
// scripts it emits. nginx passes this header through and must NOT add its
// own script-src policy on top (browsers intersect multiple policies, which
// would strip the nonce again). This is a nonce strategy, not a blanket
// inline-script relaxation.
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
// Dev-mode HMR uses a websocket on the same host; production never needs it.
const connectSrc = process.env.NODE_ENV === 'development' ? `'self' ws:` : `'self'`
const csp = [
`default-src 'self'`,
`script-src 'self' 'nonce-${nonce}'`,
// Inline styles stay allowed (UI kit + sonner inject style attributes),
// matching the previous nginx baseline.
`style-src 'self' 'unsafe-inline'`,
// Remote previews: og-image/favicon render from arbitrary https origins.
`img-src 'self' data: blob: https:`,
`font-src 'self' data:`,
`connect-src ${connectSrc}`,
`frame-ancestors 'none'`,
`object-src 'none'`,
`base-uri 'self'`,
`form-action 'self'`,
].join('; ')
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
requestHeaders.set('Content-Security-Policy', csp)
const response = NextResponse.next({ request: { headers: requestHeaders } })
response.headers.set('Content-Security-Policy', csp)
return response
}
export const config = {
// Everything except immutable static assets (they cannot carry a per-request
// nonce anyway and contain no bootstrap scripts).
matcher: ['/((?!_next/static/|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)$).*)'],
}