chore/port-solidtime-audit-fixes #1

Merged
danijel merged 54 commits from chore/port-solidtime-audit-fixes into main 2026-07-12 21:13:09 +00:00
77 changed files with 1842 additions and 653 deletions
Showing only changes of commit 00fcc9d9a1 - Show all commits

View File

@@ -90,6 +90,34 @@ describe("withSecurityHeaders", () => {
expect(call[0]?.request?.headers?.get("x-nonce")).toBeTruthy(); expect(call[0]?.request?.headers?.get("x-nonce")).toBeTruthy();
}); });
it("sets the CSP on the forwarded request headers (Next reads the nonce from there)", () => {
withSecurityHeaders(makeRequest());
const call = vi.mocked(NextResponse.next).mock.calls[0] as [
{ request?: { headers?: Headers } } | undefined,
];
const requestCsp = call[0]?.request?.headers?.get(
"Content-Security-Policy",
);
expect(requestCsp).toBeTruthy();
expect(requestCsp).toBe(mock._store.get("Content-Security-Policy"));
});
it("request-header CSP carries the nonce in production mode", () => {
vi.stubEnv("NODE_ENV", "production");
withSecurityHeaders(makeRequest());
const call = vi.mocked(NextResponse.next).mock.calls[0] as [
{ request?: { headers?: Headers } } | undefined,
];
const requestCsp = call[0]?.request?.headers?.get(
"Content-Security-Policy",
);
const nonce = call[0]?.request?.headers?.get("x-nonce");
expect(requestCsp).toContain(`'nonce-${nonce}'`);
});
it("returns the NextResponse from NextResponse.next", () => { it("returns the NextResponse from NextResponse.next", () => {
const result = withSecurityHeaders(makeRequest()); const result = withSecurityHeaders(makeRequest());

View File

@@ -7,9 +7,16 @@ export function withSecurityHeaders(request: NextRequest): NextResponse {
const nonce = generateNonce(); const nonce = generateNonce();
const mode = process.env.NODE_ENV === "production" ? "prod" : "dev"; const mode = process.env.NODE_ENV === "production" ? "prod" : "dev";
const secHeaders = buildSecurityHeaders({ mode, nonce }); const secHeaders = buildSecurityHeaders({ mode, nonce });
const csp = secHeaders["Content-Security-Policy"];
const requestHeaders = new Headers(request.headers); const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-nonce", nonce); requestHeaders.set("x-nonce", nonce);
// Next.js only propagates a nonce to its own <script> tags when it can
// read it from the *request's* Content-Security-Policy header, so the CSP
// must be set on the forwarded request headers — not just the response.
if (csp) {
requestHeaders.set("Content-Security-Policy", csp);
}
const response = NextResponse.next({ const response = NextResponse.next({
request: { headers: requestHeaders }, request: { headers: requestHeaders },