@middy/http-security-headers 4.6.5 → 5.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.js +268 -240
  2. package/package.json +5 -11
  3. package/index.cjs +0 -270
package/index.js CHANGED
@@ -1,260 +1,288 @@
1
- import { normalizeHttpResponse } from '@middy/util';
1
+ import { normalizeHttpResponse } from '@middy/util'
2
+
2
3
  // Code and Defaults heavily based off https://helmetjs.github.io/
4
+
3
5
  const defaults = {
4
- contentSecurityPolicy: {
5
- // Fetch directives
6
- // 'child-src': '', // fallback default-src
7
- // 'connect-src': '', // fallback default-src
8
- 'default-src': "'none'",
9
- // 'font-src':'', // fallback default-src
10
- // 'frame-src':'', // fallback child-src > default-src
11
- // 'img-src':'', // fallback default-src
12
- // 'manifest-src':'', // fallback default-src
13
- // 'media-src':'', // fallback default-src
14
- // 'object-src':'', // fallback default-src
15
- // 'prefetch-src':'', // fallback default-src
16
- // 'script-src':'', // fallback default-src
17
- // 'script-src-elem':'', // fallback script-src > default-src
18
- // 'script-src-attr':'', // fallback script-src > default-src
19
- // 'style-src':'', // fallback default-src
20
- // 'style-src-elem':'', // fallback style-src > default-src
21
- // 'style-src-attr':'', // fallback style-src > default-src
22
- // 'worker-src':'', // fallback child-src > script-src > default-src
23
- // Document directives
24
- 'base-uri': "'none'",
25
- sandbox: '',
26
- // Navigation directives
27
- 'form-action': "'none'",
28
- 'frame-ancestors': "'none'",
29
- 'navigate-to': "'none'",
30
- // Reporting directives
31
- 'report-to': 'csp',
32
- // Other directives
33
- 'require-trusted-types-for': "'script'",
34
- 'trusted-types': "'none'",
35
- 'upgrade-insecure-requests': ''
36
- },
37
- contentTypeOptions: {
38
- action: 'nosniff'
39
- },
40
- crossOriginEmbedderPolicy: {
41
- policy: 'require-corp'
42
- },
43
- crossOriginOpenerPolicy: {
44
- policy: 'same-origin'
45
- },
46
- crossOriginResourcePolicy: {
47
- policy: 'same-origin'
48
- },
49
- dnsPrefetchControl: {
50
- allow: false
51
- },
52
- downloadOptions: {
53
- action: 'noopen'
54
- },
55
- frameOptions: {
56
- action: 'deny'
57
- },
58
- originAgentCluster: {},
59
- permissionsPolicy: {
60
- // Standard
61
- accelerometer: '',
62
- 'ambient-light-sensor': '',
63
- autoplay: '',
64
- battery: '',
65
- camera: '',
66
- 'cross-origin-isolated': '',
67
- 'display-capture': '',
68
- 'document-domain': '',
69
- 'encrypted-media': '',
70
- 'execution-while-not-rendered': '',
71
- 'execution-while-out-of-viewport': '',
72
- fullscreen: '',
73
- geolocation: '',
74
- gyroscope: '',
75
- 'keyboard-map': '',
76
- magnetometer: '',
77
- microphone: '',
78
- midi: '',
79
- 'navigation-override': '',
80
- payment: '',
81
- 'picture-in-picture': '',
82
- 'publickey-credentials-get': '',
83
- 'screen-wake-lock': '',
84
- 'sync-xhr': '',
85
- usb: '',
86
- 'web-share': '',
87
- 'xr-spatial-tracking': '',
88
- // Proposed
89
- 'clipboard-read': '',
90
- 'clipboard-write': '',
91
- gamepad: '',
92
- 'speaker-selection': '',
93
- // Experimental
94
- 'conversion-measurement': '',
95
- 'focus-without-user-activation': '',
96
- hid: '',
97
- 'idle-detection': '',
98
- 'interest-cohort': '',
99
- serial: '',
100
- 'sync-script': '',
101
- 'trust-token-redemption': '',
102
- 'window-placement': '',
103
- 'vertical-scroll': ''
104
- },
105
- permittedCrossDomainPolicies: {
106
- policy: 'none' // none, master-only, by-content-type, by-ftp-filename, all
107
- },
108
- poweredBy: {
109
- server: ''
110
- },
111
- referrerPolicy: {
112
- policy: 'no-referrer'
113
- },
114
- reportTo: {
115
- maxAge: 365 * 24 * 60 * 60,
116
- default: '',
117
- includeSubdomains: true,
118
- csp: '',
119
- staple: '',
120
- xss: ''
121
- },
122
- strictTransportSecurity: {
123
- maxAge: 180 * 24 * 60 * 60,
124
- includeSubDomains: true,
125
- preload: true
126
- },
127
- xssProtection: {
128
- reportTo: 'xss'
129
- }
130
- };
131
- const helmet = {};
132
- const helmetHtmlOnly = {};
6
+ contentSecurityPolicy: {
7
+ // Fetch directives
8
+ // 'child-src': '', // fallback default-src
9
+ // 'connect-src': '', // fallback default-src
10
+ 'default-src': "'none'",
11
+ // 'font-src':'', // fallback default-src
12
+ // 'frame-src':'', // fallback child-src > default-src
13
+ // 'img-src':'', // fallback default-src
14
+ // 'manifest-src':'', // fallback default-src
15
+ // 'media-src':'', // fallback default-src
16
+ // 'object-src':'', // fallback default-src
17
+ // 'prefetch-src':'', // fallback default-src
18
+ // 'script-src':'', // fallback default-src
19
+ // 'script-src-elem':'', // fallback script-src > default-src
20
+ // 'script-src-attr':'', // fallback script-src > default-src
21
+ // 'style-src':'', // fallback default-src
22
+ // 'style-src-elem':'', // fallback style-src > default-src
23
+ // 'style-src-attr':'', // fallback style-src > default-src
24
+ // 'worker-src':'', // fallback child-src > script-src > default-src
25
+ // Document directives
26
+ 'base-uri': "'none'",
27
+ sandbox: '',
28
+ // Navigation directives
29
+ 'form-action': "'none'",
30
+ 'frame-ancestors': "'none'",
31
+ 'navigate-to': "'none'",
32
+ // Reporting directives
33
+ 'report-to': 'csp',
34
+ // Other directives
35
+ 'require-trusted-types-for': "'script'",
36
+ 'trusted-types': "'none'",
37
+ 'upgrade-insecure-requests': ''
38
+ },
39
+ contentTypeOptions: {
40
+ action: 'nosniff'
41
+ },
42
+ crossOriginEmbedderPolicy: {
43
+ policy: 'require-corp'
44
+ },
45
+ crossOriginOpenerPolicy: {
46
+ policy: 'same-origin'
47
+ },
48
+ crossOriginResourcePolicy: {
49
+ policy: 'same-origin'
50
+ },
51
+ dnsPrefetchControl: {
52
+ allow: false
53
+ },
54
+ downloadOptions: {
55
+ action: 'noopen'
56
+ },
57
+ frameOptions: {
58
+ action: 'deny'
59
+ },
60
+ originAgentCluster: {},
61
+ permissionsPolicy: {
62
+ // Standard
63
+ accelerometer: '',
64
+ 'ambient-light-sensor': '',
65
+ autoplay: '',
66
+ battery: '',
67
+ camera: '',
68
+ 'cross-origin-isolated': '',
69
+ 'display-capture': '',
70
+ 'document-domain': '',
71
+ 'encrypted-media': '',
72
+ 'execution-while-not-rendered': '',
73
+ 'execution-while-out-of-viewport': '',
74
+ fullscreen: '',
75
+ geolocation: '',
76
+ gyroscope: '',
77
+ 'keyboard-map': '',
78
+ magnetometer: '',
79
+ microphone: '',
80
+ midi: '',
81
+ 'navigation-override': '',
82
+ payment: '',
83
+ 'picture-in-picture': '',
84
+ 'publickey-credentials-get': '',
85
+ 'screen-wake-lock': '',
86
+ 'sync-xhr': '',
87
+ usb: '',
88
+ 'web-share': '',
89
+ 'xr-spatial-tracking': '',
90
+ // Proposed
91
+ 'clipboard-read': '',
92
+ 'clipboard-write': '',
93
+ gamepad: '',
94
+ 'speaker-selection': '',
95
+ // Experimental
96
+ 'conversion-measurement': '',
97
+ 'focus-without-user-activation': '',
98
+ hid: '',
99
+ 'idle-detection': '',
100
+ 'interest-cohort': '',
101
+ serial: '',
102
+ 'sync-script': '',
103
+ 'trust-token-redemption': '',
104
+ 'window-placement': '',
105
+ 'vertical-scroll': ''
106
+ },
107
+ permittedCrossDomainPolicies: {
108
+ policy: 'none' // none, master-only, by-content-type, by-ftp-filename, all
109
+ },
110
+ poweredBy: {
111
+ server: ''
112
+ },
113
+ referrerPolicy: {
114
+ policy: 'no-referrer'
115
+ },
116
+ reportTo: {
117
+ maxAge: 365 * 24 * 60 * 60,
118
+ default: '',
119
+ includeSubdomains: true,
120
+ csp: '',
121
+ staple: '',
122
+ xss: ''
123
+ },
124
+ strictTransportSecurity: {
125
+ maxAge: 180 * 24 * 60 * 60,
126
+ includeSubDomains: true,
127
+ preload: true
128
+ },
129
+ xssProtection: {
130
+ reportTo: 'xss'
131
+ }
132
+ }
133
+
134
+ const helmet = {}
135
+ const helmetHtmlOnly = {}
136
+
133
137
  // *** https://github.com/helmetjs/helmet/tree/main/middlewares *** //
