feat(security): implement critical security headers and CSP allowlisting
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 53s
Build & Deploy / 🧪 QA (push) Successful in 1m52s
Build & Deploy / 🏗️ Build (push) Successful in 4m5s
Build & Deploy / 🚀 Deploy (push) Successful in 32s
Build & Deploy / 🧪 Smoke Test (push) Successful in 48s
Build & Deploy / ♿ WCAG (push) Successful in 2m5s
Build & Deploy / 🛡️ Quality Gates (push) Failing after 2m20s
Build & Deploy / ⚡ Lighthouse (push) Successful in 5m42s
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-02-22 17:11:15 +01:00
parent e1b441e8e7
commit 70984b9021
2 changed files with 54 additions and 1 deletions

2
next-env.d.ts vendored
View File

@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@@ -21,6 +21,59 @@ const nextConfig = {
},
},
output: 'standalone',
async headers() {
const umamiDomain = new URL(process.env.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me').origin;
const directusDomain = new URL(process.env.DIRECTUS_URL || 'https://cms.klz-cables.com').origin;
const imgproxyDomain = new URL(process.env.IMGPROXY_URL || 'https://img.infra.mintel.me').origin;
const glitchtipDomain = new URL(process.env.SENTRY_DSN ? new URL(process.env.SENTRY_DSN).origin : 'https://errors.infra.mintel.me').origin;
const cspHeader = `
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' ${umamiDomain};
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: blob: ${imgproxyDomain} ${directusDomain};
connect-src 'self' ${umamiDomain} ${glitchtipDomain} ${directusDomain};
frame-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`.replace(/\s{2,}/g, ' ').trim();
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: cspHeader,
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()',
},
],
},
];
},
async redirects() {
return [
// Blog redirects