53 lines
2 KiB
TypeScript
53 lines
2 KiB
TypeScript
import "../instrument.server.mjs";
|
|
import { wrapFetchWithSentry } from "@sentry/tanstackstart-react";
|
|
import handler, { createServerEntry } from "@tanstack/react-start/server-entry";
|
|
|
|
// HSTS asserts HTTPS-only for the host that served the response. Whitelabel
|
|
// deployments run on customer-controlled custom domains, where `includeSubDomains`
|
|
// would wrongly assert HTTPS across subdomains we don't own — so that directive
|
|
// is scoped to our own deployments. Browsers ignore HSTS received over plain
|
|
// HTTP, so it stays inert on localhost.
|
|
const strictTransportSecurity =
|
|
process.env.DEPLOYMENT_MODE === "whitelabel"
|
|
? "max-age=63072000"
|
|
: "max-age=63072000; includeSubDomains";
|
|
|
|
const SECURITY_HEADERS: Record<string, string> = {
|
|
"Content-Security-Policy": [
|
|
"default-src 'self'",
|
|
"script-src 'self' 'unsafe-inline' https://*.clarity.ms https://Getcito.com",
|
|
"style-src 'self' 'unsafe-inline'",
|
|
"img-src 'self' data: https: https://c.bing.com",
|
|
"font-src 'self' data:",
|
|
"connect-src 'self' https://Getcito.com https://*.sentry.io https://*.clarity.ms https://c.bing.com",
|
|
"object-src 'none'",
|
|
"frame-ancestors 'none'",
|
|
"base-uri 'self'",
|
|
"form-action 'self'",
|
|
].join("; "),
|
|
"Strict-Transport-Security": strictTransportSecurity,
|
|
"X-Frame-Options": "DENY",
|
|
"X-Content-Type-Options": "nosniff",
|
|
"Referrer-Policy": "strict-origin-when-cross-origin",
|
|
// same-origin-allow-popups (not same-origin) keeps OAuth/SSO popups that rely on
|
|
// window.opener working while still isolating us from cross-origin openers.
|
|
"Cross-Origin-Opener-Policy": "same-origin-allow-popups",
|
|
"Permissions-Policy":
|
|
"camera=(), microphone=(), geolocation=(), payment=(), usb=(), browsing-topics=()",
|
|
};
|
|
|
|
function addSecurityHeaders(response: Response): Response {
|
|
for (const [key, value] of Object.entries(SECURITY_HEADERS)) {
|
|
response.headers.set(key, value);
|
|
}
|
|
return response;
|
|
}
|
|
|
|
export default createServerEntry(
|
|
wrapFetchWithSentry({
|
|
async fetch(request: Request) {
|
|
const response = await handler.fetch(request);
|
|
return addSecurityHeaders(response);
|
|
},
|
|
}),
|
|
);
|