diff --git a/src/securityHeaders.ts b/src/securityHeaders.ts index 8fe7a2421..5db2d3ace 100644 --- a/src/securityHeaders.ts +++ b/src/securityHeaders.ts @@ -1,5 +1,6 @@ +import { Headers } from '@effect/platform' import cspBuilder from 'content-security-policy-builder' -import { Duration, Record, String } from 'effect' +import { Duration } from 'effect' const crowdin = { scriptSrc: ['cdn.crowdin.com', "'unsafe-inline'", "'unsafe-eval'"], @@ -23,39 +24,34 @@ const imgSrc = [ const crossOriginEmbedderPolicy = 'credentialless' export const securityHeaders = (protocol: URL['protocol'], useCrowdinInContext: boolean) => - Record.filter( - { - 'Content-Security-Policy': cspBuilder({ - directives: { - 'script-src': useCrowdinInContext ? scriptSrc.concat(crowdin.scriptSrc) : scriptSrc, - 'img-src': useCrowdinInContext ? imgSrc.concat(crowdin.imgSrc) : imgSrc, - 'upgrade-insecure-requests': protocol === 'https:', - 'default-src': "'self'", - 'base-uri': "'self'", - 'font-src': ["'self'", 'https:', 'data:'], - 'form-action': "'self'", - 'frame-ancestors': "'self'", - 'frame-src': useCrowdinInContext ? crowdin.frameSrc : "'none'", - 'object-src': "'none'", - 'script-src-attr': useCrowdinInContext ? "'unsafe-inline'" : "'none'", - 'style-src': ["'self'", 'https:', "'unsafe-inline'"], - }, - }), - 'Cross-Origin-Embedder-Policy': useCrowdinInContext - ? crowdin.crossOriginEmbedderPolicy - : crossOriginEmbedderPolicy, - 'Cross-Origin-Opener-Policy': 'same-origin', - 'Cross-Origin-Resource-Policy': 'same-origin', - 'Origin-Agent-Cluster': '?1', - 'Referrer-Policy': 'no-referrer', - 'Strict-Transport-Security': - protocol === 'https:' ? `max-age=${Duration.toSeconds('180 days')}; includeSubDomains` : undefined, - 'X-Content-Type-Options': 'nosniff', - 'X-DNS-Prefetch-Control': 'off', - 'X-Download-Options': 'noopen', - 'X-Frame-Options': 'SAMEORIGIN', - 'X-Permitted-Cross-Domain-Policies': 'none', - 'X-XSS-Protection': '0', - }, - String.isString, - ) + Headers.fromInput({ + 'Content-Security-Policy': cspBuilder({ + directives: { + 'script-src': useCrowdinInContext ? scriptSrc.concat(crowdin.scriptSrc) : scriptSrc, + 'img-src': useCrowdinInContext ? imgSrc.concat(crowdin.imgSrc) : imgSrc, + 'upgrade-insecure-requests': protocol === 'https:', + 'default-src': "'self'", + 'base-uri': "'self'", + 'font-src': ["'self'", 'https:', 'data:'], + 'form-action': "'self'", + 'frame-ancestors': "'self'", + 'frame-src': useCrowdinInContext ? crowdin.frameSrc : "'none'", + 'object-src': "'none'", + 'script-src-attr': useCrowdinInContext ? "'unsafe-inline'" : "'none'", + 'style-src': ["'self'", 'https:', "'unsafe-inline'"], + }, + }), + 'Cross-Origin-Embedder-Policy': useCrowdinInContext ? crowdin.crossOriginEmbedderPolicy : crossOriginEmbedderPolicy, + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Resource-Policy': 'same-origin', + 'Origin-Agent-Cluster': '?1', + 'Referrer-Policy': 'no-referrer', + 'Strict-Transport-Security': + protocol === 'https:' ? `max-age=${Duration.toSeconds('180 days')}; includeSubDomains` : undefined, + 'X-Content-Type-Options': 'nosniff', + 'X-DNS-Prefetch-Control': 'off', + 'X-Download-Options': 'noopen', + 'X-Frame-Options': 'SAMEORIGIN', + 'X-Permitted-Cross-Domain-Policies': 'none', + 'X-XSS-Protection': '0', + })