Fix security and correctness issues found in full-codebase audit
- Restrict meetingPassword field so it isn't leaked via the public REST/GraphQL API - Fix booking slot computation to use Europe/Berlin time instead of the server's UTC clock - Reset email verification when a customer changes their email address - Fix ICS calendar injection via unescaped carriage returns in event descriptions - Bind WebRTC meeting kicks to a persistent client id so they survive a rejoin - Move the meeting signaling token out of the WebSocket query string into the first message - Prevent duplicate signaling connections from leaking when the same participant reconnects - Use the rightmost X-Forwarded-For entry for rate limiting (single trusted reverse proxy) - Use constant-time comparison for the cron endpoint's bearer token - Remove SVG from allowed Media upload types (stored-XSS risk, unused anyway) - Add baseline security headers and a CSP to next.config.ts - Fix CI/CD script injection via unsanitized git ref names in release.yml
This commit is contained in:
@@ -6,6 +6,47 @@ import { fileURLToPath } from "url";
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const dirname = path.dirname(__filename);
|
||||
|
||||
// Applied to every route, including /admin and /api — none of these
|
||||
// constrain what the page can load/render, so they can't break the Payload
|
||||
// admin bundle. Camera/microphone/display-capture are allowed for the
|
||||
// site's own origin because the WebRTC call pages (app/(call)/**) need them.
|
||||
const baseSecurityHeaders = [
|
||||
{ key: "X-Content-Type-Options", value: "nosniff" },
|
||||
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
|
||||
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
||||
{ key: "Permissions-Policy", value: "camera=(self), microphone=(self), display-capture=(self), geolocation=(), payment=()" },
|
||||
// Browsers only ever honor this over an actual HTTPS connection, so it's
|
||||
// harmless to send in local http:// dev too.
|
||||
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
|
||||
];
|
||||
|
||||
// Deliberately NOT applied to /admin or /api: Payload's admin bundle isn't
|
||||
// audited here for CSP compatibility, and API routes serve data, not HTML,
|
||||
// so a content policy doesn't apply to them anyway. connect-src stays broad
|
||||
// (rather than 'self') because the WebRTC call pages connect to whatever
|
||||
// STUN/TURN server is configured via env vars at runtime (see
|
||||
// lib/meeting/iceServers.ts) — that host isn't known here. style-src needs
|
||||
// 'unsafe-inline' because framer-motion drives its animations through
|
||||
// inline style attributes. script-src allows 'unsafe-eval' only in dev,
|
||||
// matching Next's own documented CSP guidance — React uses eval there for
|
||||
// server-error-stack reconstruction; neither React nor Next use it in a
|
||||
// production build.
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const frontendCSP = [
|
||||
"default-src 'self'",
|
||||
`script-src 'self'${isDev ? " 'unsafe-eval'" : ""}`,
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"img-src 'self' data: blob:",
|
||||
"font-src 'self' data:",
|
||||
"connect-src 'self' https: wss: stun: turn: turns:",
|
||||
"media-src 'self' blob:",
|
||||
"frame-ancestors 'self'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
"object-src 'none'",
|
||||
"upgrade-insecure-requests",
|
||||
].join("; ");
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
// NOT "standalone": this project uses a custom server (server.ts) for the
|
||||
// WebRTC signaling WebSocket, and standalone output can't be combined with
|
||||
@@ -25,6 +66,12 @@ const nextConfig: NextConfig = {
|
||||
turbopack: {
|
||||
root: path.resolve(dirname),
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{ source: "/:path*", headers: baseSecurityHeaders },
|
||||
{ source: "/((?!admin|api).*)", headers: [{ key: "Content-Security-Policy", value: frontendCSP }] },
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default withPayload(nextConfig, { devBundleServerPackages: false });
|
||||
|
||||
Reference in New Issue
Block a user