134
138
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
135
- helmetHtmlOnly.contentSecurityPolicy = (headers, config)=>{
136
- let header = Object.keys(config).map((policy)=>config[policy] ? `${policy} ${config[policy]}` : '').filter((str)=>str).join('; ');
137
- if (config.sandbox === '') {
138
- header += '; sandbox';
139
- }
140
- if (config['upgrade-insecure-requests'] === '') {
141
- header += '; upgrade-insecure-requests';
142
- }
143
- headers['Content-Security-Policy'] = header;
144
- };
139
+ helmetHtmlOnly.contentSecurityPolicy = (headers, config) => {
140
+ let header = Object.keys(config)
141
+ .map((policy) => (config[policy] ? `${policy} ${config[policy]}` : ''))
142
+ .filter((str) => str)
143
+ .join('; ')
144
+ if (config.sandbox === '') {
145
+ header += '; sandbox'
146
+ }
147
+ if (config['upgrade-insecure-requests'] === '') {
148
+ header += '; upgrade-insecure-requests'
149
+ }
150
+ headers['Content-Security-Policy'] = header
151
+ }
145
152
  // crossdomain - N/A - for Adobe products
146
- helmetHtmlOnly.crossOriginEmbedderPolicy = (headers, config)=>{
147
- headers['Cross-Origin-Embedder-Policy'] = config.policy;
148
- };
149
- helmetHtmlOnly.crossOriginOpenerPolicy = (headers, config)=>{
150
- headers['Cross-Origin-Opener-Policy'] = config.policy;
151
- };
152
- helmetHtmlOnly.crossOriginResourcePolicy = (headers, config)=>{
153
- headers['Cross-Origin-Resource-Policy'] = config.policy;
154
- };
153
+ helmetHtmlOnly.crossOriginEmbedderPolicy = (headers, config) => {
154
+ headers['Cross-Origin-Embedder-Policy'] = config.policy
155
+ }
156
+ helmetHtmlOnly.crossOriginOpenerPolicy = (headers, config) => {
157
+ headers['Cross-Origin-Opener-Policy'] = config.policy
158
+ }
159
+ helmetHtmlOnly.crossOriginResourcePolicy = (headers, config) => {
160
+ headers['Cross-Origin-Resource-Policy'] = config.policy
161
+ }
162
+
155
163
  // DEPRECATED: expectCt
