Website: products, library, catalogs and their console page
/products (search), product pages with revisions, getting and staying on a revision, retracting; /products/:id/edit; /create and new revisions upload from the browser straight to the catalog with a token from the hub; /library; /catalogs (directory, registering with a once-shown key); /console/catalogs to approve or reject. Every page now sends a nonce-based CSP (proxy.ts), so product descriptions can't run scripts. buttonVariants now merges classes, so outline links get their border. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b4f6fce9df
commit
51d5bb5ff3
24 files changed
+1064
-5
No files matched your search
@@ -0,0 +1,41 @@
|
||||
// A Content Security Policy for every page: scripts only from this site with the request's nonce (Next.js adds it to
|
||||
// its own scripts), so HTML that slips into a page, such as a product's description, can't run anything. Uploads go
|
||||
// from the browser straight to catalogs, so connect-src allows any https address (and local ones, for development).
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
|
||||
export function proxy(request: NextRequest) {
|
||||
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
|
||||
const dev = process.env.NODE_ENV === "development";
|
||||
const policy = [
|
||||
"default-src 'self'",
|
||||
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'${dev ? " 'unsafe-eval'" : ""}`,
|
||||
// Descriptions keep their style attributes (the API's allowlist limits them to colors, sizes and alignment).
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' https: data: blob:",
|
||||
"font-src 'self'",
|
||||
`connect-src 'self' https: http://localhost:* http://127.0.0.1:*${dev ? " ws:" : ""}`,
|
||||
"object-src 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
].join("; ");
|
||||
|
||||
const headers = new Headers(request.headers);
|
||||
headers.set("x-nonce", nonce);
|
||||
headers.set("Content-Security-Policy", policy);
|
||||
const response = NextResponse.next({ request: { headers } });
|
||||
response.headers.set("Content-Security-Policy", policy);
|
||||
return response;
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
{
|
||||
source: "/((?!api|_next/static|_next/image|icon.svg|favicon.ico).*)",
|
||||
missing: [
|
||||
{ type: "header", key: "next-router-prefetch" },
|
||||
{ type: "header", key: "purpose", value: "prefetch" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in new issue
Block a user