@hoajs/secure-headers 0.1.0
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.
- package/CHANGELOG.md +3 -0
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/dist/cjs/index.d.cts +170 -0
- package/dist/cjs/index.js +811 -0
- package/dist/esm/index.d.ts +170 -0
- package/dist/esm/index.js +766 -0
- package/package.json +77 -0
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { compose } from "hoa";
|
|
3
|
+
|
|
4
|
+
// src/contentSecurityPolicy.ts
|
|
5
|
+
var dangerouslyDisableDefaultSrc = Symbol("dangerouslyDisableDefaultSrc");
|
|
6
|
+
var SHOULD_BE_QUOTED = /* @__PURE__ */ new Set([
|
|
7
|
+
"none",
|
|
8
|
+
"self",
|
|
9
|
+
"strict-dynamic",
|
|
10
|
+
"report-sample",
|
|
11
|
+
"inline-speculation-rules",
|
|
12
|
+
"unsafe-inline",
|
|
13
|
+
"unsafe-eval",
|
|
14
|
+
"unsafe-hashes",
|
|
15
|
+
"wasm-unsafe-eval"
|
|
16
|
+
]);
|
|
17
|
+
var getDefaultDirectives = () => ({
|
|
18
|
+
"default-src": ["'self'"],
|
|
19
|
+
"base-uri": ["'self'"],
|
|
20
|
+
"font-src": ["'self'", "https:", "data:"],
|
|
21
|
+
"form-action": ["'self'"],
|
|
22
|
+
"frame-ancestors": ["'self'"],
|
|
23
|
+
"img-src": ["'self'", "data:"],
|
|
24
|
+
"object-src": ["'none'"],
|
|
25
|
+
"script-src": ["'self'"],
|
|
26
|
+
"script-src-attr": ["'none'"],
|
|
27
|
+
"style-src": ["'self'", "https:", "'unsafe-inline'"],
|
|
28
|
+
"upgrade-insecure-requests": []
|
|
29
|
+
});
|
|
30
|
+
var dashify = (str) => str.replace(/[A-Z]/g, (capitalLetter) => "-" + capitalLetter.toLowerCase());
|
|
31
|
+
var assertDirectiveValueIsValid = (directiveName, directiveValue) => {
|
|
32
|
+
if (/;|,/.test(directiveValue)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Content-Security-Policy received an invalid directive value for ${JSON.stringify(
|
|
35
|
+
directiveName
|
|
36
|
+
)}`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var assertDirectiveValueEntryIsValid = (directiveName, directiveValueEntry) => {
|
|
41
|
+
if (SHOULD_BE_QUOTED.has(directiveValueEntry) || directiveValueEntry.startsWith("nonce-") || directiveValueEntry.startsWith("sha256-") || directiveValueEntry.startsWith("sha384-") || directiveValueEntry.startsWith("sha512-")) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Content-Security-Policy received an invalid directive value for ${JSON.stringify(
|
|
44
|
+
directiveName
|
|
45
|
+
)}. ${JSON.stringify(directiveValueEntry)} should be quoted`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function normalizeDirectives(options) {
|
|
50
|
+
const defaultDirectives = getDefaultDirectives();
|
|
51
|
+
const { useDefaults = true, directives: rawDirectives = defaultDirectives } = options;
|
|
52
|
+
const result = /* @__PURE__ */ new Map();
|
|
53
|
+
const directiveNamesSeen = /* @__PURE__ */ new Set();
|
|
54
|
+
const directivesExplicitlyDisabled = /* @__PURE__ */ new Set();
|
|
55
|
+
for (const rawDirectiveName in rawDirectives) {
|
|
56
|
+
if (!Object.hasOwn(rawDirectives, rawDirectiveName)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (rawDirectiveName.length === 0 || /[^a-zA-Z0-9-]/.test(rawDirectiveName)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Content-Security-Policy received an invalid directive name ${JSON.stringify(
|
|
62
|
+
rawDirectiveName
|
|
63
|
+
)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const directiveName = dashify(rawDirectiveName);
|
|
67
|
+
if (directiveNamesSeen.has(directiveName)) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Content-Security-Policy received a duplicate directive ${JSON.stringify(
|
|
70
|
+
directiveName
|
|
71
|
+
)}`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
directiveNamesSeen.add(directiveName);
|
|
75
|
+
const rawDirectiveValue = rawDirectives[rawDirectiveName];
|
|
76
|
+
let directiveValue;
|
|
77
|
+
if (rawDirectiveValue === null) {
|
|
78
|
+
if (directiveName === "default-src") {
|
|
79
|
+
throw new Error(
|
|
80
|
+
"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`."
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
directivesExplicitlyDisabled.add(directiveName);
|
|
84
|
+
continue;
|
|
85
|
+
} else if (typeof rawDirectiveValue === "string") {
|
|
86
|
+
directiveValue = [rawDirectiveValue];
|
|
87
|
+
} else if (!rawDirectiveValue) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Content-Security-Policy received an invalid directive value for ${JSON.stringify(
|
|
90
|
+
directiveName
|
|
91
|
+
)}`
|
|
92
|
+
);
|
|
93
|
+
} else if (rawDirectiveValue === dangerouslyDisableDefaultSrc) {
|
|
94
|
+
if (directiveName === "default-src") {
|
|
95
|
+
directivesExplicitlyDisabled.add("default-src");
|
|
96
|
+
continue;
|
|
97
|
+
} else {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Content-Security-Policy: tried to disable ${JSON.stringify(
|
|
100
|
+
directiveName
|
|
101
|
+
)} as if it were default-src; simply omit the key`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
directiveValue = rawDirectiveValue;
|
|
106
|
+
}
|
|
107
|
+
for (const element of directiveValue) {
|
|
108
|
+
if (typeof element !== "string") continue;
|
|
109
|
+
assertDirectiveValueIsValid(directiveName, element);
|
|
110
|
+
assertDirectiveValueEntryIsValid(directiveName, element);
|
|
111
|
+
}
|
|
112
|
+
result.set(directiveName, directiveValue);
|
|
113
|
+
}
|
|
114
|
+
if (useDefaults) {
|
|
115
|
+
Object.entries(defaultDirectives).forEach(
|
|
116
|
+
([defaultDirectiveName, defaultDirectiveValue]) => {
|
|
117
|
+
if (!result.has(defaultDirectiveName) && !directivesExplicitlyDisabled.has(defaultDirectiveName)) {
|
|
118
|
+
result.set(defaultDirectiveName, defaultDirectiveValue);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (!result.size) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
"Content-Security-Policy has no directives. Either set some or disable the header"
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
if (!result.has("default-src") && !directivesExplicitlyDisabled.has("default-src")) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
"Content-Security-Policy needs a default-src but none was provided. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`."
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
function getHeaderValue(ctx, normalizedDirectives) {
|
|
136
|
+
const result = [];
|
|
137
|
+
for (const [directiveName, rawDirectiveValue] of normalizedDirectives) {
|
|
138
|
+
let directiveValue = "";
|
|
139
|
+
for (const element of rawDirectiveValue) {
|
|
140
|
+
if (typeof element === "function") {
|
|
141
|
+
const newElement = element(ctx);
|
|
142
|
+
assertDirectiveValueEntryIsValid(directiveName, newElement);
|
|
143
|
+
directiveValue += " " + newElement;
|
|
144
|
+
} else {
|
|
145
|
+
directiveValue += " " + element;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (directiveValue) {
|
|
149
|
+
assertDirectiveValueIsValid(directiveName, directiveValue);
|
|
150
|
+
result.push(`${directiveName}${directiveValue}`);
|
|
151
|
+
} else {
|
|
152
|
+
result.push(directiveName);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return result.join(";");
|
|
156
|
+
}
|
|
157
|
+
var contentSecurityPolicy = function contentSecurityPolicy2(options = {}) {
|
|
158
|
+
const headerName = options.reportOnly ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy";
|
|
159
|
+
const normalizedDirectives = normalizeDirectives(options);
|
|
160
|
+
return async function contentSecurityPolicyMiddleware(ctx, next) {
|
|
161
|
+
const result = getHeaderValue(ctx, normalizedDirectives);
|
|
162
|
+
ctx.res.set(headerName, result);
|
|
163
|
+
await next();
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
contentSecurityPolicy.getDefaultDirectives = getDefaultDirectives;
|
|
167
|
+
contentSecurityPolicy.dangerouslyDisableDefaultSrc = dangerouslyDisableDefaultSrc;
|
|
168
|
+
var contentSecurityPolicy_default = contentSecurityPolicy;
|
|
169
|
+
|
|
170
|
+
// src/crossOriginEmbedderPolicy.ts
|
|
171
|
+
var ALLOWED_POLICIES = /* @__PURE__ */ new Set([
|
|
172
|
+
"require-corp",
|
|
173
|
+
"credentialless",
|
|
174
|
+
"unsafe-none"
|
|
175
|
+
]);
|
|
176
|
+
function getHeaderValueFromOptions({
|
|
177
|
+
policy = "require-corp"
|
|
178
|
+
}) {
|
|
179
|
+
if (ALLOWED_POLICIES.has(policy)) {
|
|
180
|
+
return policy;
|
|
181
|
+
} else {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Cross-Origin-Embedder-Policy does not support the ${JSON.stringify(
|
|
184
|
+
policy
|
|
185
|
+
)} policy`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function crossOriginEmbedderPolicy(options = {}) {
|
|
190
|
+
const headerValue = getHeaderValueFromOptions(options);
|
|
191
|
+
return async function crossOriginEmbedderPolicyMiddleware(ctx, next) {
|
|
192
|
+
ctx.res.set("Cross-Origin-Embedder-Policy", headerValue);
|
|
193
|
+
await next();
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
var crossOriginEmbedderPolicy_default = crossOriginEmbedderPolicy;
|
|
197
|
+
|
|
198
|
+
// src/crossOriginOpenerPolicy.ts
|
|
199
|
+
var ALLOWED_POLICIES2 = /* @__PURE__ */ new Set(["same-origin", "same-origin-allow-popups", "unsafe-none"]);
|
|
200
|
+
function getHeaderValueFromOptions2({
|
|
201
|
+
policy = "same-origin"
|
|
202
|
+
}) {
|
|
203
|
+
if (ALLOWED_POLICIES2.has(policy)) {
|
|
204
|
+
return policy;
|
|
205
|
+
} else {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Cross-Origin-Opener-Policy does not support the ${JSON.stringify(
|
|
208
|
+
policy
|
|
209
|
+
)} policy`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function crossOriginOpenerPolicy(options = {}) {
|
|
214
|
+
const headerValue = getHeaderValueFromOptions2(options);
|
|
215
|
+
return async function crossOriginOpenerPolicyMiddleware(ctx, next) {
|
|
216
|
+
ctx.res.set("Cross-Origin-Opener-Policy", headerValue);
|
|
217
|
+
await next();
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
var crossOriginOpenerPolicy_default = crossOriginOpenerPolicy;
|
|
221
|
+
|
|
222
|
+
// src/crossOriginResourcePolicy.ts
|
|
223
|
+
var ALLOWED_POLICIES3 = /* @__PURE__ */ new Set(["same-origin", "same-site", "cross-origin"]);
|
|
224
|
+
function getHeaderValueFromOptions3({
|
|
225
|
+
policy = "same-origin"
|
|
226
|
+
}) {
|
|
227
|
+
if (ALLOWED_POLICIES3.has(policy)) {
|
|
228
|
+
return policy;
|
|
229
|
+
} else {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`Cross-Origin-Resource-Policy does not support the ${JSON.stringify(
|
|
232
|
+
policy
|
|
233
|
+
)} policy`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function crossOriginResourcePolicy(options = {}) {
|
|
238
|
+
const headerValue = getHeaderValueFromOptions3(options);
|
|
239
|
+
return async function crossOriginResourcePolicyMiddleware(ctx, next) {
|
|
240
|
+
ctx.res.set("Cross-Origin-Resource-Policy", headerValue);
|
|
241
|
+
await next();
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
var crossOriginResourcePolicy_default = crossOriginResourcePolicy;
|
|
245
|
+
|
|
246
|
+
// src/originAgentCluster.ts
|
|
247
|
+
function originAgentCluster() {
|
|
248
|
+
return async function originAgentClusterMiddleware(ctx, next) {
|
|
249
|
+
ctx.res.set("Origin-Agent-Cluster", "?1");
|
|
250
|
+
await next();
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
var originAgentCluster_default = originAgentCluster;
|
|
254
|
+
|
|
255
|
+
// src/referrerPolicy.ts
|
|
256
|
+
var ALLOWED_TOKENS = /* @__PURE__ */ new Set([
|
|
257
|
+
"no-referrer",
|
|
258
|
+
"no-referrer-when-downgrade",
|
|
259
|
+
"same-origin",
|
|
260
|
+
"origin",
|
|
261
|
+
"strict-origin",
|
|
262
|
+
"origin-when-cross-origin",
|
|
263
|
+
"strict-origin-when-cross-origin",
|
|
264
|
+
"unsafe-url",
|
|
265
|
+
""
|
|
266
|
+
]);
|
|
267
|
+
function getHeaderValueFromOptions4({
|
|
268
|
+
policy = ["no-referrer"]
|
|
269
|
+
}) {
|
|
270
|
+
const tokens = typeof policy === "string" ? [policy] : policy;
|
|
271
|
+
if (tokens.length === 0) {
|
|
272
|
+
throw new Error("Referrer-Policy received no policy tokens");
|
|
273
|
+
}
|
|
274
|
+
const tokensSeen = /* @__PURE__ */ new Set();
|
|
275
|
+
tokens.forEach((token) => {
|
|
276
|
+
if (!ALLOWED_TOKENS.has(token)) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`Referrer-Policy received an unexpected policy token ${JSON.stringify(
|
|
279
|
+
token
|
|
280
|
+
)}`
|
|
281
|
+
);
|
|
282
|
+
} else if (tokensSeen.has(token)) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`Referrer-Policy received a duplicate policy token ${JSON.stringify(
|
|
285
|
+
token
|
|
286
|
+
)}`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
tokensSeen.add(token);
|
|
290
|
+
});
|
|
291
|
+
return tokens.join(",");
|
|
292
|
+
}
|
|
293
|
+
function referrerPolicy(options = {}) {
|
|
294
|
+
const headerValue = getHeaderValueFromOptions4(options);
|
|
295
|
+
return async function referrerPolicyMiddleware(ctx, next) {
|
|
296
|
+
ctx.res.set("Referrer-Policy", headerValue);
|
|
297
|
+
await next();
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
var referrerPolicy_default = referrerPolicy;
|
|
301
|
+
|
|
302
|
+
// src/strictTransportSecurity.ts
|
|
303
|
+
var DEFAULT_MAX_AGE = 365 * 24 * 60 * 60;
|
|
304
|
+
function parseMaxAge(value = DEFAULT_MAX_AGE) {
|
|
305
|
+
if (value >= 0 && Number.isFinite(value)) {
|
|
306
|
+
return Math.floor(value);
|
|
307
|
+
} else {
|
|
308
|
+
throw new Error(
|
|
309
|
+
`Strict-Transport-Security: ${JSON.stringify(
|
|
310
|
+
value
|
|
311
|
+
)} is not a valid value for maxAge. Please choose a positive integer.`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function getHeaderValueFromOptions5(options) {
|
|
316
|
+
if ("maxage" in options) {
|
|
317
|
+
throw new Error(
|
|
318
|
+
"Strict-Transport-Security received an unsupported property, `maxage`. Did you mean to pass `maxAge`?"
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
if ("includeSubdomains" in options) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
'Strict-Transport-Security middleware should use `includeSubDomains` instead of `includeSubdomains`. (The correct one has an uppercase "D".)'
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
const directives = [`max-age=${parseMaxAge(options.maxAge)}`];
|
|
327
|
+
if (options.includeSubDomains === void 0 || options.includeSubDomains) {
|
|
328
|
+
directives.push("includeSubDomains");
|
|
329
|
+
}
|
|
330
|
+
if (options.preload) {
|
|
331
|
+
directives.push("preload");
|
|
332
|
+
}
|
|
333
|
+
return directives.join("; ");
|
|
334
|
+
}
|
|
335
|
+
function strictTransportSecurity(options = {}) {
|
|
336
|
+
const headerValue = getHeaderValueFromOptions5(options);
|
|
337
|
+
return async function strictTransportSecurityMiddleware(ctx, next) {
|
|
338
|
+
ctx.res.set("Strict-Transport-Security", headerValue);
|
|
339
|
+
await next();
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
var strictTransportSecurity_default = strictTransportSecurity;
|
|
343
|
+
|
|
344
|
+
// src/xContentTypeOptions.ts
|
|
345
|
+
function xContentTypeOptions() {
|
|
346
|
+
return async function xContentTypeOptionsMiddleware(ctx, next) {
|
|
347
|
+
ctx.res.set("X-Content-Type-Options", "nosniff");
|
|
348
|
+
await next();
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
var xContentTypeOptions_default = xContentTypeOptions;
|
|
352
|
+
|
|
353
|
+
// src/xDnsPrefetchControl.ts
|
|
354
|
+
function xDnsPrefetchControl(options = {}) {
|
|
355
|
+
const headerValue = options.allow ? "on" : "off";
|
|
356
|
+
return async function xDnsPrefetchControlMiddleware(ctx, next) {
|
|
357
|
+
ctx.res.set("X-DNS-Prefetch-Control", headerValue);
|
|
358
|
+
await next();
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
var xDnsPrefetchControl_default = xDnsPrefetchControl;
|
|
362
|
+
|
|
363
|
+
// src/xDownloadOptions.ts
|
|
364
|
+
function xDownloadOptions() {
|
|
365
|
+
return async function xDownloadOptionsMiddleware(ctx, next) {
|
|
366
|
+
ctx.res.set("X-Download-Options", "noopen");
|
|
367
|
+
await next();
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
var xDownloadOptions_default = xDownloadOptions;
|
|
371
|
+
|
|
372
|
+
// src/xFrameOptions.ts
|
|
373
|
+
function getHeaderValueFromOptions6({
|
|
374
|
+
action = "sameorigin"
|
|
375
|
+
}) {
|
|
376
|
+
const normalizedAction = typeof action === "string" ? action.toUpperCase() : action;
|
|
377
|
+
switch (normalizedAction) {
|
|
378
|
+
case "SAME-ORIGIN":
|
|
379
|
+
return "SAMEORIGIN";
|
|
380
|
+
case "DENY":
|
|
381
|
+
case "SAMEORIGIN":
|
|
382
|
+
return normalizedAction;
|
|
383
|
+
default:
|
|
384
|
+
throw new Error(
|
|
385
|
+
`X-Frame-Options received an invalid action ${JSON.stringify(action)}`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function xFrameOptions(options = {}) {
|
|
390
|
+
const headerValue = getHeaderValueFromOptions6(options);
|
|
391
|
+
return async function xFrameOptionsMiddleware(ctx, next) {
|
|
392
|
+
ctx.res.set("X-Frame-Options", headerValue);
|
|
393
|
+
await next();
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
var xFrameOptions_default = xFrameOptions;
|
|
397
|
+
|
|
398
|
+
// src/xPermittedCrossDomainPolicies.ts
|
|
399
|
+
var ALLOWED_PERMITTED_POLICIES = /* @__PURE__ */ new Set([
|
|
400
|
+
"none",
|
|
401
|
+
"master-only",
|
|
402
|
+
"by-content-type",
|
|
403
|
+
"all"
|
|
404
|
+
]);
|
|
405
|
+
function getHeaderValueFromOptions7({
|
|
406
|
+
permittedPolicies = "none"
|
|
407
|
+
}) {
|
|
408
|
+
if (ALLOWED_PERMITTED_POLICIES.has(permittedPolicies)) {
|
|
409
|
+
return permittedPolicies;
|
|
410
|
+
} else {
|
|
411
|
+
throw new Error(
|
|
412
|
+
`X-Permitted-Cross-Domain-Policies does not support ${JSON.stringify(
|
|
413
|
+
permittedPolicies
|
|
414
|
+
)}`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function xPermittedCrossDomainPolicies(options = {}) {
|
|
419
|
+
const headerValue = getHeaderValueFromOptions7(options);
|
|
420
|
+
return async function xPermittedCrossDomainPoliciesMiddleware(ctx, next) {
|
|
421
|
+
ctx.res.set("X-Permitted-Cross-Domain-Policies", headerValue);
|
|
422
|
+
await next();
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
var xPermittedCrossDomainPolicies_default = xPermittedCrossDomainPolicies;
|
|
426
|
+
|
|
427
|
+
// src/xXssProtection.ts
|
|
428
|
+
function xXssProtection() {
|
|
429
|
+
return async function xXssProtectionMiddleware(ctx, next) {
|
|
430
|
+
ctx.res.set("X-XSS-Protection", "0");
|
|
431
|
+
await next();
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
var xXssProtection_default = xXssProtection;
|
|
435
|
+
|
|
436
|
+
// src/permissionPolicy.ts
|
|
437
|
+
function dashify2(str) {
|
|
438
|
+
return str.replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
|
|
439
|
+
}
|
|
440
|
+
var SHOULD_NOT_BE_QUOTED = /* @__PURE__ */ new Set(["*", "none", "self", "src"]);
|
|
441
|
+
function normalizeDirectives2(options) {
|
|
442
|
+
const directiveNamesSeen = /* @__PURE__ */ new Set();
|
|
443
|
+
const result = {};
|
|
444
|
+
for (const rawDirectiveName in options) {
|
|
445
|
+
const directiveName = dashify2(rawDirectiveName);
|
|
446
|
+
if (directiveNamesSeen.has(directiveName)) {
|
|
447
|
+
throw new Error(`Permission-Policy received a duplicate directive ${JSON.stringify(
|
|
448
|
+
directiveName
|
|
449
|
+
)}`);
|
|
450
|
+
}
|
|
451
|
+
directiveNamesSeen.add(directiveName);
|
|
452
|
+
const rawDirectiveValue = options[rawDirectiveName];
|
|
453
|
+
if (typeof rawDirectiveValue === "boolean") {
|
|
454
|
+
result[directiveName] = rawDirectiveValue ? "(*)" : "()";
|
|
455
|
+
} else if (Array.isArray(rawDirectiveValue)) {
|
|
456
|
+
if (rawDirectiveValue.length === 0) {
|
|
457
|
+
result[directiveName] = "()";
|
|
458
|
+
} else if (rawDirectiveValue.length === 1 && (rawDirectiveValue[0] === "*" || rawDirectiveValue[0] === "none")) {
|
|
459
|
+
result[directiveName] = `(${rawDirectiveValue[0]})`;
|
|
460
|
+
} else {
|
|
461
|
+
const allowList = rawDirectiveValue.map((v) => SHOULD_NOT_BE_QUOTED.has(v) ? v : `"${v}"`);
|
|
462
|
+
result[directiveName] = `(${allowList.join(" ")})`;
|
|
463
|
+
}
|
|
464
|
+
} else {
|
|
465
|
+
throw new Error(
|
|
466
|
+
`Permission-Policy received an invalid directive value for ${JSON.stringify(
|
|
467
|
+
directiveName
|
|
468
|
+
)}. ${JSON.stringify(rawDirectiveValue)} should be a boolean or an array of strings.`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (Object.keys(result).length === 0) {
|
|
473
|
+
throw new Error("Permission-Policy has no directives. Either set some or disable the header");
|
|
474
|
+
}
|
|
475
|
+
return result;
|
|
476
|
+
}
|
|
477
|
+
function getHeaderValue2(normalizedDirectives) {
|
|
478
|
+
return Object.entries(normalizedDirectives).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
479
|
+
}
|
|
480
|
+
function permissionPolicy(options = {}) {
|
|
481
|
+
const normalizedDirectives = normalizeDirectives2(options);
|
|
482
|
+
const headerValue = getHeaderValue2(normalizedDirectives);
|
|
483
|
+
return async function permissionPolicyMiddleware(ctx, next) {
|
|
484
|
+
ctx.res.set("Permissions-Policy", headerValue);
|
|
485
|
+
await next();
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
var permissionPolicy_default = permissionPolicy;
|
|
489
|
+
|
|
490
|
+
// src/index.ts
|
|
491
|
+
function getMiddlewareFunctionsFromOptions(options) {
|
|
492
|
+
const result = [];
|
|
493
|
+
switch (options.contentSecurityPolicy) {
|
|
494
|
+
case void 0:
|
|
495
|
+
case true:
|
|
496
|
+
result.push(contentSecurityPolicy_default());
|
|
497
|
+
break;
|
|
498
|
+
case false:
|
|
499
|
+
break;
|
|
500
|
+
default:
|
|
501
|
+
result.push(contentSecurityPolicy_default(options.contentSecurityPolicy));
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
switch (options.crossOriginEmbedderPolicy) {
|
|
505
|
+
case void 0:
|
|
506
|
+
case false:
|
|
507
|
+
break;
|
|
508
|
+
case true:
|
|
509
|
+
result.push(crossOriginEmbedderPolicy_default());
|
|
510
|
+
break;
|
|
511
|
+
default:
|
|
512
|
+
result.push(crossOriginEmbedderPolicy_default(options.crossOriginEmbedderPolicy));
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
switch (options.crossOriginOpenerPolicy) {
|
|
516
|
+
case void 0:
|
|
517
|
+
case true:
|
|
518
|
+
result.push(crossOriginOpenerPolicy_default());
|
|
519
|
+
break;
|
|
520
|
+
case false:
|
|
521
|
+
break;
|
|
522
|
+
default:
|
|
523
|
+
result.push(crossOriginOpenerPolicy_default(options.crossOriginOpenerPolicy));
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
switch (options.crossOriginResourcePolicy) {
|
|
527
|
+
case void 0:
|
|
528
|
+
case true:
|
|
529
|
+
result.push(crossOriginResourcePolicy_default());
|
|
530
|
+
break;
|
|
531
|
+
case false:
|
|
532
|
+
break;
|
|
533
|
+
default:
|
|
534
|
+
result.push(crossOriginResourcePolicy_default(options.crossOriginResourcePolicy));
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
switch (options.originAgentCluster) {
|
|
538
|
+
case void 0:
|
|
539
|
+
case true:
|
|
540
|
+
result.push(originAgentCluster_default());
|
|
541
|
+
break;
|
|
542
|
+
case false:
|
|
543
|
+
break;
|
|
544
|
+
default:
|
|
545
|
+
result.push(originAgentCluster_default());
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
switch (options.referrerPolicy) {
|
|
549
|
+
case void 0:
|
|
550
|
+
case true:
|
|
551
|
+
result.push(referrerPolicy_default());
|
|
552
|
+
break;
|
|
553
|
+
case false:
|
|
554
|
+
break;
|
|
555
|
+
default:
|
|
556
|
+
result.push(referrerPolicy_default(options.referrerPolicy));
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
if ("strictTransportSecurity" in options && "hsts" in options) {
|
|
560
|
+
throw new Error(
|
|
561
|
+
"Strict-Transport-Security option was specified twice. Remove the `hsts` option to fix this error."
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
const strictTransportSecurityOption = options.strictTransportSecurity ?? options.hsts;
|
|
565
|
+
switch (strictTransportSecurityOption) {
|
|
566
|
+
case void 0:
|
|
567
|
+
case true:
|
|
568
|
+
result.push(strictTransportSecurity_default());
|
|
569
|
+
break;
|
|
570
|
+
case false:
|
|
571
|
+
break;
|
|
572
|
+
default:
|
|
573
|
+
result.push(strictTransportSecurity_default(strictTransportSecurityOption));
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
576
|
+
if ("xContentTypeOptions" in options && "noSniff" in options) {
|
|
577
|
+
throw new Error(
|
|
578
|
+
"X-Content-Type-Options option was specified twice. Remove the `noSniff` option to fix this error."
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
const xContentTypeOptionsOption = options.xContentTypeOptions ?? options.noSniff;
|
|
582
|
+
switch (xContentTypeOptionsOption) {
|
|
583
|
+
case void 0:
|
|
584
|
+
case true:
|
|
585
|
+
result.push(xContentTypeOptions_default());
|
|
586
|
+
break;
|
|
587
|
+
case false:
|
|
588
|
+
break;
|
|
589
|
+
default:
|
|
590
|
+
result.push(xContentTypeOptions_default());
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
if ("xDnsPrefetchControl" in options && "dnsPrefetchControl" in options) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
"X-DNS-Prefetch-Control option was specified twice. Remove the `dnsPrefetchControl` option to fix this error."
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const xDnsPrefetchControlOption = options.xDnsPrefetchControl ?? options.dnsPrefetchControl;
|
|
599
|
+
switch (xDnsPrefetchControlOption) {
|
|
600
|
+
case void 0:
|
|
601
|
+
case true:
|
|
602
|
+
result.push(xDnsPrefetchControl_default());
|
|
603
|
+
break;
|
|
604
|
+
case false:
|
|
605
|
+
break;
|
|
606
|
+
default:
|
|
607
|
+
result.push(xDnsPrefetchControl_default(xDnsPrefetchControlOption));
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
if ("xDownloadOptions" in options && "ieNoOpen" in options) {
|
|
611
|
+
throw new Error(
|
|
612
|
+
"X-Download-Options option was specified twice. Remove the `ieNoOpen` option to fix this error."
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
const xDownloadOptionsOption = options.xDownloadOptions ?? options.ieNoOpen;
|
|
616
|
+
switch (xDownloadOptionsOption) {
|
|
617
|
+
case void 0:
|
|
618
|
+
case true:
|
|
619
|
+
result.push(xDownloadOptions_default());
|
|
620
|
+
break;
|
|
621
|
+
case false:
|
|
622
|
+
break;
|
|
623
|
+
default:
|
|
624
|
+
result.push(xDownloadOptions_default());
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
if ("xFrameOptions" in options && "frameguard" in options) {
|
|
628
|
+
throw new Error(
|
|
629
|
+
"X-Frame-Options option was specified twice. Remove the `frameguard` option to fix this error."
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
const xFrameOptionsOption = options.xFrameOptions ?? options.frameguard;
|
|
633
|
+
switch (xFrameOptionsOption) {
|
|
634
|
+
case void 0:
|
|
635
|
+
case true:
|
|
636
|
+
result.push(xFrameOptions_default());
|
|
637
|
+
break;
|
|
638
|
+
case false:
|
|
639
|
+
break;
|
|
640
|
+
default:
|
|
641
|
+
result.push(xFrameOptions_default(xFrameOptionsOption));
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
if ("xPermittedCrossDomainPolicies" in options && "permittedCrossDomainPolicies" in options) {
|
|
645
|
+
throw new Error(
|
|
646
|
+
"X-Permitted-Cross-Domain-Policies option was specified twice. Remove the `permittedCrossDomainPolicies` option to fix this error."
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
const xPermittedCrossDomainPoliciesOption = options.xPermittedCrossDomainPolicies ?? options.permittedCrossDomainPolicies;
|
|
650
|
+
switch (xPermittedCrossDomainPoliciesOption) {
|
|
651
|
+
case void 0:
|
|
652
|
+
case true:
|
|
653
|
+
result.push(xPermittedCrossDomainPolicies_default());
|
|
654
|
+
break;
|
|
655
|
+
case false:
|
|
656
|
+
break;
|
|
657
|
+
default:
|
|
658
|
+
result.push(
|
|
659
|
+
xPermittedCrossDomainPolicies_default(xPermittedCrossDomainPoliciesOption)
|
|
660
|
+
);
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
if ("xPoweredBy" in options && "hidePoweredBy" in options) {
|
|
664
|
+
throw new Error(
|
|
665
|
+
"X-Powered-By option was specified twice. Remove the `hidePoweredBy` option to fix this error."
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
const xPoweredByOption = options.xPoweredBy ?? options.hidePoweredBy;
|
|
669
|
+
const xPoweredBy = function xPoweredBy2() {
|
|
670
|
+
return async function xPoweredByMiddleware(ctx, next) {
|
|
671
|
+
ctx.res.delete("X-Powered-By");
|
|
672
|
+
await next();
|
|
673
|
+
};
|
|
674
|
+
};
|
|
675
|
+
switch (xPoweredByOption) {
|
|
676
|
+
case void 0:
|
|
677
|
+
case true:
|
|
678
|
+
result.push(xPoweredBy());
|
|
679
|
+
break;
|
|
680
|
+
case false:
|
|
681
|
+
break;
|
|
682
|
+
default:
|
|
683
|
+
result.push(xPoweredBy());
|
|
684
|
+
break;
|
|
685
|
+
}
|
|
686
|
+
if ("xXssProtection" in options && "xssFilter" in options) {
|
|
687
|
+
throw new Error(
|
|
688
|
+
"X-XSS-Protection option was specified twice. Remove the `xssFilter` option to fix this error."
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
const xXssProtectionOption = options.xXssProtection ?? options.xssFilter;
|
|
692
|
+
switch (xXssProtectionOption) {
|
|
693
|
+
case void 0:
|
|
694
|
+
case true:
|
|
695
|
+
result.push(xXssProtection_default());
|
|
696
|
+
break;
|
|
697
|
+
case false:
|
|
698
|
+
break;
|
|
699
|
+
default:
|
|
700
|
+
result.push(xXssProtection_default());
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
if (options.permissionPolicy && typeof options.permissionPolicy === "object") {
|
|
704
|
+
result.push(permissionPolicy_default(options.permissionPolicy));
|
|
705
|
+
}
|
|
706
|
+
return result;
|
|
707
|
+
}
|
|
708
|
+
var secureHeaders = Object.assign(
|
|
709
|
+
function secureHeaders2(options = {}) {
|
|
710
|
+
const middlewareFunctions = getMiddlewareFunctionsFromOptions(options);
|
|
711
|
+
const secureHeadersHandler = compose(middlewareFunctions);
|
|
712
|
+
return async function secureHeadersMiddleware(ctx, next) {
|
|
713
|
+
await secureHeadersHandler(ctx, next);
|
|
714
|
+
};
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
contentSecurityPolicy: contentSecurityPolicy_default,
|
|
718
|
+
crossOriginEmbedderPolicy: crossOriginEmbedderPolicy_default,
|
|
719
|
+
crossOriginOpenerPolicy: crossOriginOpenerPolicy_default,
|
|
720
|
+
crossOriginResourcePolicy: crossOriginResourcePolicy_default,
|
|
721
|
+
originAgentCluster: originAgentCluster_default,
|
|
722
|
+
referrerPolicy: referrerPolicy_default,
|
|
723
|
+
strictTransportSecurity: strictTransportSecurity_default,
|
|
724
|
+
xContentTypeOptions: xContentTypeOptions_default,
|
|
725
|
+
xDnsPrefetchControl: xDnsPrefetchControl_default,
|
|
726
|
+
xDownloadOptions: xDownloadOptions_default,
|
|
727
|
+
xFrameOptions: xFrameOptions_default,
|
|
728
|
+
xPermittedCrossDomainPolicies: xPermittedCrossDomainPolicies_default,
|
|
729
|
+
xXssProtection: xXssProtection_default,
|
|
730
|
+
permissionPolicy: permissionPolicy_default,
|
|
731
|
+
// Legacy aliases
|
|
732
|
+
dnsPrefetchControl: xDnsPrefetchControl_default,
|
|
733
|
+
xssFilter: xXssProtection_default,
|
|
734
|
+
permittedCrossDomainPolicies: xPermittedCrossDomainPolicies_default,
|
|
735
|
+
ieNoOpen: xDownloadOptions_default,
|
|
736
|
+
noSniff: xContentTypeOptions_default,
|
|
737
|
+
frameguard: xFrameOptions_default,
|
|
738
|
+
hsts: strictTransportSecurity_default
|
|
739
|
+
}
|
|
740
|
+
);
|
|
741
|
+
var index_default = secureHeaders;
|
|
742
|
+
export {
|
|
743
|
+
contentSecurityPolicy_default as contentSecurityPolicy,
|
|
744
|
+
crossOriginEmbedderPolicy_default as crossOriginEmbedderPolicy,
|
|
745
|
+
crossOriginOpenerPolicy_default as crossOriginOpenerPolicy,
|
|
746
|
+
crossOriginResourcePolicy_default as crossOriginResourcePolicy,
|
|
747
|
+
index_default as default,
|
|
748
|
+
xDnsPrefetchControl_default as dnsPrefetchControl,
|
|
749
|
+
xFrameOptions_default as frameguard,
|
|
750
|
+
strictTransportSecurity_default as hsts,
|
|
751
|
+
xDownloadOptions_default as ieNoOpen,
|
|
752
|
+
xContentTypeOptions_default as noSniff,
|
|
753
|
+
originAgentCluster_default as originAgentCluster,
|
|
754
|
+
permissionPolicy_default as permissionPolicy,
|
|
755
|
+
xPermittedCrossDomainPolicies_default as permittedCrossDomainPolicies,
|
|
756
|
+
referrerPolicy_default as referrerPolicy,
|
|
757
|
+
secureHeaders,
|
|
758
|
+
strictTransportSecurity_default as strictTransportSecurity,
|
|
759
|
+
xContentTypeOptions_default as xContentTypeOptions,
|
|
760
|
+
xDnsPrefetchControl_default as xDnsPrefetchControl,
|
|
761
|
+
xDownloadOptions_default as xDownloadOptions,
|
|
762
|
+
xFrameOptions_default as xFrameOptions,
|
|
763
|
+
xPermittedCrossDomainPolicies_default as xPermittedCrossDomainPolicies,
|
|
764
|
+
xXssProtection_default as xXssProtection,
|
|
765
|
+
xXssProtection_default as xssFilter
|
|
766
|
+
};
|