156
164
  // DEPRECATED: hpkp
165
+
157
166
  // https://www.permissionspolicy.com/
158
- helmetHtmlOnly.permissionsPolicy = (headers, config)=>{
159
- headers['Permissions-Policy'] = Object.keys(config).map((policy)=>`${policy}=${config[policy] === '*' ? '*' : '(' + config[policy] + ')'}`).join(', ');
160
- };
161
- helmet.originAgentCluster = (headers, config)=>{
162
- headers['Origin-Agent-Cluster'] = '?1';
163
- };
167
+ helmetHtmlOnly.permissionsPolicy = (headers, config) => {
168
+ headers['Permissions-Policy'] = Object.keys(config)
169
+ .map(
170
+ (policy) =>
171
+ `${policy}=${config[policy] === '*' ? '*' : '(' + config[policy] + ')'}`
172
+ )
173
+ .join(', ')
174
+ }
175
+
176
+ helmet.originAgentCluster = (headers, config) => {
177
+ headers['Origin-Agent-Cluster'] = '?1'
178
+ }
179
+
164
180
  // https://github.com/helmetjs/referrer-policy
165
- helmet.referrerPolicy = (headers, config)=>{
166
- headers['Referrer-Policy'] = config.policy;
167
- };
168
- helmetHtmlOnly.reportTo = (headers, config)=>{
169
- headers['Report-To'] = Object.keys(config).map((group)=>{
170
- const includeSubdomains = group === 'default' ? `, "include_subdomains": ${config.includeSubdomains}` : '';
171
- return config[group] && group !== 'includeSubdomains' ? `{ "group": "default", "max_age": ${config.maxAge}, "endpoints": [ { "url": "${config[group]}" } ]${includeSubdomains} }` : '';
172
- }).filter((str)=>str).join(', ');
173
- };
181
+ helmet.referrerPolicy = (headers, config) => {
182
+ headers['Referrer-Policy'] = config.policy
183
+ }
184
+
185
+ helmetHtmlOnly.reportTo = (headers, config) => {
186
+ headers['Report-To'] = Object.keys(config)
187
+ .map((group) => {
188
+ const includeSubdomains =
189
+ group === 'default'
190
+ ? `, "include_subdomains": ${config.includeSubdomains}`
191
+ : ''
192
+ return config[group] && group !== 'includeSubdomains'
193
+ ? `{ "group": "default", "max_age": ${config.maxAge}, "endpoints": [ { "url": "${config[group]}" } ]${includeSubdomains} }`
194
+ : ''
195
+ })
196
+ .filter((str) => str)
197
+ .join(', ')
198
+ }
199
+
174
200
  // https://github.com/helmetjs/hsts
