mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-07-06 12:28:05 +02:00
Migrate CSP middleware to Next.js proxy convention.
Renames src/middleware.ts to src/proxy.ts per Next.js 16 deprecation guidance. CSP nonce behavior is unchanged; build no longer emits the middleware warning. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* 1. Document CSP remains hydration-safe for the Next.js runtime
|
||||
* 2. CSP is deterministic across repeated requests
|
||||
* 3. next.config.ts no longer owns a static CSP header
|
||||
* 4. Middleware does not break API/static routes (matcher exclusion)
|
||||
* 4. Proxy does not break API/static routes (matcher exclusion)
|
||||
* 5. Google Fonts domains are preserved in CSP
|
||||
* 6. Production CSP preserves required directives
|
||||
*/
|
||||
@@ -13,26 +13,26 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
import { middleware, config as middlewareConfig } from '@/middleware';
|
||||
import { proxy, config as proxyConfig } from '@/proxy';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Call middleware with a fake document request and return the response. */
|
||||
function callMiddleware(path = '/') {
|
||||
/** Call proxy with a fake document request and return the response. */
|
||||
function callProxy(path = '/') {
|
||||
const req = new NextRequest(`http://localhost${path}`, { method: 'GET' });
|
||||
return middleware(req);
|
||||
return proxy(req);
|
||||
}
|
||||
|
||||
/** Extract the CSP header string from a middleware response. */
|
||||
/** Extract the CSP header string from a proxy response. */
|
||||
function getCsp(path = '/'): string {
|
||||
return callMiddleware(path).headers.get('Content-Security-Policy') ?? '';
|
||||
return callProxy(path).headers.get('Content-Security-Policy') ?? '';
|
||||
}
|
||||
|
||||
/** Check whether the middleware matcher regex excludes a given path. */
|
||||
/** Check whether the proxy matcher regex excludes a given path. */
|
||||
function matcherExcludes(path: string): boolean {
|
||||
const pattern = middlewareConfig.matcher[0];
|
||||
const pattern = proxyConfig.matcher[0];
|
||||
// Next.js wraps the matcher in ^/<pattern>$ for path matching.
|
||||
// We replicate the essential check: the negative-lookahead prefix groups.
|
||||
const re = new RegExp(`^${pattern}$`);
|
||||
@@ -55,7 +55,7 @@ describe('hydration-safe CSP header', () => {
|
||||
expect(csp).toMatch(/script-src [^;]*'unsafe-inline'/);
|
||||
});
|
||||
|
||||
it('middleware still returns a CSP header for document requests', () => {
|
||||
it('proxy still returns a CSP header for document requests', () => {
|
||||
const csp = getCsp();
|
||||
expect(csp).toContain("default-src 'self'");
|
||||
expect(csp).toContain("script-src 'self'");
|
||||
@@ -117,10 +117,10 @@ describe('next.config.ts CSP removal', () => {
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 4. Middleware does not break API/static routes
|
||||
// 4. Proxy does not break API/static routes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('middleware matcher exclusions', () => {
|
||||
describe('proxy matcher exclusions', () => {
|
||||
it('excludes /api paths', () => {
|
||||
expect(matcherExcludes('/api/mesh/events')).toBe(true);
|
||||
});
|
||||
|
||||
@@ -13,19 +13,19 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
import { middleware, config as middlewareConfig } from '@/middleware';
|
||||
import { proxy, config as proxyConfig } from '@/proxy';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function callMiddleware(path = '/') {
|
||||
function callProxy(path = '/') {
|
||||
const req = new NextRequest(`http://localhost${path}`, { method: 'GET' });
|
||||
return middleware(req);
|
||||
return proxy(req);
|
||||
}
|
||||
|
||||
function getCsp(path = '/'): string {
|
||||
return callMiddleware(path).headers.get('Content-Security-Policy') ?? '';
|
||||
return callProxy(path).headers.get('Content-Security-Policy') ?? '';
|
||||
}
|
||||
|
||||
/** Extract a single CSP directive by name. */
|
||||
@@ -36,7 +36,7 @@ function getDirective(name: string, csp?: string): string {
|
||||
}
|
||||
|
||||
function matcherExcludes(path: string): boolean {
|
||||
const pattern = middlewareConfig.matcher[0];
|
||||
const pattern = proxyConfig.matcher[0];
|
||||
const re = new RegExp(`^${pattern}$`);
|
||||
return !re.test(path);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Phase 5F-A: CSP nonce plumbing middleware.
|
||||
* Phase 5F-A: CSP nonce plumbing proxy.
|
||||
*
|
||||
* Generates a per-request cryptographic nonce and emits a dynamic
|
||||
* Content-Security-Policy header for document (page) responses.
|
||||
@@ -36,7 +36,7 @@ function buildCsp(nonce: string, strictScripts = false): string {
|
||||
return directives.join('; ');
|
||||
}
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
export function proxy(request: NextRequest) {
|
||||
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
|
||||
|
||||
// Forward a nonce for staged CSP support. Strict script-src is opt-in until
|
||||
Reference in New Issue
Block a user