@shgysk8zer0/polyfills 0.3.5 → 0.3.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgysk8zer0/polyfills",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "A collection of JavaScript polyfills",
@@ -77,6 +77,7 @@
77
77
  "http-server": "^14.1.1"
78
78
  },
79
79
  "dependencies": {
80
- "@aegisjsproject/sanitizer": "^0.0.4"
80
+ "@aegisjsproject/sanitizer": "^0.0.8",
81
+ "@aegisjsproject/trusted-types": "^1.0.1"
81
82
  }
82
83
  }
package/trustedTypes.js CHANGED
@@ -1,3 +1,2 @@
1
- import { polyfill, trustPolicies } from './assets/TrustedTypes.js';
2
- const polyfilled = polyfill();
3
- export { trustPolicies, polyfilled };
1
+ import '@aegisjsproject/trusted-types/trusted-types.js';
2
+ import '@aegisjsproject/trusted-types/harden.js';
@@ -1,595 +0,0 @@
1
- /**
2
- * @copyright 2023 Chris Zuber <admin@kernvalley.us>
3
- */
4
- import { supported as isSupported } from './trust.js';
5
- import { events } from './attributes.js';
6
- import { getMetaCSP, getHTTPCSP } from './csp.js';
7
- /**
8
- * @See https://github.com/w3c/webappsec-trusted-types/blob/main/src/trustedtypes.js
9
- * @See https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API
10
- */
11
-
12
- // `<html data-fetch-trust-csp="1">` will enable using actual CSP
13
- const fetchCSP = document.documentElement.dataset.fetchTrustCsp === '1';
14
-
15
- function parseCSP({ 'trusted-types': trustedTypes = null } = {}) {
16
- if (Array.isArray(trustedTypes)) {
17
- const policies = new Set(trustedTypes);
18
- const allowDuplicates = policies.has('\'allow-duplicates\'');
19
- const hasNone = policies.has('\'none\'');
20
-
21
- if (allowDuplicates) {
22
- policies.delete('\'allow-duplicates\'');
23
- }
24
-
25
- if (hasNone) {
26
- policies.clear();
27
- }
28
-
29
- policies.add('empty#html');
30
- policies.add('empty#script');
31
-
32
- return { policies, allowDuplicates, hasNone };
33
- } else {
34
- return {};
35
- }
36
- }
37
-
38
- async function getActualCSP() {
39
- const { policies, allowDuplicates = false, hasNone = false } = await getHTTPCSP().then(parseCSP);
40
-
41
- if (policies instanceof Set) {
42
- return { policies, allowDuplicates, hasNone };
43
- } else {
44
- return {};
45
- }
46
- }
47
-
48
- function init() {
49
- // Temporary config if/while fetching actual CSP
50
- // Maps to `trusted-types 'allow-duplicates'`
51
- const getAllowDuplicates = () => {
52
- const csp = parseCSP(getMetaCSP());
53
-
54
- if (typeof csp === 'object' && 'allowDuplicates' in csp) {
55
- return csp.allowDuplicates;
56
- } else {
57
- return document.documentElement.dataset.allowTrustDuplicates === '1';
58
- }
59
- };
60
-
61
- const getAllowedPolicies = () => {
62
- if (document.documentElement.dataset.hasOwnProperty('trustedPolicies')) {
63
- return new Set([
64
- 'empty#html', 'empty#script',
65
- ...document.documentElement.dataset.trustedPolicies.split(' '),
66
- ]);
67
- } else {
68
- const { policies } = parseCSP(getMetaCSP());
69
-
70
- if (policies instanceof Set) {
71
- return policies;
72
- } else {
73
- return new Set(['empty#html', 'empty#script']);
74
- }
75
- }
76
- };
77
-
78
- const fromCSP = parseCSP(getMetaCSP());
79
-
80
- if (typeof fromCSP === 'object' && fromCSP.policies instanceof Set) {
81
- const { policies: allowedPolicies, allowDuplicates } = fromCSP;
82
- return { allowDuplicates, allowedPolicies };
83
- } else {
84
- const allowDuplicates = getAllowDuplicates();
85
- const allowedPolicies = getAllowedPolicies();
86
-
87
- if (allowedPolicies.has('\'none\'')) {
88
- allowedPolicies.clear();
89
- }
90
-
91
- allowedPolicies.add('empty#html');
92
- allowedPolicies.add('empty#script');
93
-
94
- return { allowDuplicates, allowedPolicies };
95
- }
96
- }
97
-
98
- const { allowDuplicates, allowedPolicies } = init();
99
-
100
- /**
101
- * [supported description]
102
- */
103
- export const supported = isSupported();
104
-
105
- /**
106
- * [symbols description]
107
- * @type {Object}
108
- */
109
- const symbols = {
110
- trustedValue: Symbol('[[Data]]'),
111
- trustedKey: Symbol('trusted-key'),
112
- emptyHTML: Symbol('policy-empty#html'),
113
- emptyScript: Symbol('policy-empty#script'),
114
- policy: Symbol.for('trust-policy'),
115
- defaultPolicy: Symbol('default-policy'),
116
- trustedTypesCSP: Symbol('trusted-types-csp'),
117
- };
118
-
119
- if (! Symbol.hasOwnProperty('toStringTag')) {
120
- Symbol.toStringTag = Symbol('Symbol.toStringTag');
121
- }
122
-
123
- const policies = [];
124
-
125
- function getPolicy(name) {
126
- return policies.find(policy => policy.name === name) || null;
127
- }
128
-
129
- function hasPolicy(name) {
130
- return getPolicy(name) !== null;
131
- }
132
-
133
- /**
134
- * [getUnsetPolicyException description]
135
- * @param {TrustedTypePolicy} policy [description]
136
- * @param {String} method [description]
137
- */
138
- function getUnsetPolicyException(policy, method) {
139
- return function() {
140
- throw new TypeError(`Failed to execute '${method}' on 'TrustedTypePolicy': Policy ${policy.name}'s TrustedTypePolicyOptions did not specify a '${method}' member.`);
141
- };
142
- }
143
-
144
- /**
145
- * [TrustedType description]
146
- * @type {TrustedType}
147
- */
148
- export class TrustedType {
149
- /**
150
- * [constructor description]
151
- * @param {String} value [description]
152
- * @param {Symbol} key [description]
153
- */
154
- constructor(value, { key, policy }) {
155
- if (key !== symbols.trustedKey) {
156
- throw new TypeError('Invalid constructor');
157
- } else {
158
- Object.defineProperties(this, {
159
- [symbols.trustedValue]: {
160
- enumerable: false,
161
- configurable: false,
162
- writable: false,
163
- value: value.toString(),
164
- },
165
- [symbols.policy]: {
166
- enumerable: false,
167
- configurable: false,
168
- writable: false,
169
- value: policy.name,
170
- },
171
- });
172
-
173
- Object.freeze(this);
174
- }
175
- }
176
-
177
- /**
178
- * [toString description]
179
- * @return {String} [description]
180
- */
181
- toString() {
182
- return this.valueOf();
183
- }
184
-
185
- /**
186
- * [toJSON description]
187
- * @return {String} [description]
188
- */
189
- toJSON() {
190
- return this.valueOf();
191
- }
192
-
193
- valueOf() {
194
- return this[symbols.trustedValue];
195
- }
196
- }
197
-
198
- /**
199
- * [description]
200
- * @type {TrustedHTML}
201
- */
202
- export class TrustedHTML extends TrustedType {
203
- [Symbol.toStringTag]() {
204
- return 'TrustedHTML';
205
- }
206
- }
207
-
208
- /**
209
- * [description]
210
- * @type {TrustedScript}
211
- */
212
- export class TrustedScript extends TrustedType {
213
- [Symbol.toStringTag]() {
214
- return 'TrustedScript';
215
- }
216
- }
217
-
218
- /**
219
- * [name description]
220
- * @type {TrustedScriptURL}
221
- */
222
- export class TrustedScriptURL extends TrustedType {
223
- [Symbol.toStringTag]() {
224
- return 'TrustedScriptURL';
225
- }
226
- }
227
-
228
- /**
229
- * [name description]
230
- * @type {TrustedTypePolicy}
231
- */
232
- export class TrustedTypePolicy {
233
- /**
234
- * [constructor description]
235
- * @param {String} name [description]
236
- * @param {Function} createHTML [description]
237
- * @param {Function} createScript [description]
238
- * @param {Function} createScriptURL [description]
239
- * @param {String} key [description]
240
- */
241
- constructor(name, { createHTML, createScript, createScriptURL } = {}, { key }) {
242
- // @TODO: Chrome seems to allow any same-origin scripts creating policies
243
- if (key !== symbols.trustedKey) {
244
- throw new TypeError('Invalid constructor');
245
- } else {
246
- Object.defineProperties(this, {
247
- name: {
248
- enumerable: true,
249
- configurable: false,
250
- writable: false,
251
- value: name.toString(),
252
- },
253
- createHTML: {
254
- enumerable: true,
255
- configurable: false,
256
- writable: false,
257
- value: createHTML instanceof Function
258
- ? (input, ...args) => new TrustedHTML(
259
- createHTML(input.toString(), ...args),
260
- { key: symbols.trustedKey, policy: this }
261
- )
262
- : getUnsetPolicyException(this, 'createHTML'),
263
- },
264
- createScript: {
265
- enumerable: true,
266
- configurable: false,
267
- writable: false,
268
- value: createScript instanceof Function
269
- ? (input, ...args) => new TrustedScript(
270
- createScript(input.toString(), ...args),
271
- { key: symbols.trustedKey, policy: this }
272
- )
273
- : getUnsetPolicyException(this, 'createScript'),
274
- },
275
- createScriptURL: {
276
- enumerable: true,
277
- configurable: false,
278
- writable: false,
279
- value: createScriptURL instanceof Function
280
- ? (input, ...args) => new TrustedScriptURL(
281
- createScriptURL(input.toString(), ...args),
282
- { key: symbols.trustedKey, policy: this }
283
- )
284
- : getUnsetPolicyException(this, 'createScriptURL'),
285
- },
286
- });
287
- }
288
- }
289
- }
290
-
291
- /**
292
- * [enumerable description]
293
- * @type {TrustedTypePolicyFactory}
294
- */
295
- export class TrustedTypePolicyFactory extends EventTarget {
296
- /**
297
- * [constructor description]
298
- * @param {Symbol} key [description]
299
- */
300
- constructor(key) {
301
- super();
302
-
303
- if (key !== symbols.trustedKey) {
304
- throw new TypeError('Invalid constructor');
305
- }
306
-
307
- if (fetchCSP) {
308
- getActualCSP().then(({ policies, allowDuplicates = false, hasNone = false }) => {
309
- if (typeof policies !== 'undefined') {
310
- Object.defineProperty(TrustedTypePolicyFactory, symbols.trustedTypesCSP, {
311
- value: { policies, allowDuplicates, hasNone },
312
- enumberable: false,
313
- writable: false,
314
- configurable: false,
315
- });
316
- }
317
- }).catch(err => console.error(err));
318
- }
319
-
320
- Object.defineProperties(this, {
321
- [symbols.defaultPolicy]: {
322
- enumerable: false,
323
- configurable: false,
324
- writable: true,
325
- value: null,
326
- },
327
- [symbols.emptyHTML]: {
328
- enumerable: false,
329
- configurable: false,
330
- writable: false,
331
- value: this.createPolicy('empty#html', { createHTML: () => '' }),
332
- },
333
- [symbols.emptyScript]: {
334
- enumerable: false,
335
- configurable: false,
336
- writable: false,
337
- value: this.createPolicy('empty#script', { createScript: () => '' }),
338
- },
339
- });
340
- }
341
-
342
- /**
343
- * [isHTML description]
344
- * @param {String} value [description]
345
- * @return {Boolean} [description]
346
- */
347
- isHTML(value) {
348
- return value instanceof globalThis.TrustedHTML;
349
- }
350
-
351
- /**
352
- * [isScript description]
353
- * @param {String} value [description]
354
- * @return {Boolean} [description]
355
- */
356
- isScript(value) {
357
- return value instanceof globalThis.TrustedScript;
358
- }
359
-
360
- /**
361
- * [isScriptURL description]
362
- * @param {String} value [description]
363
- * @return {Boolean} [description]
364
- */
365
- isScriptURL(value) {
366
- return value instanceof globalThis.TrustedScriptURL;
367
- }
368
-
369
- /**
370
- * [createPolicy description]
371
- * @param {String} name [description]
372
- * @param {Function} createHTML [description]
373
- * @param {Function} createScript [description]
374
- * @param {Function} createScriptURL [description]
375
- */
376
- createPolicy(name, { createHTML, createScript, createScriptURL } = {}) {
377
- const policy = new TrustedTypePolicy(name, { createHTML, createScript, createScriptURL }, { key: symbols.trustedKey });
378
-
379
- if (! name.toString().match(/^[-#a-zA-Z0-9=_/@.%]+$/g)) {
380
- throw new TypeError(`Failed to execute 'createPolicy' on 'TrustedTypePolicyFactory': Policy: "${name}" contains invalid characters.`);
381
- } else if (TrustedTypePolicyFactory.hasOwnProperty(symbols.trustedTypesCSP)) {
382
- // This requires `fetch()` and parsing response headers, so will not be immediately available
383
- const { policies, allowDuplicates, hasNone } = TrustedTypePolicyFactory[symbols.trustedTypesCSP];
384
-
385
- // Will always allow `empty#html` and `empty#script`
386
- if (hasNone && name !== 'empty#html' && name !== 'empty#script') {
387
- throw new TypeError(`Failed to execute 'createPolicy' on 'TrustedTypePolicyFactory': Policy: "${name}" disallowed.`);
388
- } else if (! policies.has(name)) {
389
- throw new TypeError(`Failed to execute 'createPolicy' on 'TrustedTypePolicyFactory': Policy: "${name}" disallowed.`);
390
- } else if(! allowDuplicates && hasPolicy(name)) {
391
- throw new TypeError(`Failed to execute 'createPolicy' on 'TrustedTypePolicyFactory': Policy: "${name}" already exists.`);
392
- }
393
- } else {
394
- // This is the fallback for before CSP headers are fetched and uses `document.documentElement.dataset`
395
- if (allowedPolicies.size > 2 && ! allowedPolicies.has(name)) {
396
- throw new TypeError(`Failed to execute 'createPolicy' on 'TrustedTypePolicyFactory': Policy: "${name}" disallowed.`);
397
- } else if(! allowDuplicates && hasPolicy(name)) {
398
- throw new TypeError(`Failed to execute 'createPolicy' on 'TrustedTypePolicyFactory': Policy: "${name}" already exists.`);
399
- }
400
- }
401
-
402
- if (policy.name === 'default') {
403
- this[symbols.defaultPolicy] = policy;
404
- }
405
-
406
- policies.push(policy);
407
-
408
- return policy;
409
- }
410
-
411
- /**
412
- * [getAttributeType description]
413
- * @param {String} tagName [description]
414
- * @param {String} attribute [description]
415
- * @param {String} elementNs [description]
416
- * @return {String} [description]
417
- */
418
- getAttributeType(tagName, attribute, elementNs/*, attrNs*/) {
419
- tagName = tagName.toLowerCase();
420
- attribute = attribute.toLowerCase();
421
-
422
- /**
423
- * @Todo handle namespaced attributes
424
- */
425
- if (typeof elementNS === 'string' && elementNs.length !== 0) {
426
- return events.includes(attribute) ? 'TrustedScript' : null;
427
- }
428
-
429
- /**
430
- * This is an `on*` attribute
431
- */
432
- if (events.includes(attribute)) {
433
- return 'TrustedScript';
434
- }
435
-
436
- switch(tagName) {
437
- case 'script': {
438
- if (attribute === 'src') {
439
- return 'TrustedScriptURL';
440
- } else {
441
- return null;
442
- }
443
- }
444
-
445
- case 'iframe': {
446
- if (attribute === 'srcdoc') {
447
- return 'TrustedHTML';
448
- } else if (attribute === 'src') {
449
- return 'TrustedScriptURL';
450
- } else {
451
- return null;
452
- }
453
- }
454
-
455
- default:
456
- return null;
457
- }
458
- }
459
-
460
- /**
461
- * [getPropertyType description]
462
- * @param {String} tagName [description]
463
- * @param {String} property [description]
464
- * @return {String} [description]
465
- */
466
- getPropertyType(tagName, property/*, elementNS*/) {
467
- tagName = tagName.toLowerCase();
468
-
469
- if (events.includes(property.toLowerCase())) {
470
- return 'TrustedScript';
471
- }
472
-
473
- switch(tagName) {
474
- case 'embed':
475
- case 'iframe': {
476
- if (property === 'src') {
477
- return 'TrustedScriptURL';
478
- } else {
479
- return null;
480
- }
481
- }
482
-
483
- case 'script': {
484
- if (property === 'src') {
485
- return 'TrustedScriptURL';
486
- } else if (['text', 'innerText', 'textContent', 'innerHTML'].includes(property)) {
487
- return 'TrustedScript';
488
- } else if (['outerHTML'].includes(property)) {
489
- return 'TrustedHTML';
490
- } else {
491
- return null;
492
- }
493
- }
494
-
495
- default: {
496
- if (['innerHTML', 'outerHTML'].includes(property)) {
497
- return 'TrustedHTML';
498
- } else {
499
- return null;
500
- }
501
- }
502
- }
503
- }
504
-
505
- /**
506
- * [emptyHTML description]
507
- * @return {TrustedHTML}
508
- */
509
- get emptyHTML() {
510
- return this[symbols.emptyHTML].createHTML('');
511
- }
512
-
513
- /**
514
- * [emptyScript description]
515
- * @return {TrustedScript}
516
- */
517
- get emptyScript() {
518
- return this[symbols.emptyScript].createScript('');
519
- }
520
-
521
- /**
522
- * [defaultPolicy description]
523
- * @return {TrustedTypePolicy} [description]
524
- */
525
- get defaultPolicy() {
526
- return this[symbols.defaultPolicy];
527
- }
528
-
529
- /**
530
- * For consistency with existing polyfill
531
- * @return {Boolean} [description]
532
- */
533
- get _isPolyfill_() {
534
- return true;
535
- }
536
-
537
- static get allowDuplicates() {
538
- return allowDuplicates;
539
- }
540
- }
541
-
542
- /**
543
- * [trustedTypes description]
544
- * @type {TrustedTypeFactory}
545
- */
546
- export const trustedTypes = new TrustedTypePolicyFactory(symbols.trustedKey);
547
-
548
- /**
549
- * [polyfill description]
550
- * @return {[type]} [description]
551
- */
552
- export function polyfill() {
553
- if (! ('TrustedTypePolicyFactory' in globalThis)) {
554
- globalThis.TrustedTypePolicyFactory = TrustedTypePolicyFactory;
555
- }
556
-
557
- if (! ('TrustedTypePolicy' in globalThis)) {
558
- globalThis.TrustedTypePolicy = TrustedTypePolicy;
559
- }
560
-
561
- if (! ('TrustedType' in globalThis)) {
562
- globalThis.TrustedType = TrustedType;
563
- }
564
-
565
- if (! ('TrustedHTML' in globalThis)) {
566
- globalThis.TrustedHTML = TrustedHTML;
567
- }
568
-
569
- if (! ('TrustedScript' in globalThis)) {
570
- globalThis.TrustedScript = TrustedScript;
571
- }
572
-
573
- if (! ('TrustedScriptURL' in globalThis)) {
574
- globalThis.TrustedScriptURL = TrustedScriptURL;
575
- }
576
-
577
- if (! ('trustedTypes' in globalThis)) {
578
- globalThis.trustedTypes = trustedTypes;
579
- } else {
580
- try {
581
- /**
582
- * Create these policies even if not needed to prevent
583
- * their use elsewhere. Not creating them but allowing them via CSP
584
- * would allow creating them as arbitrary policies.
585
- * @type {[type]}
586
- */
587
- globalThis.trustedTypes.createPolicy('empty#html', { createHTML: () => '' });
588
- globalThis.trustedTypes.createPolicy('empty#script', { createScript: () => '' });
589
- } catch(err) {
590
- console.error(err);
591
- }
592
- }
593
- }
594
-
595
- export const trustPolicies = [...allowedPolicies];
package/assets/csp.js DELETED
@@ -1,29 +0,0 @@
1
- import { callOnce } from './utility.js';
2
-
3
- export const getHTTPCSP = callOnce(async function getHTTPCSP() {
4
- const { ok, headers } = await fetch(location.href, { method: 'HEAD' });
5
-
6
- if (ok && headers.has('Content-Security-Policy')) {
7
- const directives = headers.get('Content-Security-Policy').trim().split(';').filter(str => str.length !== 0);
8
-
9
- return Object.fromEntries(directives.map(directive => {
10
- const [key, ...rest] = directive.trim().split(' ').filter(part => part.length !== 0);
11
- return [key, rest];
12
- }));
13
- }
14
- });
15
-
16
- export function getMetaCSP() {
17
- const meta = document.head.querySelector('meta[http-equiv="Content-Security-Policy"][content]');
18
-
19
- if (meta instanceof HTMLMetaElement) {
20
- const directives = meta.content.trim().split(';').filter(str => str.length !== 0);
21
-
22
- return Object.fromEntries(directives.map(directive => {
23
- const [key, ...rest] = directive.trim().split(' ').filter(part => part.length !== 0);
24
- return [key, rest];
25
- }));
26
- } else {
27
- return {};
28
- }
29
- }