Skip to content

Commit

Permalink
Remove duplication
Browse files Browse the repository at this point in the history
Refs #2116
  • Loading branch information
thewilkybarkid committed Dec 12, 2024
1 parent 21ee216 commit 2718f2d
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 66 deletions.
14 changes: 0 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"fetch-fp-ts": "^0.1.6",
"fp-ts": "^2.16.9",
"fp-ts-routing": "^0.6.0",
"helmet": "^7.1.0",
"html-entities": "^2.5.2",
"http-errors": "^2.0.0",
"http-proxy-middleware": "^3.0.3",
Expand Down
11 changes: 7 additions & 4 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import asyncHandler from 'express-async-handler'
import type { Json } from 'fp-ts/lib/Json.js'
import * as R from 'fp-ts/lib/Reader.js'
import { apply, pipe } from 'fp-ts/lib/function.js'
import helmet from 'helmet'
import { createProxyMiddleware } from 'http-proxy-middleware'
import type { ResponseEnded, StatusOpen } from 'hyper-ts'
import * as M from 'hyper-ts/lib/Middleware.js'
Expand All @@ -22,7 +21,7 @@ import { type LegacyEnv, legacyRoutes } from './legacy-routes/index.js'
import type { SupportedLocale } from './locales/index.js'
import { type NodemailerEnv, sendEmailWithNodemailer } from './nodemailer.js'
import { handleResponse } from './response.js'
import { helmetOptions } from './securityHeaders.js'
import { securityHeaders } from './securityHeaders.js'
import { maybeGetUser, type User } from './user.js'

export type ConfigEnv = Omit<
Expand Down Expand Up @@ -121,7 +120,6 @@ export const app = (config: ConfigEnv) => {
useCrowdinInContext: boolean
}) => {
return express()
.disable('x-powered-by')
.use((req, res, next) => {
req.logger = logger

Expand All @@ -131,7 +129,12 @@ export const app = (config: ConfigEnv) => {

next()
})
.use(helmet(helmetOptions(config.publicUrl.protocol, useCrowdinInContext)))
.use((req, res, next) => {
res.setHeaders(new Headers(securityHeaders(config.publicUrl.protocol, useCrowdinInContext)))
res.removeHeader('X-Powered-By')

next()
})
.use(asyncHandler(proxy))
.use(express.urlencoded({ extended: true }))
.use((req, res, next) => {
Expand Down
83 changes: 36 additions & 47 deletions src/securityHeaders.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import cspBuilder from 'content-security-policy-builder'
import type { HelmetOptions } from 'helmet'
import { Record, String } from 'effect'

const crowdin = {
scriptSrc: ['cdn.crowdin.com', "'unsafe-inline'", "'unsafe-eval'"],
Expand All @@ -22,50 +22,39 @@ const imgSrc = [
]
const crossOriginEmbedderPolicy = 'credentialless'

export const securityHeaders = (protocol: URL['protocol'], useCrowdinInContext: boolean) => ({
'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'"],
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=15552000; 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',
},
}),
'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=15552000; 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',
})

export const helmetOptions = (protocol: URL['protocol'], useCrowdinInContext: boolean) =>
({
contentSecurityPolicy: {
directives: {
'script-src': useCrowdinInContext ? scriptSrc.concat(crowdin.scriptSrc) : scriptSrc,
'img-src': useCrowdinInContext ? imgSrc.concat(crowdin.imgSrc) : imgSrc,
upgradeInsecureRequests: protocol === 'https:' ? [] : null,
'script-src-attr': useCrowdinInContext ? "'unsafe-inline'" : "'none'",
'frame-src': useCrowdinInContext ? crowdin.frameSrc : "'none'",
},
},
crossOriginEmbedderPolicy: {
policy: useCrowdinInContext ? crowdin.crossOriginEmbedderPolicy : crossOriginEmbedderPolicy,
},
strictTransportSecurity: protocol === 'https:',
}) satisfies Readonly<HelmetOptions>
String.isString,
)

0 comments on commit 2718f2d

Please sign in to comment.