175
- helmet.strictTransportSecurity = (headers, config)=>{
176
- let header = 'max-age=' + Math.round(config.maxAge);
177
- if (config.includeSubDomains) {
178
- header += '; includeSubDomains';
179
- }
180
- if (config.preload) {
181
- header += '; preload';
182
- }
183
- headers['Strict-Transport-Security'] = header;
184
- };
201
+ helmet.strictTransportSecurity = (headers, config) => {
202
+ let header = 'max-age=' + Math.round(config.maxAge)
203
+ if (config.includeSubDomains) {
204
+ header += '; includeSubDomains'
205
+ }
206
+ if (config.preload) {
207
+ header += '; preload'
208
+ }
209
+ headers['Strict-Transport-Security'] = header
210
+ }
211
+
185
212
  // noCache - N/A - separate middleware
213
+
186
214
  // X-* //
187
215
  // https://github.com/helmetjs/dont-sniff-mimetype
188
- helmet.contentTypeOptions = (headers, config)=>{
189
- headers['X-Content-Type-Options'] = config.action;
190
- };
216
+ helmet.contentTypeOptions = (headers, config) => {
217
+ headers['X-Content-Type-Options'] = config.action
218
+ }
219
+
191
220
  // https://github.com/helmetjs/dns-Prefetch-control
192
- helmet.dnsPrefetchControl = (headers, config)=>{
193
- headers['X-DNS-Prefetch-Control'] = config.allow ? 'on' : 'off';
194
- };
221
+ helmet.dnsPrefetchControl = (headers, config) => {
222
+ headers['X-DNS-Prefetch-Control'] = config.allow ? 'on' : 'off'
223
+ }
224
+
195
225
  // https://github.com/helmetjs/ienoopen
196
- helmet.downloadOptions = (headers, config)=>{
197
- headers['X-Download-Options'] = config.action;
198
- };
226
+ helmet.downloadOptions = (headers, config) => {
227
+ headers['X-Download-Options'] = config.action
228
+ }
229
+
199
230
  // https://github.com/helmetjs/frameOptions
