@sveltejs/kit 1.0.0-next.371 → 1.0.0-next.372
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/assets/server/index.js +118 -54
- package/dist/chunks/error.js +37 -34
- package/dist/cli.js +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +1 -0
package/assets/server/index.js
CHANGED
|
@@ -27,32 +27,34 @@ function to_headers(object) {
|
|
|
27
27
|
* @param {string[]} types
|
|
28
28
|
*/
|
|
29
29
|
function negotiate(accept, types) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
.map((str, i) => {
|
|
33
|
-
const match = /([^/]+)\/([^;]+)(?:;q=([0-9.]+))?/.exec(str);
|
|
34
|
-
if (match) {
|
|
35
|
-
const [, type, subtype, q = '1'] = match;
|
|
36
|
-
return { type, subtype, q: +q, i };
|
|
37
|
-
}
|
|
30
|
+
/** @type {Array<{ type: string, subtype: string, q: number, i: number }>} */
|
|
31
|
+
const parts = [];
|
|
38
32
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.sort((a, b) => {
|
|
42
|
-
if (a.q !== b.q) {
|
|
43
|
-
return b.q - a.q;
|
|
44
|
-
}
|
|
33
|
+
accept.split(',').forEach((str, i) => {
|
|
34
|
+
const match = /([^/]+)\/([^;]+)(?:;q=([0-9.]+))?/.exec(str);
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
// no match equals invalid header — ignore
|
|
37
|
+
if (match) {
|
|
38
|
+
const [, type, subtype, q = '1'] = match;
|
|
39
|
+
parts.push({ type, subtype, q: +q, i });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
49
42
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
parts.sort((a, b) => {
|
|
44
|
+
if (a.q !== b.q) {
|
|
45
|
+
return b.q - a.q;
|
|
46
|
+
}
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
if ((a.subtype === '*') !== (b.subtype === '*')) {
|
|
49
|
+
return a.subtype === '*' ? 1 : -1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if ((a.type === '*') !== (b.type === '*')) {
|
|
53
|
+
return a.type === '*' ? 1 : -1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return a.i - b.i;
|
|
57
|
+
});
|
|
56
58
|
|
|
57
59
|
let accepted;
|
|
58
60
|
let min_priority = Infinity;
|
|
@@ -975,9 +977,6 @@ function base64(bytes) {
|
|
|
975
977
|
return result;
|
|
976
978
|
}
|
|
977
979
|
|
|
978
|
-
/** @type {Promise<void>} */
|
|
979
|
-
let csp_ready;
|
|
980
|
-
|
|
981
980
|
const array = new Uint8Array(16);
|
|
982
981
|
|
|
983
982
|
function generate_nonce() {
|
|
@@ -997,13 +996,12 @@ const quoted = new Set([
|
|
|
997
996
|
|
|
998
997
|
const crypto_pattern = /^(nonce|sha\d\d\d)-/;
|
|
999
998
|
|
|
1000
|
-
|
|
999
|
+
// CSP and CSP Report Only are extremely similar with a few caveats
|
|
1000
|
+
// the easiest/DRYest way to express this is with some private encapsulation
|
|
1001
|
+
class BaseProvider {
|
|
1001
1002
|
/** @type {boolean} */
|
|
1002
1003
|
#use_hashes;
|
|
1003
1004
|
|
|
1004
|
-
/** @type {boolean} */
|
|
1005
|
-
#dev;
|
|
1006
|
-
|
|
1007
1005
|
/** @type {boolean} */
|
|
1008
1006
|
#script_needs_csp;
|
|
1009
1007
|
|
|
@@ -1019,21 +1017,18 @@ class Csp {
|
|
|
1019
1017
|
/** @type {import('types').Csp.Source[]} */
|
|
1020
1018
|
#style_src;
|
|
1021
1019
|
|
|
1020
|
+
/** @type {string} */
|
|
1021
|
+
#nonce;
|
|
1022
|
+
|
|
1022
1023
|
/**
|
|
1023
|
-
* @param {
|
|
1024
|
-
*
|
|
1025
|
-
*
|
|
1026
|
-
* }
|
|
1027
|
-
* @param {{
|
|
1028
|
-
* dev: boolean;
|
|
1029
|
-
* prerender: boolean;
|
|
1030
|
-
* needs_nonce: boolean;
|
|
1031
|
-
* }} opts
|
|
1024
|
+
* @param {boolean} use_hashes
|
|
1025
|
+
* @param {import('types').CspDirectives} directives
|
|
1026
|
+
* @param {string} nonce
|
|
1027
|
+
* @param {boolean} dev
|
|
1032
1028
|
*/
|
|
1033
|
-
constructor(
|
|
1034
|
-
this.#use_hashes =
|
|
1029
|
+
constructor(use_hashes, directives, nonce, dev) {
|
|
1030
|
+
this.#use_hashes = use_hashes;
|
|
1035
1031
|
this.#directives = dev ? { ...directives } : directives; // clone in dev so we can safely mutate
|
|
1036
|
-
this.#dev = dev;
|
|
1037
1032
|
|
|
1038
1033
|
const d = this.#directives;
|
|
1039
1034
|
|
|
@@ -1075,10 +1070,7 @@ class Csp {
|
|
|
1075
1070
|
|
|
1076
1071
|
this.script_needs_nonce = this.#script_needs_csp && !this.#use_hashes;
|
|
1077
1072
|
this.style_needs_nonce = this.#style_needs_csp && !this.#use_hashes;
|
|
1078
|
-
|
|
1079
|
-
if (this.script_needs_nonce || this.style_needs_nonce || needs_nonce) {
|
|
1080
|
-
this.nonce = generate_nonce();
|
|
1081
|
-
}
|
|
1073
|
+
this.#nonce = nonce;
|
|
1082
1074
|
}
|
|
1083
1075
|
|
|
1084
1076
|
/** @param {string} content */
|
|
@@ -1087,7 +1079,7 @@ class Csp {
|
|
|
1087
1079
|
if (this.#use_hashes) {
|
|
1088
1080
|
this.#script_src.push(`sha256-${sha256(content)}`);
|
|
1089
1081
|
} else if (this.#script_src.length === 0) {
|
|
1090
|
-
this.#script_src.push(`nonce-${this
|
|
1082
|
+
this.#script_src.push(`nonce-${this.#nonce}`);
|
|
1091
1083
|
}
|
|
1092
1084
|
}
|
|
1093
1085
|
}
|
|
@@ -1098,12 +1090,14 @@ class Csp {
|
|
|
1098
1090
|
if (this.#use_hashes) {
|
|
1099
1091
|
this.#style_src.push(`sha256-${sha256(content)}`);
|
|
1100
1092
|
} else if (this.#style_src.length === 0) {
|
|
1101
|
-
this.#style_src.push(`nonce-${this
|
|
1093
|
+
this.#style_src.push(`nonce-${this.#nonce}`);
|
|
1102
1094
|
}
|
|
1103
1095
|
}
|
|
1104
1096
|
}
|
|
1105
1097
|
|
|
1106
|
-
/**
|
|
1098
|
+
/**
|
|
1099
|
+
* @param {boolean} [is_meta]
|
|
1100
|
+
*/
|
|
1107
1101
|
get_header(is_meta = false) {
|
|
1108
1102
|
const header = [];
|
|
1109
1103
|
|
|
@@ -1134,7 +1128,7 @@ class Csp {
|
|
|
1134
1128
|
continue;
|
|
1135
1129
|
}
|
|
1136
1130
|
|
|
1137
|
-
// @ts-expect-error gimme a break typescript, `key` is obviously a member of
|
|
1131
|
+
// @ts-expect-error gimme a break typescript, `key` is obviously a member of internal_directives
|
|
1138
1132
|
const value = /** @type {string[] | true} */ (directives[key]);
|
|
1139
1133
|
|
|
1140
1134
|
if (!value) continue;
|
|
@@ -1155,13 +1149,81 @@ class Csp {
|
|
|
1155
1149
|
|
|
1156
1150
|
return header.join('; ');
|
|
1157
1151
|
}
|
|
1152
|
+
}
|
|
1158
1153
|
|
|
1154
|
+
class CspProvider extends BaseProvider {
|
|
1159
1155
|
get_meta() {
|
|
1160
1156
|
const content = escape_html_attr(this.get_header(true));
|
|
1161
1157
|
return `<meta http-equiv="content-security-policy" content=${content}>`;
|
|
1162
1158
|
}
|
|
1163
1159
|
}
|
|
1164
1160
|
|
|
1161
|
+
class CspReportOnlyProvider extends BaseProvider {
|
|
1162
|
+
/**
|
|
1163
|
+
* @param {boolean} use_hashes
|
|
1164
|
+
* @param {import('types').CspDirectives} directives
|
|
1165
|
+
* @param {string} nonce
|
|
1166
|
+
* @param {boolean} dev
|
|
1167
|
+
*/
|
|
1168
|
+
constructor(use_hashes, directives, nonce, dev) {
|
|
1169
|
+
super(use_hashes, directives, nonce, dev);
|
|
1170
|
+
|
|
1171
|
+
if (Object.values(directives).filter((v) => !!v).length > 0) {
|
|
1172
|
+
// If we're generating content-security-policy-report-only,
|
|
1173
|
+
// if there are any directives, we need a report-uri or report-to (or both)
|
|
1174
|
+
// else it's just an expensive noop.
|
|
1175
|
+
const has_report_to = directives['report-to']?.length ?? 0 > 0;
|
|
1176
|
+
const has_report_uri = directives['report-uri']?.length ?? 0 > 0;
|
|
1177
|
+
if (!has_report_to && !has_report_uri) {
|
|
1178
|
+
throw Error(
|
|
1179
|
+
'`content-security-policy-report-only` must be specified with either the `report-to` or `report-uri` directives, or both'
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
class Csp {
|
|
1187
|
+
/** @readonly */
|
|
1188
|
+
nonce = generate_nonce();
|
|
1189
|
+
|
|
1190
|
+
/** @type {CspProvider} */
|
|
1191
|
+
csp_provider;
|
|
1192
|
+
|
|
1193
|
+
/** @type {CspReportOnlyProvider} */
|
|
1194
|
+
report_only_provider;
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* @param {import('./types').CspConfig} config
|
|
1198
|
+
* @param {import('./types').CspOpts} opts
|
|
1199
|
+
*/
|
|
1200
|
+
constructor({ mode, directives, reportOnly }, { prerender, dev }) {
|
|
1201
|
+
const use_hashes = mode === 'hash' || (mode === 'auto' && prerender);
|
|
1202
|
+
this.csp_provider = new CspProvider(use_hashes, directives, this.nonce, dev);
|
|
1203
|
+
this.report_only_provider = new CspReportOnlyProvider(use_hashes, reportOnly, this.nonce, dev);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
get script_needs_nonce() {
|
|
1207
|
+
return this.csp_provider.script_needs_nonce || this.report_only_provider.script_needs_nonce;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
get style_needs_nonce() {
|
|
1211
|
+
return this.csp_provider.style_needs_nonce || this.report_only_provider.style_needs_nonce;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/** @param {string} content */
|
|
1215
|
+
add_script(content) {
|
|
1216
|
+
this.csp_provider.add_script(content);
|
|
1217
|
+
this.report_only_provider.add_script(content);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/** @param {string} content */
|
|
1221
|
+
add_style(content) {
|
|
1222
|
+
this.csp_provider.add_style(content);
|
|
1223
|
+
this.report_only_provider.add_style(content);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1165
1227
|
const absolute = /^([a-z]+:)?\/?\//;
|
|
1166
1228
|
const scheme = /^[a-z]+:/;
|
|
1167
1229
|
|
|
@@ -1387,11 +1449,9 @@ async function render_response({
|
|
|
1387
1449
|
|
|
1388
1450
|
let { head, html: body } = rendered;
|
|
1389
1451
|
|
|
1390
|
-
await csp_ready;
|
|
1391
1452
|
const csp = new Csp(options.csp, {
|
|
1392
1453
|
dev: options.dev,
|
|
1393
|
-
prerender: !!state.prerendering
|
|
1394
|
-
needs_nonce: options.template_contains_nonce
|
|
1454
|
+
prerender: !!state.prerendering
|
|
1395
1455
|
});
|
|
1396
1456
|
|
|
1397
1457
|
const target = hash(body);
|
|
@@ -1502,7 +1562,7 @@ async function render_response({
|
|
|
1502
1562
|
if (state.prerendering) {
|
|
1503
1563
|
const http_equiv = [];
|
|
1504
1564
|
|
|
1505
|
-
const csp_headers = csp.get_meta();
|
|
1565
|
+
const csp_headers = csp.csp_provider.get_meta();
|
|
1506
1566
|
if (csp_headers) {
|
|
1507
1567
|
http_equiv.push(csp_headers);
|
|
1508
1568
|
}
|
|
@@ -1534,10 +1594,14 @@ async function render_response({
|
|
|
1534
1594
|
}
|
|
1535
1595
|
|
|
1536
1596
|
if (!state.prerendering) {
|
|
1537
|
-
const csp_header = csp.get_header();
|
|
1597
|
+
const csp_header = csp.csp_provider.get_header();
|
|
1538
1598
|
if (csp_header) {
|
|
1539
1599
|
headers.set('content-security-policy', csp_header);
|
|
1540
1600
|
}
|
|
1601
|
+
const report_only_header = csp.report_only_provider.get_header();
|
|
1602
|
+
if (report_only_header) {
|
|
1603
|
+
headers.set('content-security-policy-report-only', report_only_header);
|
|
1604
|
+
}
|
|
1541
1605
|
}
|
|
1542
1606
|
|
|
1543
1607
|
return new Response(html, {
|
package/dist/chunks/error.js
CHANGED
|
@@ -111,6 +111,40 @@ function init(open, close) {
|
|
|
111
111
|
|
|
112
112
|
/** @typedef {import('./types').Validator} Validator */
|
|
113
113
|
|
|
114
|
+
const directives = object({
|
|
115
|
+
'child-src': string_array(),
|
|
116
|
+
'default-src': string_array(),
|
|
117
|
+
'frame-src': string_array(),
|
|
118
|
+
'worker-src': string_array(),
|
|
119
|
+
'connect-src': string_array(),
|
|
120
|
+
'font-src': string_array(),
|
|
121
|
+
'img-src': string_array(),
|
|
122
|
+
'manifest-src': string_array(),
|
|
123
|
+
'media-src': string_array(),
|
|
124
|
+
'object-src': string_array(),
|
|
125
|
+
'prefetch-src': string_array(),
|
|
126
|
+
'script-src': string_array(),
|
|
127
|
+
'script-src-elem': string_array(),
|
|
128
|
+
'script-src-attr': string_array(),
|
|
129
|
+
'style-src': string_array(),
|
|
130
|
+
'style-src-elem': string_array(),
|
|
131
|
+
'style-src-attr': string_array(),
|
|
132
|
+
'base-uri': string_array(),
|
|
133
|
+
sandbox: string_array(),
|
|
134
|
+
'form-action': string_array(),
|
|
135
|
+
'frame-ancestors': string_array(),
|
|
136
|
+
'navigate-to': string_array(),
|
|
137
|
+
'report-uri': string_array(),
|
|
138
|
+
'report-to': string_array(),
|
|
139
|
+
'require-trusted-types-for': string_array(),
|
|
140
|
+
'trusted-types': string_array(),
|
|
141
|
+
'upgrade-insecure-requests': boolean(false),
|
|
142
|
+
'require-sri-for': string_array(),
|
|
143
|
+
'block-all-mixed-content': boolean(false),
|
|
144
|
+
'plugin-types': string_array(),
|
|
145
|
+
referrer: string_array()
|
|
146
|
+
});
|
|
147
|
+
|
|
114
148
|
/** @type {Validator} */
|
|
115
149
|
const options = object(
|
|
116
150
|
{
|
|
@@ -189,39 +223,8 @@ const options = object(
|
|
|
189
223
|
|
|
190
224
|
csp: object({
|
|
191
225
|
mode: list(['auto', 'hash', 'nonce']),
|
|
192
|
-
directives
|
|
193
|
-
|
|
194
|
-
'default-src': string_array(),
|
|
195
|
-
'frame-src': string_array(),
|
|
196
|
-
'worker-src': string_array(),
|
|
197
|
-
'connect-src': string_array(),
|
|
198
|
-
'font-src': string_array(),
|
|
199
|
-
'img-src': string_array(),
|
|
200
|
-
'manifest-src': string_array(),
|
|
201
|
-
'media-src': string_array(),
|
|
202
|
-
'object-src': string_array(),
|
|
203
|
-
'prefetch-src': string_array(),
|
|
204
|
-
'script-src': string_array(),
|
|
205
|
-
'script-src-elem': string_array(),
|
|
206
|
-
'script-src-attr': string_array(),
|
|
207
|
-
'style-src': string_array(),
|
|
208
|
-
'style-src-elem': string_array(),
|
|
209
|
-
'style-src-attr': string_array(),
|
|
210
|
-
'base-uri': string_array(),
|
|
211
|
-
sandbox: string_array(),
|
|
212
|
-
'form-action': string_array(),
|
|
213
|
-
'frame-ancestors': string_array(),
|
|
214
|
-
'navigate-to': string_array(),
|
|
215
|
-
'report-uri': string_array(),
|
|
216
|
-
'report-to': string_array(),
|
|
217
|
-
'require-trusted-types-for': string_array(),
|
|
218
|
-
'trusted-types': string_array(),
|
|
219
|
-
'upgrade-insecure-requests': boolean(false),
|
|
220
|
-
'require-sri-for': string_array(),
|
|
221
|
-
'block-all-mixed-content': boolean(false),
|
|
222
|
-
'plugin-types': string_array(),
|
|
223
|
-
referrer: string_array()
|
|
224
|
-
})
|
|
226
|
+
directives,
|
|
227
|
+
reportOnly: directives
|
|
225
228
|
}),
|
|
226
229
|
|
|
227
230
|
// TODO: remove this for the 1.0 release
|
|
@@ -410,7 +413,7 @@ const options = object(
|
|
|
410
413
|
* @param {boolean} [allow_unknown]
|
|
411
414
|
* @returns {Validator}
|
|
412
415
|
*/
|
|
413
|
-
function object(children, allow_unknown) {
|
|
416
|
+
function object(children, allow_unknown = false) {
|
|
414
417
|
return (input, keypath) => {
|
|
415
418
|
/** @type {Record<string, any>} */
|
|
416
419
|
const output = {};
|
package/dist/cli.js
CHANGED
package/package.json
CHANGED