diff --git a/packages/http-security-headers/__tests__/index.js b/packages/http-security-headers/__tests__/index.js index 5f3e833e3..5906a75dc 100644 --- a/packages/http-security-headers/__tests__/index.js +++ b/packages/http-security-headers/__tests__/index.js @@ -274,9 +274,8 @@ test('It should support report only mode', async (t) => { handler.use( httpSecurityHeaders({ - contentSecurityPolicy: { - reportOnly: true - } + contentSecurityPolicy: {}, + contentSecurityPolicyReportOnly: true }) ) diff --git a/packages/http-security-headers/index.d.ts b/packages/http-security-headers/index.d.ts index c2a8e9f84..55d6c153a 100644 --- a/packages/http-security-headers/index.d.ts +++ b/packages/http-security-headers/index.d.ts @@ -28,7 +28,8 @@ interface Options { xssProtection?: { reportUri?: string } - contentSecurityPolicy?: Record + contentSecurityPolicy?: Record + contentSecurityPolicyReportOnly?: boolean crossOriginEmbedderPolicy?: { policy?: string } @@ -54,7 +55,7 @@ interface Options { type WithFalseValues = { [K in keyof T]: T[K] | false } -declare function httpSecurityHeaders ( +declare function httpSecurityHeaders( options?: WithFalseValues ): middy.MiddlewareObj diff --git a/packages/http-security-headers/index.js b/packages/http-security-headers/index.js index 6f4ffe52c..002031345 100644 --- a/packages/http-security-headers/index.js +++ b/packages/http-security-headers/index.js @@ -34,9 +34,9 @@ const defaults = { // Other directives 'require-trusted-types-for': "'script'", 'trusted-types': "'none'", - 'upgrade-insecure-requests': '', - reportOnly: false + 'upgrade-insecure-requests': '' }, + contentSecurityPolicyReportOnly: false, contentTypeOptions: { action: 'nosniff' }, @@ -114,11 +114,16 @@ const defaults = { referrerPolicy: { policy: 'no-referrer' }, + reportingEndpoints: { + csp: '', + permissions: '' + }, reportTo: { maxAge: 365 * 24 * 60 * 60, default: '', includeSubdomains: true, csp: '', + permissions: '', staple: '', xss: '' }, @@ -137,9 +142,8 @@ const helmetHtmlOnly = {} // *** https://github.com/helmetjs/helmet/tree/main/middlewares *** // // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy -helmetHtmlOnly.contentSecurityPolicy = (headers, config) => { +helmetHtmlOnly.contentSecurityPolicy = (reportOnly) => (headers, config) => { let header = Object.keys(config) - .filter((policy) => policy !== 'reportOnly') .map((policy) => (config[policy] ? `${policy} ${config[policy]}` : '')) .filter((str) => str) .join('; ') @@ -150,7 +154,7 @@ helmetHtmlOnly.contentSecurityPolicy = (headers, config) => { header += '; upgrade-insecure-requests' } - const cspHeaderName = config.reportOnly + const cspHeaderName = reportOnly ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy' headers[cspHeaderName] = header @@ -188,6 +192,7 @@ helmet.referrerPolicy = (headers, config) => { headers['Referrer-Policy'] = config.policy } +// DEPRECATED by reportingEndpoints helmetHtmlOnly.reportTo = (headers, config) => { headers['Report-To'] = Object.keys(config) .map((group) => { @@ -203,6 +208,15 @@ helmetHtmlOnly.reportTo = (headers, config) => { .join(', ') } +helmet.reportingEndpoints = (headers, config) => { + headers['Reporting-Endpoints'] = Object.keys(config) + .map((group) => { + return config[group] && group + '-endpoint=' + config[group] + }) + .filter((str) => str) + .join(', ') +} + // https://github.com/helmetjs/hsts helmet.strictTransportSecurity = (headers, config) => { let header = 'max-age=' + Math.round(config.maxAge) @@ -280,7 +294,14 @@ const httpSecurityHeadersMiddleware = (opts = {}) => { Object.keys(helmetHtmlOnly).forEach((key) => { if (!options[key]) return const config = { ...defaults[key], ...options[key] } - helmetHtmlOnly[key](request.response.headers, config) + if (key === 'contentSecurityPolicy') { + helmetHtmlOnly[key](options.contentSecurityPolicyReportOnly)( + request.response.headers, + config + ) + } else { + helmetHtmlOnly[key](request.response.headers, config) + } }) } } diff --git a/website/docs/middlewares/http-security-headers.md b/website/docs/middlewares/http-security-headers.md index b818add67..336b80f80 100644 --- a/website/docs/middlewares/http-security-headers.md +++ b/website/docs/middlewares/http-security-headers.md @@ -12,10 +12,8 @@ To install this middleware you can use NPM: npm install --save @middy/http-security-headers ``` -## Options - +## Features - `dnsPrefetchControl` controls browser DNS prefetching -- `expectCt` for handling Certificate Transparency (Future Feature) - `frameguard` to prevent clickjacking - `hidePoweredBy` to remove the Server/X-Powered-By header - `hsts` for HTTP Strict Transport Security @@ -24,6 +22,10 @@ npm install --save @middy/http-security-headers - `referrerPolicy` to hide the Referer header - `xssFilter` adds some small XSS protections +## Options + +There are a lot, see [source](https://github.com/middyjs/middy/blob/main/packages/http-security-headers/index.js#L5) + ## Sample usage ```javascript diff --git a/website/docs/upgrade/4-5.md b/website/docs/upgrade/4-5.md index a9203572a..47834ff67 100644 --- a/website/docs/upgrade/4-5.md +++ b/website/docs/upgrade/4-5.md @@ -55,6 +55,7 @@ No change ### [http-content-encoding](/docs/middlewares/http-content-encoding) - Use `preferredLanguage` from `context` instead of `event` (See http-content-negotiation). **Breaking Change** +- Add in `Vary` support ([#1253](https://github.com/middyjs/middy/issues/1253)) **Breaking Change** ### [http-content-negotiation](/docs/middlewares/http-content-negotiation) @@ -98,7 +99,7 @@ No change ### [http-security-headers](/docs/middlewares/http-security-headers) -No change +- Add in support for `Content-Security-Policy-Report-Only` ([#1248](https://github.com/middyjs/middy/issues/1248)) ### [http-urlencode-body-parser](/docs/middlewares/http-urlencode-body-parser)