200
- helmetHtmlOnly.frameOptions = (headers, config)=>{
201
- headers['X-Frame-Options'] = config.action.toUpperCase();
202
- };
231
+ helmetHtmlOnly.frameOptions = (headers, config) => {
232
+ headers['X-Frame-Options'] = config.action.toUpperCase()
233
+ }
234
+
203
235
  // https://github.com/helmetjs/crossdomain
204
- helmet.permittedCrossDomainPolicies = (headers, config)=>{
205
- headers['X-Permitted-Cross-Domain-Policies'] = config.policy;
206
- };
236
+ helmet.permittedCrossDomainPolicies = (headers, config) => {
237
+ headers['X-Permitted-Cross-Domain-Policies'] = config.policy
238
+ }
239
+
207
240
  // https://github.com/helmetjs/hide-powered-by
208
- helmet.poweredBy = (headers, config)=>{
209
- if (config.server) {
210
- headers['X-Powered-By'] = config.server;
211
- } else {
212
- delete headers.Server;
213
- delete headers['X-Powered-By'];
214
- }
215
- };
241
+ helmet.poweredBy = (headers, config) => {
242
+ if (config.server) {
243
+ headers['X-Powered-By'] = config.server
244
+ } else {
245
+ delete headers.Server
246
+ delete headers['X-Powered-By']
247
+ }
248
+ }
249
+
216
250
  // https://github.com/helmetjs/x-xss-protection
217
- helmetHtmlOnly.xssProtection = (headers, config)=>{
218
- let header = '1; mode=block';
219
- if (config.reportTo) {
220
- header += '; report=' + config.reportTo;
221
- }
222
- headers['X-XSS-Protection'] = header;
223
- };
224
- const httpSecurityHeadersMiddleware = (opts = {})=>{
225
- const options = {
226
- ...defaults,
227
- ...opts
228
- };
229
- const httpSecurityHeadersMiddlewareAfter = async (request)=>{
230
- normalizeHttpResponse(request);
231
- Object.keys(helmet).forEach((key)=>{
232
- if (!options[key]) return;
233
- const config = {
234
- ...defaults[key],
235
- ...options[key]
236
- };
237
- helmet[key](request.response.headers, config);
238
- });
239
- if (request.response.headers['Content-Type']?.includes('text/html')) {
240
- Object.keys(helmetHtmlOnly).forEach((key)=>{
241
- if (!options[key]) return;
242
- const config = {
243
- ...defaults[key],
244
- ...options[key]
245
- };
246
- helmetHtmlOnly[key](request.response.headers, config);
247
- });
248
- }
249
- };
250
- const httpSecurityHeadersMiddlewareOnError = async (request)=>{
251
- if (request.response === undefined) return;
252
- await httpSecurityHeadersMiddlewareAfter(request);
253
- };
254
- return {
255
- after: httpSecurityHeadersMiddlewareAfter,
256
- onError: httpSecurityHeadersMiddlewareOnError
257
- };
258
- };
259
- export default httpSecurityHeadersMiddleware;
251
+ helmetHtmlOnly.xssProtection = (headers, config) => {
252
+ let header = '1; mode=block'
253
+ if (config.reportTo) {
254
+ header += '; report=' + config.reportTo
255
+ }
256
+ headers['X-XSS-Protection'] = header
257
+ }
258
+
259
+ const httpSecurityHeadersMiddleware = (opts = {}) => {
260
+ const options = { ...defaults, ...opts }
260
261
 
262
+ const httpSecurityHeadersMiddlewareAfter = async (request) => {
263
+ normalizeHttpResponse(request)
264
+
265
+ Object.keys(helmet).forEach((key) => {
266
+ if (!options[key]) return
267
+ const config = { ...defaults[key], ...options[key] }
268
+ helmet[key](request.response.headers, config)
269
+ })
270
+
271
+ if (request.response.headers['Content-Type']?.includes('text/html')) {
272
+ Object.keys(helmetHtmlOnly).forEach((key) => {
273
+ if (!options[key]) return
274
+ const config = { ...defaults[key], ...options[key] }
275
+ helmetHtmlOnly[key](request.response.headers, config)
276
+ })
277
+ }
278
+ }
279
+ const httpSecurityHeadersMiddlewareOnError = async (request) => {
280
+ if (request.response === undefined) return
281
+ await httpSecurityHeadersMiddlewareAfter(request)
282
+ }
283
+ return {
284
+ after: httpSecurityHeadersMiddlewareAfter,
285
+ onError: httpSecurityHeadersMiddlewareOnError
286
+ }
287
+ }
288
+ export default httpSecurityHeadersMiddleware
package/package.json CHANGED
@@ -1,33 +1,27 @@
1
1
  {
2
2
  "name": "@middy/http-security-headers",
3
- "version": "4.6.5",
3
+ "version": "5.0.0-alpha.1",
4
4
  "description": "Applies best practice security headers to responses. It's a simplified port of HelmetJS",
5
5
  "type": "module",
6
6
  "engines": {
7
- "node": ">=16"
7
+ "node": ">=18"
8
8
  },
9
9
  "engineStrict": true,
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
13
- "main": "./index.cjs",
14
13
  "module": "./index.js",
15
14
  "exports": {
16
15
  ".": {
17
16
  "import": {
18
17
  "types": "./index.d.ts",
19
18
  "default": "./index.js"
20
- },
21
- "require": {
22
- "types": "./index.d.ts",
23
- "default": "./index.cjs"
24
19
  }
25
20
  }
26
21
  },
27
22
  "types": "index.d.ts",
28
23
  "files": [
29
24
  "index.js",
30
- "index.cjs",
31
25
  "index.d.ts"
32
26
  ],
33
27
  "scripts": {
@@ -68,11 +62,11 @@
68
62
  "type": "github",
69
63
  "url": "https://github.com/sponsors/willfarrell"
70
64
  },
71
- "gitHead": "573d7b0bb243d8c5a9bcb00cf29d031aa7a0c606",
65
+ "gitHead": "ebce8d5df8783077fa49ba62ee9be20e8486a7f1",
72
66
  "dependencies": {
73
- "@middy/util": "4.6.5"
67
+ "@middy/util": "5.0.0-alpha.1"
74
68
  },
75
69
  "devDependencies": {
76
- "@middy/core": "4.6.5"
70
+ "@middy/core": "5.0.0-alpha.1"
77
71
  }
78
72
  }
