1 | /*!
|
---|
2 | * Bootstrap sanitizer.js v5.3.3 (https://getbootstrap.com/)
|
---|
3 | * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
---|
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
---|
5 | */
|
---|
6 | (function (global, factory) {
|
---|
7 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
---|
8 | typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
---|
9 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Sanitizer = {}));
|
---|
10 | })(this, (function (exports) { 'use strict';
|
---|
11 |
|
---|
12 | /**
|
---|
13 | * --------------------------------------------------------------------------
|
---|
14 | * Bootstrap util/sanitizer.js
|
---|
15 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
---|
16 | * --------------------------------------------------------------------------
|
---|
17 | */
|
---|
18 |
|
---|
19 | // js-docs-start allow-list
|
---|
20 | const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
|
---|
21 | const DefaultAllowlist = {
|
---|
22 | // Global attributes allowed on any supplied element below.
|
---|
23 | '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
|
---|
24 | a: ['target', 'href', 'title', 'rel'],
|
---|
25 | area: [],
|
---|
26 | b: [],
|
---|
27 | br: [],
|
---|
28 | col: [],
|
---|
29 | code: [],
|
---|
30 | dd: [],
|
---|
31 | div: [],
|
---|
32 | dl: [],
|
---|
33 | dt: [],
|
---|
34 | em: [],
|
---|
35 | hr: [],
|
---|
36 | h1: [],
|
---|
37 | h2: [],
|
---|
38 | h3: [],
|
---|
39 | h4: [],
|
---|
40 | h5: [],
|
---|
41 | h6: [],
|
---|
42 | i: [],
|
---|
43 | img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
|
---|
44 | li: [],
|
---|
45 | ol: [],
|
---|
46 | p: [],
|
---|
47 | pre: [],
|
---|
48 | s: [],
|
---|
49 | small: [],
|
---|
50 | span: [],
|
---|
51 | sub: [],
|
---|
52 | sup: [],
|
---|
53 | strong: [],
|
---|
54 | u: [],
|
---|
55 | ul: []
|
---|
56 | };
|
---|
57 | // js-docs-end allow-list
|
---|
58 |
|
---|
59 | const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation
|
---|
63 | * contexts.
|
---|
64 | *
|
---|
65 | * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38
|
---|
66 | */
|
---|
67 | // eslint-disable-next-line unicorn/better-regex
|
---|
68 | const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;
|
---|
69 | const allowedAttribute = (attribute, allowedAttributeList) => {
|
---|
70 | const attributeName = attribute.nodeName.toLowerCase();
|
---|
71 | if (allowedAttributeList.includes(attributeName)) {
|
---|
72 | if (uriAttributes.has(attributeName)) {
|
---|
73 | return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));
|
---|
74 | }
|
---|
75 | return true;
|
---|
76 | }
|
---|
77 |
|
---|
78 | // Check if a regular expression validates the attribute.
|
---|
79 | return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));
|
---|
80 | };
|
---|
81 | function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
|
---|
82 | if (!unsafeHtml.length) {
|
---|
83 | return unsafeHtml;
|
---|
84 | }
|
---|
85 | if (sanitizeFunction && typeof sanitizeFunction === 'function') {
|
---|
86 | return sanitizeFunction(unsafeHtml);
|
---|
87 | }
|
---|
88 | const domParser = new window.DOMParser();
|
---|
89 | const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
|
---|
90 | const elements = [].concat(...createdDocument.body.querySelectorAll('*'));
|
---|
91 | for (const element of elements) {
|
---|
92 | const elementName = element.nodeName.toLowerCase();
|
---|
93 | if (!Object.keys(allowList).includes(elementName)) {
|
---|
94 | element.remove();
|
---|
95 | continue;
|
---|
96 | }
|
---|
97 | const attributeList = [].concat(...element.attributes);
|
---|
98 | const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);
|
---|
99 | for (const attribute of attributeList) {
|
---|
100 | if (!allowedAttribute(attribute, allowedAttributes)) {
|
---|
101 | element.removeAttribute(attribute.nodeName);
|
---|
102 | }
|
---|
103 | }
|
---|
104 | }
|
---|
105 | return createdDocument.body.innerHTML;
|
---|
106 | }
|
---|
107 |
|
---|
108 | exports.DefaultAllowlist = DefaultAllowlist;
|
---|
109 | exports.sanitizeHtml = sanitizeHtml;
|
---|
110 |
|
---|
111 | Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
---|
112 |
|
---|
113 | }));
|
---|
114 | //# sourceMappingURL=sanitizer.js.map
|
---|