mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: send HSTS on TLS requests and drop the X-Powered-By header
This commit is contained in:
@@ -135,6 +135,19 @@ describe('ALLOWED_FRAME_ANCESTORS', () => {
|
||||
else process.env.ALLOWED_FRAME_ANCESTORS = original;
|
||||
});
|
||||
|
||||
it('sends HSTS only on a request that arrived over TLS', async () => {
|
||||
const plain = await load('/en/mail');
|
||||
expect(plain.headers.get('strict-transport-security')).toBeNull();
|
||||
const forwarded = await proxy(new NextRequest('http://localhost:3000/en/mail', {
|
||||
headers: { 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', 'sec-fetch-site': 'none', 'x-forwarded-proto': 'https' },
|
||||
}));
|
||||
expect(forwarded.headers.get('strict-transport-security')).toBe('max-age=63072000');
|
||||
const direct = await proxy(new NextRequest('https://webmail.example/en/mail', {
|
||||
headers: { 'sec-fetch-dest': 'document', 'sec-fetch-mode': 'navigate', 'sec-fetch-site': 'none' },
|
||||
}));
|
||||
expect(direct.headers.get('strict-transport-security')).toBe('max-age=63072000');
|
||||
});
|
||||
|
||||
it('lets a portal frame the mail UI', async () => {
|
||||
const res = await load('/en/mail');
|
||||
expect(res.headers.get('content-security-policy')).toContain('frame-ancestors https://portal.example');
|
||||
|
||||
@@ -49,6 +49,8 @@ if (isLite && (process.env.NEXT_PUBLIC_LOCALE_PREFIX ?? "never") !== "always") {
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: isLite ? "export" : "standalone",
|
||||
// No `X-Powered-By: Next.js` on every response.
|
||||
poweredByHeader: false,
|
||||
// Directory-style pages (out/en/mail/index.html) are what static hosts and
|
||||
// the shipped SPA-fallback rules expect; next/image has no optimizer here.
|
||||
...(isLite ? { trailingSlash: true, images: { unoptimized: true } } : {}),
|
||||
|
||||
@@ -398,6 +398,13 @@ export async function proxy(request: NextRequest) {
|
||||
|
||||
response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
|
||||
response.headers.set("X-XSS-Protection", "0");
|
||||
// Only on a request that arrived over TLS (directly or via the reverse
|
||||
// proxy's X-Forwarded-Proto): a plain-http dev or LAN install must not be
|
||||
// pinned to https by a header it cannot honour. No includeSubDomains, which
|
||||
// would speak for the operator's other hosts.
|
||||
if (request.nextUrl.protocol === "https:" || request.headers.get("x-forwarded-proto") === "https") {
|
||||
response.headers.set("Strict-Transport-Security", "max-age=63072000");
|
||||
}
|
||||
response.headers.set(
|
||||
"Permissions-Policy",
|
||||
"camera=(), microphone=(), geolocation=(), payment=()"
|
||||
|
||||
Reference in New Issue
Block a user