| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- "use strict"
- Object.defineProperties(exports, {__esModule: {value: true}, [Symbol.toStringTag]: {value: "Module"}})
- const dangerouslyDisableDefaultSrc = Symbol("dangerouslyDisableDefaultSrc")
- const SHOULD_BE_QUOTED = new Set(["none", "self", "strict-dynamic", "report-sample", "inline-speculation-rules", "unsafe-inline", "unsafe-eval", "unsafe-hashes", "wasm-unsafe-eval"])
- const getDefaultDirectives = () => ({
- "default-src": ["'self'"],
- "base-uri": ["'self'"],
- "font-src": ["'self'", "https:", "data:"],
- "form-action": ["'self'"],
- "frame-ancestors": ["'self'"],
- "img-src": ["'self'", "data:"],
- "object-src": ["'none'"],
- "script-src": ["'self'"],
- "script-src-attr": ["'none'"],
- "style-src": ["'self'", "https:", "'unsafe-inline'"],
- "upgrade-insecure-requests": []
- })
- const dashify = str => str.replace(/[A-Z]/g, capitalLetter => "-" + capitalLetter.toLowerCase())
- const assertDirectiveValueIsValid = (directiveName, directiveValue) => {
- if (/;|,/.test(directiveValue)) {
- throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`)
- }
- }
- const assertDirectiveValueEntryIsValid = (directiveName, directiveValueEntry) => {
- if (SHOULD_BE_QUOTED.has(directiveValueEntry) || directiveValueEntry.startsWith("nonce-") || directiveValueEntry.startsWith("sha256-") || directiveValueEntry.startsWith("sha384-") || directiveValueEntry.startsWith("sha512-")) {
- throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}. ${JSON.stringify(directiveValueEntry)} should be quoted`)
- }
- }
- function normalizeDirectives(options) {
- const defaultDirectives = getDefaultDirectives()
- const {useDefaults = true, directives: rawDirectives = defaultDirectives} = options
- const result = new Map()
- const directiveNamesSeen = new Set()
- const directivesExplicitlyDisabled = new Set()
- for (const rawDirectiveName in rawDirectives) {
- if (!Object.hasOwn(rawDirectives, rawDirectiveName)) {
- continue
- }
- if (rawDirectiveName.length === 0 || /[^a-zA-Z0-9-]/.test(rawDirectiveName)) {
- throw new Error(`Content-Security-Policy received an invalid directive name ${JSON.stringify(rawDirectiveName)}`)
- }
- const directiveName = dashify(rawDirectiveName)
- if (directiveNamesSeen.has(directiveName)) {
- throw new Error(`Content-Security-Policy received a duplicate directive ${JSON.stringify(directiveName)}`)
- }
- directiveNamesSeen.add(directiveName)
- const rawDirectiveValue = rawDirectives[rawDirectiveName]
- let directiveValue
- if (rawDirectiveValue === null) {
- if (directiveName === "default-src") {
- throw new Error("Content-Security-Policy needs a default-src but it was set to `null`. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.")
- }
- directivesExplicitlyDisabled.add(directiveName)
- continue
- } else if (typeof rawDirectiveValue === "string") {
- directiveValue = [rawDirectiveValue]
- } else if (!rawDirectiveValue) {
- throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`)
- } else if (rawDirectiveValue === dangerouslyDisableDefaultSrc) {
- if (directiveName === "default-src") {
- directivesExplicitlyDisabled.add("default-src")
- continue
- } else {
- throw new Error(`Content-Security-Policy: tried to disable ${JSON.stringify(directiveName)} as if it were default-src; simply omit the key`)
- }
- } else {
- directiveValue = rawDirectiveValue
- }
- for (const element of directiveValue) {
- if (typeof element !== "string") continue
- assertDirectiveValueIsValid(directiveName, element)
- assertDirectiveValueEntryIsValid(directiveName, element)
- }
- result.set(directiveName, directiveValue)
- }
- if (useDefaults) {
- Object.entries(defaultDirectives).forEach(([defaultDirectiveName, defaultDirectiveValue]) => {
- if (!result.has(defaultDirectiveName) && !directivesExplicitlyDisabled.has(defaultDirectiveName)) {
- result.set(defaultDirectiveName, defaultDirectiveValue)
- }
- })
- }
- if (!result.size) {
- throw new Error("Content-Security-Policy has no directives. Either set some or disable the header")
- }
- if (!result.has("default-src") && !directivesExplicitlyDisabled.has("default-src")) {
- throw new Error("Content-Security-Policy needs a default-src but none was provided. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.")
- }
- return result
- }
- function getHeaderValue(req, res, normalizedDirectives) {
- const result = []
- for (const [directiveName, rawDirectiveValue] of normalizedDirectives) {
- let directiveValue = ""
- for (const element of rawDirectiveValue) {
- if (typeof element === "function") {
- const newElement = element(req, res)
- assertDirectiveValueEntryIsValid(directiveName, newElement)
- directiveValue += " " + newElement
- } else {
- directiveValue += " " + element
- }
- }
- if (directiveValue) {
- assertDirectiveValueIsValid(directiveName, directiveValue)
- result.push(`${directiveName}${directiveValue}`)
- } else {
- result.push(directiveName)
- }
- }
- return result.join(";")
- }
- const contentSecurityPolicy = function contentSecurityPolicy(options = {}) {
- const headerName = options.reportOnly ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy"
- const normalizedDirectives = normalizeDirectives(options)
- return function contentSecurityPolicyMiddleware(req, res, next) {
- const result = getHeaderValue(req, res, normalizedDirectives)
- if (result instanceof Error) {
- next(result)
- } else {
- res.setHeader(headerName, result)
- next()
- }
- }
- }
- contentSecurityPolicy.getDefaultDirectives = getDefaultDirectives
- contentSecurityPolicy.dangerouslyDisableDefaultSrc = dangerouslyDisableDefaultSrc
- const ALLOWED_POLICIES$2 = new Set(["require-corp", "credentialless", "unsafe-none"])
- function getHeaderValueFromOptions$6({policy = "require-corp"}) {
- if (ALLOWED_POLICIES$2.has(policy)) {
- return policy
- } else {
- throw new Error(`Cross-Origin-Embedder-Policy does not support the ${JSON.stringify(policy)} policy`)
- }
- }
- function crossOriginEmbedderPolicy(options = {}) {
- const headerValue = getHeaderValueFromOptions$6(options)
- return function crossOriginEmbedderPolicyMiddleware(_req, res, next) {
- res.setHeader("Cross-Origin-Embedder-Policy", headerValue)
- next()
- }
- }
- const ALLOWED_POLICIES$1 = new Set(["same-origin", "same-origin-allow-popups", "unsafe-none"])
- function getHeaderValueFromOptions$5({policy = "same-origin"}) {
- if (ALLOWED_POLICIES$1.has(policy)) {
- return policy
- } else {
- throw new Error(`Cross-Origin-Opener-Policy does not support the ${JSON.stringify(policy)} policy`)
- }
- }
- function crossOriginOpenerPolicy(options = {}) {
- const headerValue = getHeaderValueFromOptions$5(options)
- return function crossOriginOpenerPolicyMiddleware(_req, res, next) {
- res.setHeader("Cross-Origin-Opener-Policy", headerValue)
- next()
- }
- }
- const ALLOWED_POLICIES = new Set(["same-origin", "same-site", "cross-origin"])
- function getHeaderValueFromOptions$4({policy = "same-origin"}) {
- if (ALLOWED_POLICIES.has(policy)) {
- return policy
- } else {
- throw new Error(`Cross-Origin-Resource-Policy does not support the ${JSON.stringify(policy)} policy`)
- }
- }
- function crossOriginResourcePolicy(options = {}) {
- const headerValue = getHeaderValueFromOptions$4(options)
- return function crossOriginResourcePolicyMiddleware(_req, res, next) {
- res.setHeader("Cross-Origin-Resource-Policy", headerValue)
- next()
- }
- }
- function originAgentCluster() {
- return function originAgentClusterMiddleware(_req, res, next) {
- res.setHeader("Origin-Agent-Cluster", "?1")
- next()
- }
- }
- const ALLOWED_TOKENS = new Set(["no-referrer", "no-referrer-when-downgrade", "same-origin", "origin", "strict-origin", "origin-when-cross-origin", "strict-origin-when-cross-origin", "unsafe-url", ""])
- function getHeaderValueFromOptions$3({policy = ["no-referrer"]}) {
- const tokens = typeof policy === "string" ? [policy] : policy
- if (tokens.length === 0) {
- throw new Error("Referrer-Policy received no policy tokens")
- }
- const tokensSeen = new Set()
- tokens.forEach(token => {
- if (!ALLOWED_TOKENS.has(token)) {
- throw new Error(`Referrer-Policy received an unexpected policy token ${JSON.stringify(token)}`)
- } else if (tokensSeen.has(token)) {
- throw new Error(`Referrer-Policy received a duplicate policy token ${JSON.stringify(token)}`)
- }
- tokensSeen.add(token)
- })
- return tokens.join(",")
- }
- function referrerPolicy(options = {}) {
- const headerValue = getHeaderValueFromOptions$3(options)
- return function referrerPolicyMiddleware(_req, res, next) {
- res.setHeader("Referrer-Policy", headerValue)
- next()
- }
- }
- const DEFAULT_MAX_AGE = 365 * 24 * 60 * 60
- function parseMaxAge(value = DEFAULT_MAX_AGE) {
- if (value >= 0 && Number.isFinite(value)) {
- return Math.floor(value)
- } else {
- throw new Error(`Strict-Transport-Security: ${JSON.stringify(value)} is not a valid value for maxAge. Please choose a positive integer.`)
- }
- }
- function getHeaderValueFromOptions$2(options) {
- if ("maxage" in options) {
- throw new Error("Strict-Transport-Security received an unsupported property, `maxage`. Did you mean to pass `maxAge`?")
- }
- if ("includeSubdomains" in options) {
- throw new Error('Strict-Transport-Security middleware should use `includeSubDomains` instead of `includeSubdomains`. (The correct one has an uppercase "D".)')
- }
- const directives = [`max-age=${parseMaxAge(options.maxAge)}`]
- if (options.includeSubDomains === undefined || options.includeSubDomains) {
- directives.push("includeSubDomains")
- }
- if (options.preload) {
- directives.push("preload")
- }
- return directives.join("; ")
- }
- function strictTransportSecurity(options = {}) {
- const headerValue = getHeaderValueFromOptions$2(options)
- return function strictTransportSecurityMiddleware(_req, res, next) {
- res.setHeader("Strict-Transport-Security", headerValue)
- next()
- }
- }
- function xContentTypeOptions() {
- return function xContentTypeOptionsMiddleware(_req, res, next) {
- res.setHeader("X-Content-Type-Options", "nosniff")
- next()
- }
- }
- function xDnsPrefetchControl(options = {}) {
- const headerValue = options.allow ? "on" : "off"
- return function xDnsPrefetchControlMiddleware(_req, res, next) {
- res.setHeader("X-DNS-Prefetch-Control", headerValue)
- next()
- }
- }
- function xDownloadOptions() {
- return function xDownloadOptionsMiddleware(_req, res, next) {
- res.setHeader("X-Download-Options", "noopen")
- next()
- }
- }
- function getHeaderValueFromOptions$1({action = "sameorigin"}) {
- const normalizedAction = typeof action === "string" ? action.toUpperCase() : action
- switch (normalizedAction) {
- case "SAME-ORIGIN":
- return "SAMEORIGIN"
- case "DENY":
- case "SAMEORIGIN":
- return normalizedAction
- default:
- throw new Error(`X-Frame-Options received an invalid action ${JSON.stringify(action)}`)
- }
- }
- function xFrameOptions(options = {}) {
- const headerValue = getHeaderValueFromOptions$1(options)
- return function xFrameOptionsMiddleware(_req, res, next) {
- res.setHeader("X-Frame-Options", headerValue)
- next()
- }
- }
- const ALLOWED_PERMITTED_POLICIES = new Set(["none", "master-only", "by-content-type", "all"])
- function getHeaderValueFromOptions({permittedPolicies = "none"}) {
- if (ALLOWED_PERMITTED_POLICIES.has(permittedPolicies)) {
- return permittedPolicies
- } else {
- throw new Error(`X-Permitted-Cross-Domain-Policies does not support ${JSON.stringify(permittedPolicies)}`)
- }
- }
- function xPermittedCrossDomainPolicies(options = {}) {
- const headerValue = getHeaderValueFromOptions(options)
- return function xPermittedCrossDomainPoliciesMiddleware(_req, res, next) {
- res.setHeader("X-Permitted-Cross-Domain-Policies", headerValue)
- next()
- }
- }
- function xPoweredBy() {
- return function xPoweredByMiddleware(_req, res, next) {
- res.removeHeader("X-Powered-By")
- next()
- }
- }
- function xXssProtection() {
- return function xXssProtectionMiddleware(_req, res, next) {
- res.setHeader("X-XSS-Protection", "0")
- next()
- }
- }
- function getMiddlewareFunctionsFromOptions(options) {
- const result = []
- switch (options.contentSecurityPolicy) {
- case undefined:
- case true:
- result.push(contentSecurityPolicy())
- break
- case false:
- break
- default:
- result.push(contentSecurityPolicy(options.contentSecurityPolicy))
- break
- }
- switch (options.crossOriginEmbedderPolicy) {
- case undefined:
- case false:
- break
- case true:
- result.push(crossOriginEmbedderPolicy())
- break
- default:
- result.push(crossOriginEmbedderPolicy(options.crossOriginEmbedderPolicy))
- break
- }
- switch (options.crossOriginOpenerPolicy) {
- case undefined:
- case true:
- result.push(crossOriginOpenerPolicy())
- break
- case false:
- break
- default:
- result.push(crossOriginOpenerPolicy(options.crossOriginOpenerPolicy))
- break
- }
- switch (options.crossOriginResourcePolicy) {
- case undefined:
- case true:
- result.push(crossOriginResourcePolicy())
- break
- case false:
- break
- default:
- result.push(crossOriginResourcePolicy(options.crossOriginResourcePolicy))
- break
- }
- switch (options.originAgentCluster) {
- case undefined:
- case true:
- result.push(originAgentCluster())
- break
- case false:
- break
- default:
- console.warn("Origin-Agent-Cluster does not take options. Remove the property to silence this warning.")
- result.push(originAgentCluster())
- break
- }
- switch (options.referrerPolicy) {
- case undefined:
- case true:
- result.push(referrerPolicy())
- break
- case false:
- break
- default:
- result.push(referrerPolicy(options.referrerPolicy))
- break
- }
- if ("strictTransportSecurity" in options && "hsts" in options) {
- throw new Error("Strict-Transport-Security option was specified twice. Remove `hsts` to silence this warning.")
- }
- const strictTransportSecurityOption = options.strictTransportSecurity ?? options.hsts
- switch (strictTransportSecurityOption) {
- case undefined:
- case true:
- result.push(strictTransportSecurity())
- break
- case false:
- break
- default:
- result.push(strictTransportSecurity(strictTransportSecurityOption))
- break
- }
- if ("xContentTypeOptions" in options && "noSniff" in options) {
- throw new Error("X-Content-Type-Options option was specified twice. Remove `noSniff` to silence this warning.")
- }
- const xContentTypeOptionsOption = options.xContentTypeOptions ?? options.noSniff
- switch (xContentTypeOptionsOption) {
- case undefined:
- case true:
- result.push(xContentTypeOptions())
- break
- case false:
- break
- default:
- console.warn("X-Content-Type-Options does not take options. Remove the property to silence this warning.")
- result.push(xContentTypeOptions())
- break
- }
- if ("xDnsPrefetchControl" in options && "dnsPrefetchControl" in options) {
- throw new Error("X-DNS-Prefetch-Control option was specified twice. Remove `dnsPrefetchControl` to silence this warning.")
- }
- const xDnsPrefetchControlOption = options.xDnsPrefetchControl ?? options.dnsPrefetchControl
- switch (xDnsPrefetchControlOption) {
- case undefined:
- case true:
- result.push(xDnsPrefetchControl())
- break
- case false:
- break
- default:
- result.push(xDnsPrefetchControl(xDnsPrefetchControlOption))
- break
- }
- if ("xDownloadOptions" in options && "ieNoOpen" in options) {
- throw new Error("X-Download-Options option was specified twice. Remove `ieNoOpen` to silence this warning.")
- }
- const xDownloadOptionsOption = options.xDownloadOptions ?? options.ieNoOpen
- switch (xDownloadOptionsOption) {
- case undefined:
- case true:
- result.push(xDownloadOptions())
- break
- case false:
- break
- default:
- console.warn("X-Download-Options does not take options. Remove the property to silence this warning.")
- result.push(xDownloadOptions())
- break
- }
- if ("xFrameOptions" in options && "frameguard" in options) {
- throw new Error("X-Frame-Options option was specified twice. Remove `frameguard` to silence this warning.")
- }
- const xFrameOptionsOption = options.xFrameOptions ?? options.frameguard
- switch (xFrameOptionsOption) {
- case undefined:
- case true:
- result.push(xFrameOptions())
- break
- case false:
- break
- default:
- result.push(xFrameOptions(xFrameOptionsOption))
- break
- }
- if ("xPermittedCrossDomainPolicies" in options && "permittedCrossDomainPolicies" in options) {
- throw new Error("X-Permitted-Cross-Domain-Policies option was specified twice. Remove `permittedCrossDomainPolicies` to silence this warning.")
- }
- const xPermittedCrossDomainPoliciesOption = options.xPermittedCrossDomainPolicies ?? options.permittedCrossDomainPolicies
- switch (xPermittedCrossDomainPoliciesOption) {
- case undefined:
- case true:
- result.push(xPermittedCrossDomainPolicies())
- break
- case false:
- break
- default:
- result.push(xPermittedCrossDomainPolicies(xPermittedCrossDomainPoliciesOption))
- break
- }
- if ("xPoweredBy" in options && "hidePoweredBy" in options) {
- throw new Error("X-Powered-By option was specified twice. Remove `hidePoweredBy` to silence this warning.")
- }
- const xPoweredByOption = options.xPoweredBy ?? options.hidePoweredBy
- switch (xPoweredByOption) {
- case undefined:
- case true:
- result.push(xPoweredBy())
- break
- case false:
- break
- default:
- console.warn("X-Powered-By does not take options. Remove the property to silence this warning.")
- result.push(xPoweredBy())
- break
- }
- if ("xXssProtection" in options && "xssFilter" in options) {
- throw new Error("X-XSS-Protection option was specified twice. Remove `xssFilter` to silence this warning.")
- }
- const xXssProtectionOption = options.xXssProtection ?? options.xssFilter
- switch (xXssProtectionOption) {
- case undefined:
- case true:
- result.push(xXssProtection())
- break
- case false:
- break
- default:
- console.warn("X-XSS-Protection does not take options. Remove the property to silence this warning.")
- result.push(xXssProtection())
- break
- }
- return result
- }
- const helmet = Object.assign(
- function helmet(options = {}) {
- // People should be able to pass an options object with no prototype,
- // so we want this optional chaining.
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- if (options.constructor?.name === "IncomingMessage") {
- throw new Error("It appears you have done something like `app.use(helmet)`, but it should be `app.use(helmet())`.")
- }
- const middlewareFunctions = getMiddlewareFunctionsFromOptions(options)
- return function helmetMiddleware(req, res, next) {
- let middlewareIndex = 0
- ;(function internalNext(err) {
- if (err) {
- next(err)
- return
- }
- const middlewareFunction = middlewareFunctions[middlewareIndex]
- if (middlewareFunction) {
- middlewareIndex++
- middlewareFunction(req, res, internalNext)
- } else {
- next()
- }
- })()
- }
- },
- {
- contentSecurityPolicy,
- crossOriginEmbedderPolicy,
- crossOriginOpenerPolicy,
- crossOriginResourcePolicy,
- originAgentCluster,
- referrerPolicy,
- strictTransportSecurity,
- xContentTypeOptions,
- xDnsPrefetchControl,
- xDownloadOptions,
- xFrameOptions,
- xPermittedCrossDomainPolicies,
- xPoweredBy,
- xXssProtection,
- // Legacy aliases
- dnsPrefetchControl: xDnsPrefetchControl,
- xssFilter: xXssProtection,
- permittedCrossDomainPolicies: xPermittedCrossDomainPolicies,
- ieNoOpen: xDownloadOptions,
- noSniff: xContentTypeOptions,
- frameguard: xFrameOptions,
- hidePoweredBy: xPoweredBy,
- hsts: strictTransportSecurity
- }
- )
- exports.contentSecurityPolicy = contentSecurityPolicy
- exports.crossOriginEmbedderPolicy = crossOriginEmbedderPolicy
- exports.crossOriginOpenerPolicy = crossOriginOpenerPolicy
- exports.crossOriginResourcePolicy = crossOriginResourcePolicy
- exports.default = helmet
- exports.dnsPrefetchControl = xDnsPrefetchControl
- exports.frameguard = xFrameOptions
- exports.hidePoweredBy = xPoweredBy
- exports.hsts = strictTransportSecurity
- exports.ieNoOpen = xDownloadOptions
- exports.noSniff = xContentTypeOptions
- exports.originAgentCluster = originAgentCluster
- exports.permittedCrossDomainPolicies = xPermittedCrossDomainPolicies
- exports.referrerPolicy = referrerPolicy
- exports.strictTransportSecurity = strictTransportSecurity
- exports.xContentTypeOptions = xContentTypeOptions
- exports.xDnsPrefetchControl = xDnsPrefetchControl
- exports.xDownloadOptions = xDownloadOptions
- exports.xFrameOptions = xFrameOptions
- exports.xPermittedCrossDomainPolicies = xPermittedCrossDomainPolicies
- exports.xPoweredBy = xPoweredBy
- exports.xXssProtection = xXssProtection
- exports.xssFilter = xXssProtection
- module.exports = exports.default
- module.exports.default = module.exports
|