[d565449] | 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
|
---|