package/index.cjs DELETED
@@ -1,270 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(module, "exports", {
6
- enumerable: true,
7
- get: function() {
8
- return _default;
9
- }
10
- });
11
- const _util = require("@middy/util");
12
- // Code and Defaults heavily based off https://helmetjs.github.io/
13
- const defaults = {
14
- contentSecurityPolicy: {
15
- // Fetch directives
16
- // 'child-src': '', // fallback default-src
17
- // 'connect-src': '', // fallback default-src
18
- 'default-src': "'none'",
19
- // 'font-src':'', // fallback default-src
20
- // 'frame-src':'', // fallback child-src > default-src
21
- // 'img-src':'', // fallback default-src
22
- // 'manifest-src':'', // fallback default-src
23
- // 'media-src':'', // fallback default-src
24
- // 'object-src':'', // fallback default-src
25
- // 'prefetch-src':'', // fallback default-src
26
- // 'script-src':'', // fallback default-src
27
- // 'script-src-elem':'', // fallback script-src > default-src
28
- // 'script-src-attr':'', // fallback script-src > default-src
29
- // 'style-src':'', // fallback default-src
30
- // 'style-src-elem':'', // fallback style-src > default-src
31
- // 'style-src-attr':'', // fallback style-src > default-src
32
- // 'worker-src':'', // fallback child-src > script-src > default-src
33
- // Document directives
34
- 'base-uri': "'none'",
35
- sandbox: '',
36
- // Navigation directives
37
- 'form-action': "'none'",
38
- 'frame-ancestors': "'none'",
39
- 'navigate-to': "'none'",
40
- // Reporting directives
41
- 'report-to': 'csp',
42
- // Other directives
43
- 'require-trusted-types-for': "'script'",
44
- 'trusted-types': "'none'",
45
- 'upgrade-insecure-requests': ''
46
- },
47
- contentTypeOptions: {
48
- action: 'nosniff'
49
- },
50
- crossOriginEmbedderPolicy: {
51
- policy: 'require-corp'
52
- },
53
- crossOriginOpenerPolicy: {
54
- policy: 'same-origin'
55
- },
56
- crossOriginResourcePolicy: {
57
- policy: 'same-origin'
58
- },
59
- dnsPrefetchControl: {
60
- allow: false
61
- },
62
- downloadOptions: {
63
- action: 'noopen'
64
- },
65
- frameOptions: {
66
- action: 'deny'
67
- },
68
- originAgentCluster: {},
69
- permissionsPolicy: {
70
- // Standard
71
- accelerometer: '',
72
- 'ambient-light-sensor': '',
73
- autoplay: '',
74
- battery: '',
75
- camera: '',
76
- 'cross-origin-isolated': '',
77
- 'display-capture': '',
78
- 'document-domain': '',
79
- 'encrypted-media': '',
80
- 'execution-while-not-rendered': '',
81
- 'execution-while-out-of-viewport': '',
82
- fullscreen: '',
83
- geolocation: '',
84
- gyroscope: '',
85
- 'keyboard-map': '',
86
- magnetometer: '',
87
- microphone: '',
88
- midi: '',
89
- 'navigation-override': '',
90
- payment: '',
91
- 'picture-in-picture': '',
92
- 'publickey-credentials-get': '',
93
- 'screen-wake-lock': '',
94
- 'sync-xhr': '',
95
- usb: '',
96
- 'web-share': '',
97
- 'xr-spatial-tracking': '',
98
- // Proposed
99
- 'clipboard-read': '',
100
- 'clipboard-write': '',
101
- gamepad: '',
102
- 'speaker-selection': '',
103
- // Experimental
104
- 'conversion-measurement': '',
105
- 'focus-without-user-activation': '',
106
- hid: '',
107
- 'idle-detection': '',
108
- 'interest-cohort': '',
109
- serial: '',
110
- 'sync-script': '',
111
- 'trust-token-redemption': '',
112
- 'window-placement': '',
113
- 'vertical-scroll': ''
114
- },
115
- permittedCrossDomainPolicies: {
116
- policy: 'none' // none, master-only, by-content-type, by-ftp-filename, all
117
- },
118
- poweredBy: {
119
- server: ''
120
- },
121
- referrerPolicy: {
122
- policy: 'no-referrer'
123
- },
124
- reportTo: {
125
- maxAge: 365 * 24 * 60 * 60,
126
- default: '',
127
- includeSubdomains: true,
128
- csp: '',
129
- staple: '',
130
- xss: ''
131
- },
132
- strictTransportSecurity: {
133
- maxAge: 180 * 24 * 60 * 60,
134
- includeSubDomains: true,
135
- preload: true
136
- },
137
- xssProtection: {
138
- reportTo: 'xss'
139
- }
140
- };
141
- const helmet = {};
142
- const helmetHtmlOnly = {};
143
- // *** https://github.com/helmetjs/helmet/tree/main/middlewares *** //
144
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
145
- helmetHtmlOnly.contentSecurityPolicy = (headers, config)=>{
146
- let header = Object.keys(config).map((policy)=>config[policy] ? `${policy} ${config[policy]}` : '').filter((str)=>str).join('; ');
147
- if (config.sandbox === '') {
148
- header += '; sandbox';
149
- }
150
- if (config['upgrade-insecure-requests'] === '') {
151
- header += '; upgrade-insecure-requests';
152
- }
153
- headers['Content-Security-Policy'] = header;
154
- };
155
- // crossdomain - N/A - for Adobe products
156
- helmetHtmlOnly.crossOriginEmbedderPolicy = (headers, config)=>{
157
- headers['Cross-Origin-Embedder-Policy'] = config.policy;
158
- };
159
- helmetHtmlOnly.crossOriginOpenerPolicy = (headers, config)=>{
160
- headers['Cross-Origin-Opener-Policy'] = config.policy;
161
- };
162
- helmetHtmlOnly.crossOriginResourcePolicy = (headers, config)=>{
163
- headers['Cross-Origin-Resource-Policy'] = config.policy;
164
- };
165
- // DEPRECATED: expectCt
166
- // DEPRECATED: hpkp
167
- // https://www.permissionspolicy.com/
168
- helmetHtmlOnly.permissionsPolicy = (headers, config)=>{
169
- headers['Permissions-Policy'] = Object.keys(config).map((policy)=>`${policy}=${config[policy] === '*' ? '*' : '(' + config[policy] + ')'}`).join(', ');
170
- };
171
- helmet.originAgentCluster = (headers, config)=>{
172
- headers['Origin-Agent-Cluster'] = '?1';
173
- };
174
- // https://github.com/helmetjs/referrer-policy
175
- helmet.referrerPolicy = (headers, config)=>{
176
- headers['Referrer-Policy'] = config.policy;
177
- };
178
- helmetHtmlOnly.reportTo = (headers, config)=>{
179
- headers['Report-To'] = Object.keys(config).map((group)=>{
180
- const includeSubdomains = group === 'default' ? `, "include_subdomains": ${config.includeSubdomains}` : '';
181
- return config[group] && group !== 'includeSubdomains' ? `{ "group": "default", "max_age": ${config.maxAge}, "endpoints": [ { "url": "${config[group]}" } ]${includeSubdomains} }` : '';
182
- }).filter((str)=>str).join(', ');
183
- };
184
- // https://github.com/helmetjs/hsts
185
- helmet.strictTransportSecurity = (headers, config)=>{
186
- let header = 'max-age=' + Math.round(config.maxAge);
187
- if (config.includeSubDomains) {
188
- header += '; includeSubDomains';
189
- }
190
- if (config.preload) {
191
- header += '; preload';
192
- }
193
- headers['Strict-Transport-Security'] = header;
194
- };
195
- // noCache - N/A - separate middleware
196
- // X-* //
197
- // https://github.com/helmetjs/dont-sniff-mimetype
198
- helmet.contentTypeOptions = (headers, config)=>{
199
- headers['X-Content-Type-Options'] = config.action;
200
- };
201
- // https://github.com/helmetjs/dns-Prefetch-control
202
- helmet.dnsPrefetchControl = (headers, config)=>{
203
- headers['X-DNS-Prefetch-Control'] = config.allow ? 'on' : 'off';
204
- };
205
- // https://github.com/helmetjs/ienoopen
206
- helmet.downloadOptions = (headers, config)=>{
207
- headers['X-Download-Options'] = config.action;
208
- };
209
- // https://github.com/helmetjs/frameOptions
210
- helmetHtmlOnly.frameOptions = (headers, config)=>{
211
- headers['X-Frame-Options'] = config.action.toUpperCase();
212
- };
213
- // https://github.com/helmetjs/crossdomain
214
- helmet.permittedCrossDomainPolicies = (headers, config)=>{
215
- headers['X-Permitted-Cross-Domain-Policies'] = config.policy;
216
- };
217
- // https://github.com/helmetjs/hide-powered-by
218
- helmet.poweredBy = (headers, config)=>{
219
- if (config.server) {
220
- headers['X-Powered-By'] = config.server;
221
- } else {
222
- delete headers.Server;
223
- delete headers['X-Powered-By'];
224
- }
225
- };
226
- // https://github.com/helmetjs/x-xss-protection
227
- helmetHtmlOnly.xssProtection = (headers, config)=>{
228
- let header = '1; mode=block';
229
- if (config.reportTo) {
230
- header += '; report=' + config.reportTo;
231
- }
232
- headers['X-XSS-Protection'] = header;
233
- };
234
- const httpSecurityHeadersMiddleware = (opts = {})=>{
235
- const options = {
236
- ...defaults,
237
- ...opts
238
- };
239
- const httpSecurityHeadersMiddlewareAfter = async (request)=>{
240
- (0, _util.normalizeHttpResponse)(request);
241
- Object.keys(helmet).forEach((key)=>{
242
- if (!options[key]) return;
243
- const config = {
244
- ...defaults[key],
245
- ...options[key]
246
- };
247
- helmet[key](request.response.headers, config);
248
- });
249
- if (request.response.headers['Content-Type']?.includes('text/html')) {
250
- Object.keys(helmetHtmlOnly).forEach((key)=>{
251
- if (!options[key]) return;
252
- const config = {
253
- ...defaults[key],
254
- ...options[key]
255
- };
256
- helmetHtmlOnly[key](request.response.headers, config);
257
- });
258
- }
259
- };
260
- const httpSecurityHeadersMiddlewareOnError = async (request)=>{
261
- if (request.response === undefined) return;
262
- await httpSecurityHeadersMiddlewareAfter(request);
263
- };
264
- return {
265
- after: httpSecurityHeadersMiddlewareAfter,
266
- onError: httpSecurityHeadersMiddlewareOnError
267
- };
268
- };
269
- const _default = httpSecurityHeadersMiddleware;
270
-