fceec19ded
- next.config.ts: nosniff, Referrer-Policy, X-Frame-Options SAMEORIGIN, CSP nur frame-ancestors 'self', Permissions-Policy (Kamera, Mikrofon, Standort, Zahlung, USB), COOP same-origin-allow-popups, poweredByHeader aus - API: X-Powered-By (Express) in configureHttp abgeschaltet - Tests: next-config.test.ts, http-setup.spec.ts - Sicherheitsprotokoll (Zeilen der Außenprüfung), Entwicklungsanleitung, CHANGELOG Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
76 lines
3.3 KiB
TypeScript
76 lines
3.3 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import type { NextConfig } from 'next';
|
|
import createNextIntlPlugin from 'next-intl/plugin';
|
|
|
|
// Bauzeit-Einbettung der Aenderungsliste (quick-260916-dcz): CHANGELOG.md aus
|
|
// dem Wurzelverzeichnis wird hier gelesen und als `env.TESSERA_CHANGELOG_MD`
|
|
// abgelegt. Next.js ersetzt `process.env.TESSERA_CHANGELOG_MD` fuer webpack UND
|
|
// Turbopack ueber denselben Define-Mechanismus. Der Text bleibt nur im
|
|
// Server-Bundle, weil ausschliesslich Server-Code `@/lib/changelog` importiert:
|
|
// die Server-Seite (changelog/page.tsx) und die 'use server'-Datei
|
|
// lib/release-notice-actions.ts (quick-260925-bow) — Importdisziplin, kein
|
|
// Client-Chunk.
|
|
|
|
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
|
|
|
|
function readChangelog(): string {
|
|
const file = path.resolve(__dirname, '../../CHANGELOG.md');
|
|
try {
|
|
return readFileSync(file, 'utf8');
|
|
} catch (error) {
|
|
const reason = error instanceof Error ? error.message : String(error);
|
|
throw new Error(
|
|
`CHANGELOG.md konnte nicht gelesen werden (${file}): ${reason}. ` +
|
|
'Die Datei muss im Wurzelverzeichnis liegen; im Docker-Bau braucht sie die Ausnahme ' +
|
|
'`!CHANGELOG.md` in .dockerignore und die Zeile `COPY CHANGELOG.md ./` in apps/web/Dockerfile.',
|
|
);
|
|
}
|
|
}
|
|
|
|
// Schutz-Kopfzeilen der Anwendung (quick-261009-p0m, aus dem ersten ZAP-Lauf). Sie gelten fuer
|
|
// alle Pfade und stehen hier, damit sie auch ohne vorgeschalteten Proxy gesendet werden
|
|
// (doppelt gesichert; setzt der Proxy dieselben Kopfzeilen, gewinnt keine Seite etwas oder
|
|
// verliert etwas). Absichtlich KEINE vollstaendige Content-Security-Policy -- sie braucht eine
|
|
// eigene Pruefung der Inline-Skripte von Next.js und bleibt im Sicherheitsprotokoll "offen".
|
|
// `frame-ancestors 'self'` ist nur die eine Richtlinie gegen das Einrahmen durch fremde Seiten
|
|
// (Gegenstueck zu X-Frame-Options fuer neuere Browser). Eingebettete FREMDE Seiten (XFrame-Kachel,
|
|
// eigene Module) sind nicht betroffen: diese Kopfzeilen schuetzen nur Tessera selbst.
|
|
// Permissions-Policy sperrt Kamera, Mikrofon, Standort, Zahlung und USB; die Zwischenablage
|
|
// (clipboard-write fuer "Link kopieren"), Vollbild und Benachrichtigungen bleiben unberuehrt.
|
|
// Cross-Origin-Opener-Policy `same-origin-allow-popups`: jeder Link in ein neues Fenster
|
|
// (Nextcloud-Anmeldung, Favoriten, ...) traegt noopener, nichts nutzt window.opener/postMessage.
|
|
const securityHeaders = [
|
|
{ key: 'X-Content-Type-Options', value: 'nosniff' },
|
|
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
|
|
{ key: 'X-Frame-Options', value: 'SAMEORIGIN' },
|
|
{ key: 'Content-Security-Policy', value: "frame-ancestors 'self'" },
|
|
{
|
|
key: 'Permissions-Policy',
|
|
value: 'camera=(), microphone=(), geolocation=(), payment=(), usb=()',
|
|
},
|
|
{ key: 'Cross-Origin-Opener-Policy', value: 'same-origin-allow-popups' },
|
|
];
|
|
|
|
const nextConfig: NextConfig = {
|
|
output: 'standalone' as const,
|
|
poweredByHeader: false,
|
|
env: {
|
|
TESSERA_CHANGELOG_MD: readChangelog(),
|
|
},
|
|
async headers() {
|
|
return [{ source: '/:path*', headers: securityHeaders }];
|
|
},
|
|
async rewrites() {
|
|
const apiUrl = process.env.API_INTERNAL_URL || 'http://api:3001';
|
|
return [
|
|
{
|
|
source: '/api-proxy/:path*',
|
|
destination: `${apiUrl}/:path*`,
|
|
},
|
|
];
|
|
},
|
|
};
|
|
|
|
export default withNextIntl(nextConfig);
|