dompurify 3.4.1 → 3.4.2

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/src/regexp.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { seal } from './utils.js';
2
+
3
+ // eslint-disable-next-line unicorn/better-regex
4
+ export const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
5
+ export const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
6
+ export const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
7
+ export const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
8
+ export const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
9
+ export const IS_ALLOWED_URI = seal(
10
+ /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
11
+ );
12
+ export const IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
13
+ export const ATTR_WHITESPACE = seal(
14
+ /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
15
+ );
16
+ export const DOCTYPE_NAME = seal(/^html$/i);
17
+ export const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
package/src/tags.ts ADDED
@@ -0,0 +1,285 @@
1
+ import { freeze } from './utils.js';
2
+
3
+ export const html = freeze([
4
+ 'a',
5
+ 'abbr',
6
+ 'acronym',
7
+ 'address',
8
+ 'area',
9
+ 'article',
10
+ 'aside',
11
+ 'audio',
12
+ 'b',
13
+ 'bdi',
14
+ 'bdo',
15
+ 'big',
16
+ 'blink',
17
+ 'blockquote',
18
+ 'body',
19
+ 'br',
20
+ 'button',
21
+ 'canvas',
22
+ 'caption',
23
+ 'center',
24
+ 'cite',
25
+ 'code',
26
+ 'col',
27
+ 'colgroup',
28
+ 'content',
29
+ 'data',
30
+ 'datalist',
31
+ 'dd',
32
+ 'decorator',
33
+ 'del',
34
+ 'details',
35
+ 'dfn',
36
+ 'dialog',
37
+ 'dir',
38
+ 'div',
39
+ 'dl',
40
+ 'dt',
41
+ 'element',
42
+ 'em',
43
+ 'fieldset',
44
+ 'figcaption',
45
+ 'figure',
46
+ 'font',
47
+ 'footer',
48
+ 'form',
49
+ 'h1',
50
+ 'h2',
51
+ 'h3',
52
+ 'h4',
53
+ 'h5',
54
+ 'h6',
55
+ 'head',
56
+ 'header',
57
+ 'hgroup',
58
+ 'hr',
59
+ 'html',
60
+ 'i',
61
+ 'img',
62
+ 'input',
63
+ 'ins',
64
+ 'kbd',
65
+ 'label',
66
+ 'legend',
67
+ 'li',
68
+ 'main',
69
+ 'map',
70
+ 'mark',
71
+ 'marquee',
72
+ 'menu',
73
+ 'menuitem',
74
+ 'meter',
75
+ 'nav',
76
+ 'nobr',
77
+ 'ol',
78
+ 'optgroup',
79
+ 'option',
80
+ 'output',
81
+ 'p',
82
+ 'picture',
83
+ 'pre',
84
+ 'progress',
85
+ 'q',
86
+ 'rp',
87
+ 'rt',
88
+ 'ruby',
89
+ 's',
90
+ 'samp',
91
+ 'search',
92
+ 'section',
93
+ 'select',
94
+ 'shadow',
95
+ 'slot',
96
+ 'small',
97
+ 'source',
98
+ 'spacer',
99
+ 'span',
100
+ 'strike',
101
+ 'strong',
102
+ 'style',
103
+ 'sub',
104
+ 'summary',
105
+ 'sup',
106
+ 'table',
107
+ 'tbody',
108
+ 'td',
109
+ 'template',
110
+ 'textarea',
111
+ 'tfoot',
112
+ 'th',
113
+ 'thead',
114
+ 'time',
115
+ 'tr',
116
+ 'track',
117
+ 'tt',
118
+ 'u',
119
+ 'ul',
120
+ 'var',
121
+ 'video',
122
+ 'wbr',
123
+ ] as const);
124
+
125
+ export const svg = freeze([
126
+ 'svg',
127
+ 'a',
128
+ 'altglyph',
129
+ 'altglyphdef',
130
+ 'altglyphitem',
131
+ 'animatecolor',
132
+ 'animatemotion',
133
+ 'animatetransform',
134
+ 'circle',
135
+ 'clippath',
136
+ 'defs',
137
+ 'desc',
138
+ 'ellipse',
139
+ 'enterkeyhint',
140
+ 'exportparts',
141
+ 'filter',
142
+ 'font',
143
+ 'g',
144
+ 'glyph',
145
+ 'glyphref',
146
+ 'hkern',
147
+ 'image',
148
+ 'inputmode',
149
+ 'line',
150
+ 'lineargradient',
151
+ 'marker',
152
+ 'mask',
153
+ 'metadata',
154
+ 'mpath',
155
+ 'part',
156
+ 'path',
157
+ 'pattern',
158
+ 'polygon',
159
+ 'polyline',
160
+ 'radialgradient',
161
+ 'rect',
162
+ 'stop',
163
+ 'style',
164
+ 'switch',
165
+ 'symbol',
166
+ 'text',
167
+ 'textpath',
168
+ 'title',
169
+ 'tref',
170
+ 'tspan',
171
+ 'view',
172
+ 'vkern',
173
+ ] as const);
174
+
175
+ export const svgFilters = freeze([
176
+ 'feBlend',
177
+ 'feColorMatrix',
178
+ 'feComponentTransfer',
179
+ 'feComposite',
180
+ 'feConvolveMatrix',
181
+ 'feDiffuseLighting',
182
+ 'feDisplacementMap',
183
+ 'feDistantLight',
184
+ 'feDropShadow',
185
+ 'feFlood',
186
+ 'feFuncA',
187
+ 'feFuncB',
188
+ 'feFuncG',
189
+ 'feFuncR',
190
+ 'feGaussianBlur',
191
+ 'feImage',
192
+ 'feMerge',
193
+ 'feMergeNode',
194
+ 'feMorphology',
195
+ 'feOffset',
196
+ 'fePointLight',
197
+ 'feSpecularLighting',
198
+ 'feSpotLight',
199
+ 'feTile',
200
+ 'feTurbulence',
201
+ ] as const);
202
+
203
+ // List of SVG elements that are disallowed by default.
204
+ // We still need to know them so that we can do namespace
205
+ // checks properly in case one wants to add them to
206
+ // allow-list.
207
+ export const svgDisallowed = freeze([
208
+ 'animate',
209
+ 'color-profile',
210
+ 'cursor',
211
+ 'discard',
212
+ 'font-face',
213
+ 'font-face-format',
214
+ 'font-face-name',
215
+ 'font-face-src',
216
+ 'font-face-uri',
217
+ 'foreignobject',
218
+ 'hatch',
219
+ 'hatchpath',
220
+ 'mesh',
221
+ 'meshgradient',
222
+ 'meshpatch',
223
+ 'meshrow',
224
+ 'missing-glyph',
225
+ 'script',
226
+ 'set',
227
+ 'solidcolor',
228
+ 'unknown',
229
+ 'use',
230
+ ] as const);
231
+
232
+ export const mathMl = freeze([
233
+ 'math',
234
+ 'menclose',
235
+ 'merror',
236
+ 'mfenced',
237
+ 'mfrac',
238
+ 'mglyph',
239
+ 'mi',
240
+ 'mlabeledtr',
241
+ 'mmultiscripts',
242
+ 'mn',
243
+ 'mo',
244
+ 'mover',
245
+ 'mpadded',
246
+ 'mphantom',
247
+ 'mroot',
248
+ 'mrow',
249
+ 'ms',
250
+ 'mspace',
251
+ 'msqrt',
252
+ 'mstyle',
253
+ 'msub',
254
+ 'msup',
255
+ 'msubsup',
256
+ 'mtable',
257
+ 'mtd',
258
+ 'mtext',
259
+ 'mtr',
260
+ 'munder',
261
+ 'munderover',
262
+ 'mprescripts',
263
+ ] as const);
264
+
265
+ // Similarly to SVG, we want to know all MathML elements,
266
+ // even those that we disallow by default.
267
+ export const mathMlDisallowed = freeze([
268
+ 'maction',
269
+ 'maligngroup',
270
+ 'malignmark',
271
+ 'mlongdiv',
272
+ 'mscarries',
273
+ 'mscarry',
274
+ 'msgroup',
275
+ 'mstack',
276
+ 'msline',
277
+ 'msrow',
278
+ 'semantics',
279
+ 'annotation',
280
+ 'annotation-xml',
281
+ 'mprescripts',
282
+ 'none',
283
+ ] as const);
284
+
285
+ export const text = freeze(['#text'] as const);
package/src/utils.ts ADDED
@@ -0,0 +1,338 @@
1
+ const {
2
+ entries,
3
+ setPrototypeOf,
4
+ isFrozen,
5
+ getPrototypeOf,
6
+ getOwnPropertyDescriptor,
7
+ } = Object;
8
+
9
+ let { freeze, seal, create } = Object; // eslint-disable-line import/no-mutable-exports
10
+ let { apply, construct } = typeof Reflect !== 'undefined' && Reflect;
11
+
12
+ if (!freeze) {
13
+ freeze = function <T>(x: T): T {
14
+ return x;
15
+ };
16
+ }
17
+
18
+ if (!seal) {
19
+ seal = function <T>(x: T): T {
20
+ return x;
21
+ };
22
+ }
23
+
24
+ if (!apply) {
25
+ apply = function <T>(
26
+ func: (thisArg: any, ...args: any[]) => T,
27
+ thisArg: any,
28
+ ...args: any[]
29
+ ): T {
30
+ return func.apply(thisArg, args);
31
+ };
32
+ }
33
+
34
+ if (!construct) {
35
+ construct = function <T>(Func: new (...args: any[]) => T, ...args: any[]): T {
36
+ return new Func(...args);
37
+ };
38
+ }
39
+
40
+ const arrayForEach = unapply(Array.prototype.forEach);
41
+ const arrayIndexOf = unapply(Array.prototype.indexOf);
42
+ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
43
+ const arrayPop = unapply(Array.prototype.pop);
44
+ const arrayPush = unapply(Array.prototype.push);
45
+ const arraySlice = unapply(Array.prototype.slice);
46
+ const arraySplice = unapply(Array.prototype.splice);
47
+ const arrayIsArray = Array.isArray;
48
+
49
+ const stringToLowerCase = unapply(String.prototype.toLowerCase);
50
+ const stringToString = unapply(String.prototype.toString);
51
+ const stringMatch = unapply(String.prototype.match);
52
+ const stringReplace = unapply(String.prototype.replace);
53
+ const stringIndexOf = unapply(String.prototype.indexOf);
54
+ const stringTrim = unapply(String.prototype.trim);
55
+
56
+ const numberToString = unapply(Number.prototype.toString);
57
+ const booleanToString = unapply(Boolean.prototype.toString);
58
+ const bigintToString =
59
+ typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
60
+ const symbolToString =
61
+ typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
62
+
63
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
64
+ const objectToString = unapply(Object.prototype.toString);
65
+
66
+ const regExpTest = unapply(RegExp.prototype.test);
67
+
68
+ const typeErrorCreate = unconstruct(TypeError);
69
+
70
+ /**
71
+ * Creates a new function that calls the given function with a specified thisArg and arguments.
72
+ *
73
+ * @param func - The function to be wrapped and called.
74
+ * @returns A new function that calls the given function with a specified thisArg and arguments.
75
+ */
76
+ function unapply<T>(
77
+ func: (thisArg: any, ...args: any[]) => T
78
+ ): (thisArg: any, ...args: any[]) => T {
79
+ return (thisArg: any, ...args: any[]): T => {
80
+ if (thisArg instanceof RegExp) {
81
+ thisArg.lastIndex = 0;
82
+ }
83
+
84
+ return apply(func, thisArg, args);
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Creates a new function that constructs an instance of the given constructor function with the provided arguments.
90
+ *
91
+ * @param func - The constructor function to be wrapped and called.
92
+ * @returns A new function that constructs an instance of the given constructor function with the provided arguments.
93
+ */
94
+ function unconstruct<T>(
95
+ Func: new (...args: any[]) => T
96
+ ): (...args: any[]) => T {
97
+ return (...args: any[]): T => construct(Func, args);
98
+ }
99
+
100
+ /**
101
+ * Add properties to a lookup table
102
+ *
103
+ * @param set - The set to which elements will be added.
104
+ * @param array - The array containing elements to be added to the set.
105
+ * @param transformCaseFunc - An optional function to transform the case of each element before adding to the set.
106
+ * @returns The modified set with added elements.
107
+ */
108
+ function addToSet(
109
+ set: Record<string, boolean>,
110
+ array: readonly unknown[],
111
+ transformCaseFunc: ReturnType<typeof unapply<string>> = stringToLowerCase
112
+ ): Record<string, boolean> {
113
+ if (setPrototypeOf) {
114
+ // Make 'in' and truthy checks like Boolean(set.constructor)
115
+ // independent of any properties defined on Object.prototype.
116
+ // Prevent prototype setters from intercepting set as a this value.
117
+ setPrototypeOf(set, null);
118
+ }
119
+
120
+ if (!arrayIsArray(array)) {
121
+ return set;
122
+ }
123
+
124
+ let l = array.length;
125
+ while (l--) {
126
+ let element = array[l];
127
+
128
+ if (typeof element === 'string') {
129
+ const lcElement = transformCaseFunc(element);
130
+
131
+ if (lcElement !== element) {
132
+ // Config presets (e.g. tags.js, attrs.js) are immutable.
133
+ if (!isFrozen(array)) {
134
+ (array as unknown[])[l] = lcElement;
135
+ }
136
+
137
+ element = lcElement;
138
+ }
139
+ }
140
+
141
+ set[element as string] = true;
142
+ }
143
+
144
+ return set;
145
+ }
146
+
147
+ /**
148
+ * Clean up an array to harden against CSPP
149
+ *
150
+ * @param array - The array to be cleaned.
151
+ * @returns The cleaned version of the array
152
+ */
153
+ function cleanArray<T>(array: T[]): Array<T | null> {
154
+ for (let index = 0; index < array.length; index++) {
155
+ const isPropertyExist = objectHasOwnProperty(array, index);
156
+
157
+ if (!isPropertyExist) {
158
+ array[index] = null;
159
+ }
160
+ }
161
+
162
+ return array;
163
+ }
164
+
165
+ /**
166
+ * Shallow clone an object
167
+ *
168
+ * @param object - The object to be cloned.
169
+ * @returns A new object that copies the original.
170
+ */
171
+ function clone<T extends Record<string, any>>(object: T): T {
172
+ const newObject = create(null);
173
+
174
+ for (const [property, value] of entries(object)) {
175
+ const isPropertyExist = objectHasOwnProperty(object, property);
176
+
177
+ if (isPropertyExist) {
178
+ if (arrayIsArray(value)) {
179
+ newObject[property] = cleanArray(value);
180
+ } else if (
181
+ value &&
182
+ typeof value === 'object' &&
183
+ value.constructor === Object
184
+ ) {
185
+ newObject[property] = clone(value);
186
+ } else {
187
+ newObject[property] = value;
188
+ }
189
+ }
190
+ }
191
+
192
+ return newObject;
193
+ }
194
+
195
+ /**
196
+ * Convert non-node values into strings without depending on direct property access.
197
+ *
198
+ * @param value - The value to stringify.
199
+ * @returns A string representation of the provided value.
200
+ */
201
+ function stringifyValue(value: unknown): string {
202
+ switch (typeof value) {
203
+ case 'string': {
204
+ return value;
205
+ }
206
+
207
+ case 'number': {
208
+ return numberToString(value);
209
+ }
210
+
211
+ case 'boolean': {
212
+ return booleanToString(value);
213
+ }
214
+
215
+ case 'bigint': {
216
+ return bigintToString ? bigintToString(value) : '0';
217
+ }
218
+
219
+ case 'symbol': {
220
+ return symbolToString ? symbolToString(value) : 'Symbol()';
221
+ }
222
+
223
+ case 'undefined': {
224
+ return objectToString(value);
225
+ }
226
+
227
+ case 'function':
228
+ case 'object': {
229
+ if (value === null) {
230
+ return objectToString(value);
231
+ }
232
+
233
+ const valueAsRecord = value as Record<string, any>;
234
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
235
+
236
+ if (typeof valueToString === 'function') {
237
+ const stringified = valueToString(valueAsRecord);
238
+
239
+ return typeof stringified === 'string'
240
+ ? stringified
241
+ : objectToString(stringified);
242
+ }
243
+
244
+ return objectToString(value);
245
+ }
246
+
247
+ default: {
248
+ return objectToString(value);
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * This method automatically checks if the prop is function or getter and behaves accordingly.
255
+ *
256
+ * @param object - The object to look up the getter function in its prototype chain.
257
+ * @param prop - The property name for which to find the getter function.
258
+ * @returns The getter function found in the prototype chain or a fallback function.
259
+ */
260
+ function lookupGetter<T extends Record<string, any>>(
261
+ object: T,
262
+ prop: string
263
+ ): ReturnType<typeof unapply<any>> | (() => null) {
264
+ while (object !== null) {
265
+ const desc = getOwnPropertyDescriptor(object, prop);
266
+
267
+ if (desc) {
268
+ if (desc.get) {
269
+ return unapply(desc.get);
270
+ }
271
+
272
+ if (typeof desc.value === 'function') {
273
+ return unapply(desc.value);
274
+ }
275
+ }
276
+
277
+ object = getPrototypeOf(object);
278
+ }
279
+
280
+ function fallbackValue(): null {
281
+ return null;
282
+ }
283
+
284
+ return fallbackValue;
285
+ }
286
+
287
+ function isRegex(value: unknown): value is RegExp {
288
+ try {
289
+ regExpTest(value as RegExp, '');
290
+ return true;
291
+ } catch {
292
+ return false;
293
+ }
294
+ }
295
+
296
+ export {
297
+ // Array
298
+ arrayForEach,
299
+ arrayIndexOf,
300
+ arrayIsArray,
301
+ arrayLastIndexOf,
302
+ arrayPop,
303
+ arrayPush,
304
+ arraySlice,
305
+ arraySplice,
306
+ // Object
307
+ entries,
308
+ freeze,
309
+ getPrototypeOf,
310
+ getOwnPropertyDescriptor,
311
+ isFrozen,
312
+ setPrototypeOf,
313
+ seal,
314
+ clone,
315
+ create,
316
+ objectHasOwnProperty,
317
+ objectToString,
318
+ // RegExp
319
+ regExpTest,
320
+ isRegex,
321
+ // String
322
+ stringIndexOf,
323
+ stringMatch,
324
+ stringReplace,
325
+ stringToLowerCase,
326
+ stringToString,
327
+ stringTrim,
328
+ // Other conversion
329
+ stringifyValue,
330
+ // Errors
331
+ typeErrorCreate,
332
+ // Other
333
+ lookupGetter,
334
+ addToSet,
335
+ // Reflect
336
+ unapply,
337
+ unconstruct,
338
+ };