Mercurial Hosting > sceditor
comparison src/sceditor.js @ 19:13df5ac9b34b
more OO
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 08 Aug 2022 16:50:22 -0600 |
parents | 1334920263a2 |
children | cf42d9b17c25 |
comparison
equal
deleted
inserted
replaced
18:1334920263a2 | 19:13df5ac9b34b |
---|---|
4099 /*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */ | 4099 /*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */ |
4100 | 4100 |
4101 function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | 4101 function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } |
4102 | 4102 |
4103 var hasOwnProperty = Object.hasOwnProperty, | 4103 var hasOwnProperty = Object.hasOwnProperty, |
4104 setPrototypeOf = Object.setPrototypeOf, | 4104 setPrototypeOf = Object.setPrototypeOf, |
4105 isFrozen = Object.isFrozen, | 4105 isFrozen = Object.isFrozen, |
4106 getPrototypeOf = Object.getPrototypeOf, | 4106 getPrototypeOf = Object.getPrototypeOf, |
4107 getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; | 4107 getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; |
4108 var freeze = Object.freeze, | 4108 var freeze = Object.freeze, |
4109 seal = Object.seal, | 4109 seal = Object.seal, |
4110 create = Object.create; // eslint-disable-line import/no-mutable-exports | 4110 create = Object.create; // eslint-disable-line import/no-mutable-exports |
4111 | 4111 |
4112 var _ref = typeof Reflect !== 'undefined' && Reflect, | 4112 var _ref = typeof Reflect !== 'undefined' && Reflect, |
4113 apply = _ref.apply, | 4113 apply = _ref.apply, |
4114 construct = _ref.construct; | 4114 construct = _ref.construct; |
4115 | 4115 |
4116 if (!apply) { | 4116 if (!apply) { |
4117 apply = function apply(fun, thisValue, args) { | 4117 apply = function apply(fun, thisValue, args) { |
4118 return fun.apply(thisValue, args); | 4118 return fun.apply(thisValue, args); |
4119 }; | 4119 }; |
4120 } | 4120 } |
4121 | 4121 |
4122 if (!freeze) { | 4122 if (!freeze) { |
4123 freeze = function freeze(x) { | 4123 freeze = function freeze(x) { |
4124 return x; | 4124 return x; |
4125 }; | 4125 }; |
4126 } | 4126 } |
4127 | 4127 |
4128 if (!seal) { | 4128 if (!seal) { |
4129 seal = function seal(x) { | 4129 seal = function seal(x) { |
4130 return x; | 4130 return x; |
4131 }; | 4131 }; |
4132 } | 4132 } |
4133 | 4133 |
4134 if (!construct) { | 4134 if (!construct) { |
4135 construct = function construct(Func, args) { | 4135 construct = function construct(Func, args) { |
4136 return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))(); | 4136 return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))(); |
4137 }; | 4137 }; |
4138 } | 4138 } |
4139 | 4139 |
4140 var arrayForEach = unapply(Array.prototype.forEach); | 4140 var arrayForEach = unapply(Array.prototype.forEach); |
4141 var arrayPop = unapply(Array.prototype.pop); | 4141 var arrayPop = unapply(Array.prototype.pop); |
4151 | 4151 |
4152 var typeErrorCreate = unconstruct(TypeError); | 4152 var typeErrorCreate = unconstruct(TypeError); |
4153 | 4153 |
4154 function unapply(func) { | 4154 function unapply(func) { |
4155 return function (thisArg) { | 4155 return function (thisArg) { |
4156 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | 4156 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { |
4157 args[_key - 1] = arguments[_key]; | 4157 args[_key - 1] = arguments[_key]; |
4158 } | 4158 } |
4159 | 4159 |
4160 return apply(func, thisArg, args); | 4160 return apply(func, thisArg, args); |
4161 }; | 4161 }; |
4162 } | 4162 } |
4163 | 4163 |
4164 function unconstruct(func) { | 4164 function unconstruct(func) { |
4165 return function () { | 4165 return function () { |
4166 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | 4166 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { |
4167 args[_key2] = arguments[_key2]; | 4167 args[_key2] = arguments[_key2]; |
4168 } | 4168 } |
4169 | 4169 |
4170 return construct(func, args); | 4170 return construct(func, args); |
4171 }; | 4171 }; |
4172 } | 4172 } |
4173 | 4173 |
4174 /* Add properties to a lookup table */ | 4174 /* Add properties to a lookup table */ |
4175 function addToSet(set, array) { | 4175 function addToSet(set, array) { |
4176 if (setPrototypeOf) { | 4176 if (setPrototypeOf) { |
4177 // Make 'in' and truthy checks like Boolean(set.constructor) | 4177 // Make 'in' and truthy checks like Boolean(set.constructor) |
4178 // independent of any properties defined on Object.prototype. | 4178 // independent of any properties defined on Object.prototype. |
4179 // Prevent prototype setters from intercepting set as a this value. | 4179 // Prevent prototype setters from intercepting set as a this value. |
4180 setPrototypeOf(set, null); | 4180 setPrototypeOf(set, null); |
4181 } | 4181 } |
4182 | 4182 |
4183 var l = array.length; | 4183 var l = array.length; |
4184 while (l--) { | 4184 while (l--) { |
4185 var element = array[l]; | 4185 var element = array[l]; |
4186 if (typeof element === 'string') { | 4186 if (typeof element === 'string') { |
4187 var lcElement = stringToLowerCase(element); | 4187 var lcElement = stringToLowerCase(element); |
4188 if (lcElement !== element) { | 4188 if (lcElement !== element) { |
4189 // Config presets (e.g. tags.js, attrs.js) are immutable. | 4189 // Config presets (e.g. tags.js, attrs.js) are immutable. |
4190 if (!isFrozen(array)) { | 4190 if (!isFrozen(array)) { |
4191 array[l] = lcElement; | 4191 array[l] = lcElement; |
4192 } | 4192 } |
4193 | 4193 |
4194 element = lcElement; | 4194 element = lcElement; |
4195 } | 4195 } |
4196 } | 4196 } |
4197 | 4197 |
4198 set[element] = true; | 4198 set[element] = true; |
4199 } | 4199 } |
4200 | 4200 |
4201 return set; | 4201 return set; |
4202 } | 4202 } |
4203 | 4203 |
4205 function clone(object) { | 4205 function clone(object) { |
4206 var newObject = create(null); | 4206 var newObject = create(null); |
4207 | 4207 |
4208 var property = void 0; | 4208 var property = void 0; |
4209 for (property in object) { | 4209 for (property in object) { |
4210 if (apply(hasOwnProperty, object, [property])) { | 4210 if (apply(hasOwnProperty, object, [property])) { |
4211 newObject[property] = object[property]; | 4211 newObject[property] = object[property]; |
4212 } | 4212 } |
4213 } | 4213 } |
4214 | 4214 |
4215 return newObject; | 4215 return newObject; |
4216 } | 4216 } |
4217 | 4217 |
4219 * simulate it. It also automatically checks | 4219 * simulate it. It also automatically checks |
4220 * if the prop is function or getter and behaves | 4220 * if the prop is function or getter and behaves |
4221 * accordingly. */ | 4221 * accordingly. */ |
4222 function lookupGetter(object, prop) { | 4222 function lookupGetter(object, prop) { |
4223 while (object !== null) { | 4223 while (object !== null) { |
4224 var desc = getOwnPropertyDescriptor(object, prop); | 4224 var desc = getOwnPropertyDescriptor(object, prop); |
4225 if (desc) { | 4225 if (desc) { |
4226 if (desc.get) { | 4226 if (desc.get) { |
4227 return unapply(desc.get); | 4227 return unapply(desc.get); |
4228 } | 4228 } |
4229 | 4229 |
4230 if (typeof desc.value === 'function') { | 4230 if (typeof desc.value === 'function') { |
4231 return unapply(desc.value); | 4231 return unapply(desc.value); |
4232 } | 4232 } |
4233 } | 4233 } |
4234 | 4234 |
4235 object = getPrototypeOf(object); | 4235 object = getPrototypeOf(object); |
4236 } | 4236 } |
4237 | 4237 |
4238 return null; | 4238 return null; |
4239 } | 4239 } |
4240 | 4240 |
4294 * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types | 4294 * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types |
4295 * are not supported). | 4295 * are not supported). |
4296 */ | 4296 */ |
4297 var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { | 4297 var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { |
4298 if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { | 4298 if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { |
4299 return null; | 4299 return null; |
4300 } | 4300 } |
4301 | 4301 |
4302 // Allow the callers to control the unique policy name | 4302 // Allow the callers to control the unique policy name |
4303 // by adding a data-tt-policy-suffix to the script element with the DOMPurify. | 4303 // by adding a data-tt-policy-suffix to the script element with the DOMPurify. |
4304 // Policy creation with duplicate names throws in Trusted Types. | 4304 // Policy creation with duplicate names throws in Trusted Types. |
4305 var suffix = null; | 4305 var suffix = null; |
4306 var ATTR_NAME = 'data-tt-policy-suffix'; | 4306 var ATTR_NAME = 'data-tt-policy-suffix'; |
4307 if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { | 4307 if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { |
4308 suffix = document.currentScript.getAttribute(ATTR_NAME); | 4308 suffix = document.currentScript.getAttribute(ATTR_NAME); |
4309 } | 4309 } |
4310 | 4310 |
4311 var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); | 4311 var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); |
4312 | 4312 |
4313 try { | 4313 try { |
4314 return trustedTypes.createPolicy(policyName, { | 4314 return trustedTypes.createPolicy(policyName, { |
4315 createHTML: function createHTML(html$$1) { | 4315 createHTML: function createHTML(html$$1) { |
4316 return html$$1; | 4316 return html$$1; |
4317 } | 4317 } |
4318 }); | 4318 }); |
4319 } catch (_) { | 4319 } catch (_) { |
4320 // Policy creation failed (most likely another DOMPurify script has | 4320 // Policy creation failed (most likely another DOMPurify script has |
4321 // already run). Skip creating the policy, as this will only cause errors | 4321 // already run). Skip creating the policy, as this will only cause errors |
4322 // if TT are enforced. | 4322 // if TT are enforced. |
4323 console.warn('TrustedTypes policy ' + policyName + ' could not be created.'); | 4323 console.warn('TrustedTypes policy ' + policyName + ' could not be created.'); |
4324 return null; | 4324 return null; |
4325 } | 4325 } |
4326 }; | 4326 }; |
4327 | 4327 |
4328 function createDOMPurify() { | 4328 function createDOMPurify() { |
4329 var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); | 4329 var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); |
4330 | 4330 |
4331 var DOMPurify = function DOMPurify(root) { | 4331 var DOMPurify = function DOMPurify(root) { |
4332 return createDOMPurify(root); | 4332 return createDOMPurify(root); |
4333 }; | 4333 }; |
4334 | 4334 |
4335 /** | 4335 /** |
4336 * Version label, exposed for easier checks | 4336 * Version label, exposed for easier checks |
4337 * if DOMPurify is up to date or not | 4337 * if DOMPurify is up to date or not |
4343 * Empty if nothing was removed. | 4343 * Empty if nothing was removed. |
4344 */ | 4344 */ |
4345 DOMPurify.removed = []; | 4345 DOMPurify.removed = []; |
4346 | 4346 |
4347 if (!window || !window.document || window.document.nodeType !== 9) { | 4347 if (!window || !window.document || window.document.nodeType !== 9) { |
4348 // Not running in a browser, provide a factory function | 4348 // Not running in a browser, provide a factory function |
4349 // so that you can pass your own Window | 4349 // so that you can pass your own Window |
4350 DOMPurify.isSupported = false; | 4350 DOMPurify.isSupported = false; |
4351 | 4351 |
4352 return DOMPurify; | 4352 return DOMPurify; |
4353 } | 4353 } |
4354 | 4354 |
4355 var originalDocument = window.document; | 4355 var originalDocument = window.document; |
4356 | 4356 |
4357 var document = window.document; | 4357 var document = window.document; |
4358 var DocumentFragment = window.DocumentFragment, | 4358 var DocumentFragment = window.DocumentFragment, |
4359 HTMLTemplateElement = window.HTMLTemplateElement, | 4359 HTMLTemplateElement = window.HTMLTemplateElement, |
4360 Node = window.Node, | 4360 Node = window.Node, |
4361 Element = window.Element, | 4361 Element = window.Element, |
4362 NodeFilter = window.NodeFilter, | 4362 NodeFilter = window.NodeFilter, |
4363 _window$NamedNodeMap = window.NamedNodeMap, | 4363 _window$NamedNodeMap = window.NamedNodeMap, |
4364 NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, | 4364 NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, |
4365 Text = window.Text, | 4365 Text = window.Text, |
4366 Comment = window.Comment, | 4366 Comment = window.Comment, |
4367 DOMParser = window.DOMParser, | 4367 DOMParser = window.DOMParser, |
4368 trustedTypes = window.trustedTypes; | 4368 trustedTypes = window.trustedTypes; |
4369 | 4369 |
4370 | 4370 |
4371 var ElementPrototype = Element.prototype; | 4371 var ElementPrototype = Element.prototype; |
4372 | 4372 |
4373 var cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); | 4373 var cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); |
4380 // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) | 4380 // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) |
4381 // a new empty registry is used when creating a template contents owner | 4381 // a new empty registry is used when creating a template contents owner |
4382 // document, so we use that as our parent document to ensure nothing | 4382 // document, so we use that as our parent document to ensure nothing |
4383 // is inherited. | 4383 // is inherited. |
4384 if (typeof HTMLTemplateElement === 'function') { | 4384 if (typeof HTMLTemplateElement === 'function') { |
4385 var template = document.createElement('template'); | 4385 var template = document.createElement('template'); |
4386 if (template.content && template.content.ownerDocument) { | 4386 if (template.content && template.content.ownerDocument) { |
4387 document = template.content.ownerDocument; | 4387 document = template.content.ownerDocument; |
4388 } | 4388 } |
4389 } | 4389 } |
4390 | 4390 |
4391 var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); | 4391 var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); |
4392 var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : ''; | 4392 var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : ''; |
4393 | 4393 |
4394 var _document = document, | 4394 var implementation = document.implementation, |
4395 implementation = _document.implementation, | 4395 //var importNode = originalDocument.importNode; |
4396 createNodeIterator = _document.createNodeIterator, | |
4397 getElementsByTagName = _document.getElementsByTagName, | |
4398 createDocumentFragment = _document.createDocumentFragment; | |
4399 var importNode = originalDocument.importNode; | |
4400 | 4396 |
4401 | 4397 |
4402 var documentMode = {}; | 4398 var documentMode = {}; |
4403 try { | 4399 try { |
4404 documentMode = clone(document).documentMode ? document.documentMode : {}; | 4400 documentMode = clone(document).documentMode ? document.documentMode : {}; |
4405 } catch (_) {} | 4401 } catch (_) {} |
4406 | 4402 |
4407 var hooks = {}; | 4403 var hooks = {}; |
4408 | 4404 |
4409 /** | 4405 /** |
4410 * Expose whether this browser supports running the full DOMPurify. | 4406 * Expose whether this browser supports running the full DOMPurify. |
4411 */ | 4407 */ |
4412 DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; | 4408 DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; |
4413 | 4409 |
4414 var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR, | 4410 var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR, |
4415 ERB_EXPR$$1 = ERB_EXPR, | 4411 ERB_EXPR$$1 = ERB_EXPR, |
4416 DATA_ATTR$$1 = DATA_ATTR, | 4412 DATA_ATTR$$1 = DATA_ATTR, |
4417 ARIA_ATTR$$1 = ARIA_ATTR, | 4413 ARIA_ATTR$$1 = ARIA_ATTR, |
4418 IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA, | 4414 IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA, |
4419 ATTR_WHITESPACE$$1 = ATTR_WHITESPACE; | 4415 ATTR_WHITESPACE$$1 = ATTR_WHITESPACE; |
4420 var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI; | 4416 var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI; |
4421 | 4417 |
4422 /** | 4418 /** |
4423 * We consider the elements and attributes below to be safe. Ideally | 4419 * We consider the elements and attributes below to be safe. Ideally |
4424 * don't add any new ones but feel free to remove unwanted ones. | 4420 * don't add any new ones but feel free to remove unwanted ones. |
4525 * | 4521 * |
4526 * @param {Object} cfg optional config literal | 4522 * @param {Object} cfg optional config literal |
4527 */ | 4523 */ |
4528 // eslint-disable-next-line complexity | 4524 // eslint-disable-next-line complexity |
4529 var _parseConfig = function _parseConfig(cfg) { | 4525 var _parseConfig = function _parseConfig(cfg) { |
4530 if (CONFIG && CONFIG === cfg) { | 4526 if (CONFIG && CONFIG === cfg) { |
4531 return; | 4527 return; |
4532 } | 4528 } |
4533 | 4529 |
4534 /* Shield configuration object from tampering */ | 4530 /* Shield configuration object from tampering */ |
4535 if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') { | 4531 if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') { |
4536 cfg = {}; | 4532 cfg = {}; |
4537 } | 4533 } |
4538 | 4534 |
4539 /* Shield configuration object from prototype pollution */ | 4535 /* Shield configuration object from prototype pollution */ |
4540 cfg = clone(cfg); | 4536 cfg = clone(cfg); |
4541 | 4537 |
4542 /* Set configuration parameters */ | 4538 /* Set configuration parameters */ |
4543 ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS; | 4539 ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS; |
4544 ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR; | 4540 ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR; |
4545 URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES; | 4541 URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES; |
4546 DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS; | 4542 DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS; |
4547 FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {}; | 4543 FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {}; |
4548 FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {}; | 4544 FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {}; |
4549 USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; | 4545 USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; |
4550 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true | 4546 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true |
4551 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true | 4547 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true |
4552 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false | 4548 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false |
4553 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false | 4549 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false |
4554 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false | 4550 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false |
4555 RETURN_DOM = cfg.RETURN_DOM || false; // Default false | 4551 RETURN_DOM = cfg.RETURN_DOM || false; // Default false |
4556 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false | 4552 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false |
4557 RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true | 4553 RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true |
4558 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false | 4554 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false |
4559 FORCE_BODY = cfg.FORCE_BODY || false; // Default false | 4555 FORCE_BODY = cfg.FORCE_BODY || false; // Default false |
4560 SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true | 4556 SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true |
4561 KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true | 4557 KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true |
4562 IN_PLACE = cfg.IN_PLACE || false; // Default false | 4558 IN_PLACE = cfg.IN_PLACE || false; // Default false |
4563 IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1; | 4559 IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1; |
4564 if (SAFE_FOR_TEMPLATES) { | 4560 if (SAFE_FOR_TEMPLATES) { |
4565 ALLOW_DATA_ATTR = false; | 4561 ALLOW_DATA_ATTR = false; |
4566 } | 4562 } |
4567 | 4563 |
4568 if (RETURN_DOM_FRAGMENT) { | 4564 if (RETURN_DOM_FRAGMENT) { |
4569 RETURN_DOM = true; | 4565 RETURN_DOM = true; |
4570 } | 4566 } |
4571 | 4567 |
4572 /* Parse profile info */ | 4568 /* Parse profile info */ |
4573 if (USE_PROFILES) { | 4569 if (USE_PROFILES) { |
4574 ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text))); | 4570 ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text))); |
4575 ALLOWED_ATTR = []; | 4571 ALLOWED_ATTR = []; |
4576 if (USE_PROFILES.html === true) { | 4572 if (USE_PROFILES.html === true) { |
4577 addToSet(ALLOWED_TAGS, html); | 4573 addToSet(ALLOWED_TAGS, html); |
4578 addToSet(ALLOWED_ATTR, html$1); | 4574 addToSet(ALLOWED_ATTR, html$1); |
4579 } | 4575 } |
4580 | 4576 |
4581 if (USE_PROFILES.svg === true) { | 4577 if (USE_PROFILES.svg === true) { |
4582 addToSet(ALLOWED_TAGS, svg); | 4578 addToSet(ALLOWED_TAGS, svg); |
4583 addToSet(ALLOWED_ATTR, svg$1); | 4579 addToSet(ALLOWED_ATTR, svg$1); |
4584 addToSet(ALLOWED_ATTR, xml); | 4580 addToSet(ALLOWED_ATTR, xml); |
4585 } | 4581 } |
4586 | 4582 |
4587 if (USE_PROFILES.svgFilters === true) { | 4583 if (USE_PROFILES.svgFilters === true) { |
4588 addToSet(ALLOWED_TAGS, svgFilters); | 4584 addToSet(ALLOWED_TAGS, svgFilters); |
4589 addToSet(ALLOWED_ATTR, svg$1); | 4585 addToSet(ALLOWED_ATTR, svg$1); |
4590 addToSet(ALLOWED_ATTR, xml); | 4586 addToSet(ALLOWED_ATTR, xml); |
4591 } | 4587 } |
4592 | 4588 |
4593 if (USE_PROFILES.mathMl === true) { | 4589 if (USE_PROFILES.mathMl === true) { |
4594 addToSet(ALLOWED_TAGS, mathMl); | 4590 addToSet(ALLOWED_TAGS, mathMl); |
4595 addToSet(ALLOWED_ATTR, mathMl$1); | 4591 addToSet(ALLOWED_ATTR, mathMl$1); |
4596 addToSet(ALLOWED_ATTR, xml); | 4592 addToSet(ALLOWED_ATTR, xml); |
4597 } | 4593 } |
4598 } | 4594 } |
4599 | 4595 |
4600 /* Merge configuration parameters */ | 4596 /* Merge configuration parameters */ |
4601 if (cfg.ADD_TAGS) { | 4597 if (cfg.ADD_TAGS) { |
4602 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { | 4598 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { |
4603 ALLOWED_TAGS = clone(ALLOWED_TAGS); | 4599 ALLOWED_TAGS = clone(ALLOWED_TAGS); |
4604 } | 4600 } |
4605 | 4601 |
4606 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS); | 4602 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS); |
4607 } | 4603 } |
4608 | 4604 |
4609 if (cfg.ADD_ATTR) { | 4605 if (cfg.ADD_ATTR) { |
4610 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { | 4606 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { |
4611 ALLOWED_ATTR = clone(ALLOWED_ATTR); | 4607 ALLOWED_ATTR = clone(ALLOWED_ATTR); |
4612 } | 4608 } |
4613 | 4609 |
4614 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR); | 4610 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR); |
4615 } | 4611 } |
4616 | 4612 |
4617 if (cfg.ADD_URI_SAFE_ATTR) { | 4613 if (cfg.ADD_URI_SAFE_ATTR) { |
4618 addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR); | 4614 addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR); |
4619 } | 4615 } |
4620 | 4616 |
4621 /* Add #text in case KEEP_CONTENT is set to true */ | 4617 /* Add #text in case KEEP_CONTENT is set to true */ |
4622 if (KEEP_CONTENT) { | 4618 if (KEEP_CONTENT) { |
4623 ALLOWED_TAGS['#text'] = true; | 4619 ALLOWED_TAGS['#text'] = true; |
4624 } | 4620 } |
4625 | 4621 |
4626 /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ | 4622 /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ |
4627 if (WHOLE_DOCUMENT) { | 4623 if (WHOLE_DOCUMENT) { |
4628 addToSet(ALLOWED_TAGS, ['html', 'head', 'body']); | 4624 addToSet(ALLOWED_TAGS, ['html', 'head', 'body']); |
4629 } | 4625 } |
4630 | 4626 |
4631 /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ | 4627 /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ |
4632 if (ALLOWED_TAGS.table) { | 4628 if (ALLOWED_TAGS.table) { |
4633 addToSet(ALLOWED_TAGS, ['tbody']); | 4629 addToSet(ALLOWED_TAGS, ['tbody']); |
4634 delete FORBID_TAGS.tbody; | 4630 delete FORBID_TAGS.tbody; |
4635 } | 4631 } |
4636 | 4632 |
4637 // Prevent further manipulation of configuration. | 4633 // Prevent further manipulation of configuration. |
4638 // Not available in IE8, Safari 5, etc. | 4634 // Not available in IE8, Safari 5, etc. |
4639 if (freeze) { | 4635 if (freeze) { |
4640 freeze(cfg); | 4636 freeze(cfg); |
4641 } | 4637 } |
4642 | 4638 |
4643 CONFIG = cfg; | 4639 CONFIG = cfg; |
4644 }; | 4640 }; |
4645 | 4641 |
4646 var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); | 4642 var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); |
4647 | 4643 |
4648 var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); | 4644 var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); |
4668 * @returns {boolean} Return false if the element has a | 4664 * @returns {boolean} Return false if the element has a |
4669 * namespace that a spec-compliant parser would never | 4665 * namespace that a spec-compliant parser would never |
4670 * return. Return true otherwise. | 4666 * return. Return true otherwise. |
4671 */ | 4667 */ |
4672 var _checkValidNamespace = function _checkValidNamespace(element) { | 4668 var _checkValidNamespace = function _checkValidNamespace(element) { |
4673 var parent = getParentNode(element); | 4669 var parent = getParentNode(element); |
4674 | 4670 |
4675 // In JSDOM, if we're inside shadow DOM, then parentNode | 4671 // In JSDOM, if we're inside shadow DOM, then parentNode |
4676 // can be null. We just simulate parent in this case. | 4672 // can be null. We just simulate parent in this case. |
4677 if (!parent || !parent.tagName) { | 4673 if (!parent || !parent.tagName) { |
4678 parent = { | 4674 parent = { |
4679 namespaceURI: HTML_NAMESPACE, | 4675 namespaceURI: HTML_NAMESPACE, |
4680 tagName: 'template' | 4676 tagName: 'template' |
4681 }; | 4677 }; |
4682 } | 4678 } |
4683 | 4679 |
4684 var tagName = stringToLowerCase(element.tagName); | 4680 var tagName = stringToLowerCase(element.tagName); |
4685 var parentTagName = stringToLowerCase(parent.tagName); | 4681 var parentTagName = stringToLowerCase(parent.tagName); |
4686 | 4682 |
4687 if (element.namespaceURI === SVG_NAMESPACE) { | 4683 if (element.namespaceURI === SVG_NAMESPACE) { |
4688 // The only way to switch from HTML namespace to SVG | 4684 // The only way to switch from HTML namespace to SVG |
4689 // is via <svg>. If it happens via any other tag, then | 4685 // is via <svg>. If it happens via any other tag, then |
4690 // it should be killed. | 4686 // it should be killed. |
4691 if (parent.namespaceURI === HTML_NAMESPACE) { | 4687 if (parent.namespaceURI === HTML_NAMESPACE) { |
4692 return tagName === 'svg'; | 4688 return tagName === 'svg'; |
4693 } | 4689 } |
4694 | 4690 |
4695 // The only way to switch from MathML to SVG is via | 4691 // The only way to switch from MathML to SVG is via |
4696 // svg if parent is either <annotation-xml> or MathML | 4692 // svg if parent is either <annotation-xml> or MathML |
4697 // text integration points. | 4693 // text integration points. |
4698 if (parent.namespaceURI === MATHML_NAMESPACE) { | 4694 if (parent.namespaceURI === MATHML_NAMESPACE) { |
4699 return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); | 4695 return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); |
4700 } | 4696 } |
4701 | 4697 |
4702 // We only allow elements that are defined in SVG | 4698 // We only allow elements that are defined in SVG |
4703 // spec. All others are disallowed in SVG namespace. | 4699 // spec. All others are disallowed in SVG namespace. |
4704 return Boolean(ALL_SVG_TAGS[tagName]); | 4700 return Boolean(ALL_SVG_TAGS[tagName]); |
4705 } | 4701 } |
4706 | 4702 |
4707 if (element.namespaceURI === MATHML_NAMESPACE) { | 4703 if (element.namespaceURI === MATHML_NAMESPACE) { |
4708 // The only way to switch from HTML namespace to MathML | 4704 // The only way to switch from HTML namespace to MathML |
4709 // is via <math>. If it happens via any other tag, then | 4705 // is via <math>. If it happens via any other tag, then |
4710 // it should be killed. | 4706 // it should be killed. |
4711 if (parent.namespaceURI === HTML_NAMESPACE) { | 4707 if (parent.namespaceURI === HTML_NAMESPACE) { |
4712 return tagName === 'math'; | 4708 return tagName === 'math'; |
4713 } | 4709 } |
4714 | 4710 |
4715 // The only way to switch from SVG to MathML is via | 4711 // The only way to switch from SVG to MathML is via |
4716 // <math> and HTML integration points | 4712 // <math> and HTML integration points |
4717 if (parent.namespaceURI === SVG_NAMESPACE) { | 4713 if (parent.namespaceURI === SVG_NAMESPACE) { |
4718 return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName]; | 4714 return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName]; |
4719 } | 4715 } |
4720 | 4716 |
4721 // We only allow elements that are defined in MathML | 4717 // We only allow elements that are defined in MathML |
4722 // spec. All others are disallowed in MathML namespace. | 4718 // spec. All others are disallowed in MathML namespace. |
4723 return Boolean(ALL_MATHML_TAGS[tagName]); | 4719 return Boolean(ALL_MATHML_TAGS[tagName]); |
4724 } | 4720 } |
4725 | 4721 |
4726 if (element.namespaceURI === HTML_NAMESPACE) { | 4722 if (element.namespaceURI === HTML_NAMESPACE) { |
4727 // The only way to switch from SVG to HTML is via | 4723 // The only way to switch from SVG to HTML is via |
4728 // HTML integration points, and from MathML to HTML | 4724 // HTML integration points, and from MathML to HTML |
4729 // is via MathML text integration points | 4725 // is via MathML text integration points |
4730 if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { | 4726 if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { |
4731 return false; | 4727 return false; |
4732 } | 4728 } |
4733 | 4729 |
4734 if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) { | 4730 if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) { |
4735 return false; | 4731 return false; |
4736 } | 4732 } |
4737 | 4733 |
4738 // Certain elements are allowed in both SVG and HTML | 4734 // Certain elements are allowed in both SVG and HTML |
4739 // namespace. We need to specify them explicitly | 4735 // namespace. We need to specify them explicitly |
4740 // so that they don't get erronously deleted from | 4736 // so that they don't get erronously deleted from |
4741 // HTML namespace. | 4737 // HTML namespace. |
4742 var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']); | 4738 var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']); |
4743 | 4739 |
4744 // We disallow tags that are specific for MathML | 4740 // We disallow tags that are specific for MathML |
4745 // or SVG and should never appear in HTML namespace | 4741 // or SVG and should never appear in HTML namespace |
4746 return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]); | 4742 return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]); |
4747 } | 4743 } |
4748 | 4744 |
4749 // The code should never reach this place (this means | 4745 // The code should never reach this place (this means |
4750 // that the element somehow got namespace that is not | 4746 // that the element somehow got namespace that is not |
4751 // HTML, SVG or MathML). Return false just in case. | 4747 // HTML, SVG or MathML). Return false just in case. |
4752 return false; | 4748 return false; |
4753 }; | 4749 }; |
4754 | 4750 |
4755 /** | 4751 /** |
4756 * _forceRemove | 4752 * _forceRemove |
4757 * | 4753 * |
4758 * @param {Node} node a DOM node | 4754 * @param {Node} node a DOM node |
4759 */ | 4755 */ |
4760 var _forceRemove = function _forceRemove(node) { | 4756 var _forceRemove = function _forceRemove(node) { |
4761 arrayPush(DOMPurify.removed, { element: node }); | 4757 arrayPush(DOMPurify.removed, { element: node }); |
4762 try { | 4758 try { |
4763 node.parentNode.removeChild(node); | 4759 node.parentNode.removeChild(node); |
4764 } catch (_) { | 4760 } catch (_) { |
4765 try { | 4761 try { |
4766 node.outerHTML = emptyHTML; | 4762 node.outerHTML = emptyHTML; |
4767 } catch (_) { | 4763 } catch (_) { |
4768 node.remove(); | 4764 node.remove(); |
4769 } | 4765 } |
4770 } | 4766 } |
4771 }; | 4767 }; |
4772 | 4768 |
4773 /** | 4769 /** |
4774 * _removeAttribute | 4770 * _removeAttribute |
4775 * | 4771 * |
4776 * @param {String} name an Attribute name | 4772 * @param {String} name an Attribute name |
4777 * @param {Node} node a DOM node | 4773 * @param {Node} node a DOM node |
4778 */ | 4774 */ |
4779 var _removeAttribute = function _removeAttribute(name, node) { | 4775 var _removeAttribute = function _removeAttribute(name, node) { |
4780 try { | 4776 try { |
4781 arrayPush(DOMPurify.removed, { | 4777 arrayPush(DOMPurify.removed, { |
4782 attribute: node.getAttributeNode(name), | 4778 attribute: node.getAttributeNode(name), |
4783 from: node | 4779 from: node |
4784 }); | 4780 }); |
4785 } catch (_) { | 4781 } catch (_) { |
4786 arrayPush(DOMPurify.removed, { | 4782 arrayPush(DOMPurify.removed, { |
4787 attribute: null, | 4783 attribute: null, |
4788 from: node | 4784 from: node |
4789 }); | 4785 }); |
4790 } | 4786 } |
4791 | 4787 |
4792 node.removeAttribute(name); | 4788 node.removeAttribute(name); |
4793 }; | 4789 }; |
4794 | 4790 |
4795 /** | 4791 /** |
4796 * _initDocument | 4792 * _initDocument |
4797 * | 4793 * |
4798 * @param {String} dirty a string of dirty markup | 4794 * @param {String} dirty a string of dirty markup |
4799 * @return {Document} a DOM, filled with the dirty markup | 4795 * @return {Document} a DOM, filled with the dirty markup |
4800 */ | 4796 */ |
4801 var _initDocument = function _initDocument(dirty) { | 4797 var _initDocument = function _initDocument(dirty) { |
4802 /* Create a HTML document */ | 4798 /* Create a HTML document */ |
4803 var doc = void 0; | 4799 var doc = void 0; |
4804 var leadingWhitespace = void 0; | 4800 var leadingWhitespace = void 0; |
4805 | 4801 |
4806 if (FORCE_BODY) { | 4802 if (FORCE_BODY) { |
4807 dirty = '<remove></remove>' + dirty; | 4803 dirty = '<remove></remove>' + dirty; |
4808 } else { | 4804 } else { |
4809 /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */ | 4805 /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */ |
4810 var matches = stringMatch(dirty, /^[\r\n\t ]+/); | 4806 var matches = stringMatch(dirty, /^[\r\n\t ]+/); |
4811 leadingWhitespace = matches && matches[0]; | 4807 leadingWhitespace = matches && matches[0]; |
4812 } | 4808 } |
4813 | 4809 |
4814 var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; | 4810 var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; |
4815 /* Use the DOMParser API by default, fallback later if needs be */ | 4811 /* Use the DOMParser API by default, fallback later if needs be */ |
4816 try { | 4812 try { |
4817 doc = new DOMParser().parseFromString(dirtyPayload, 'text/html'); | 4813 doc = new DOMParser().parseFromString(dirtyPayload, 'text/html'); |
4818 } catch (_) {} | 4814 } catch (_) {} |
4819 | 4815 |
4820 /* Use createHTMLDocument in case DOMParser is not available */ | 4816 /* Use createHTMLDocument in case DOMParser is not available */ |
4821 if (!doc || !doc.documentElement) { | 4817 if (!doc || !doc.documentElement) { |
4822 doc = implementation.createHTMLDocument(''); | 4818 doc = implementation.createHTMLDocument(''); |
4823 var _doc = doc, | 4819 var _doc = doc, |
4824 body = _doc.body; | 4820 body = _doc.body; |
4825 | 4821 |
4826 body.parentNode.removeChild(body.parentNode.firstElementChild); | 4822 body.parentNode.removeChild(body.parentNode.firstElementChild); |
4827 body.outerHTML = dirtyPayload; | 4823 body.outerHTML = dirtyPayload; |
4828 } | 4824 } |
4829 | 4825 |
4830 if (dirty && leadingWhitespace) { | 4826 if (dirty && leadingWhitespace) { |
4831 doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null); | 4827 doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null); |
4832 } | 4828 } |
4833 | 4829 |
4834 /* Work on whole document or just its body */ | 4830 /* Work on whole document or just its body */ |
4835 return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; | 4831 return doc.getElementsByTagName(WHOLE_DOCUMENT ? 'html' : 'body')[0]; |
4836 }; | 4832 }; |
4837 | 4833 |
4838 /** | 4834 /** |
4839 * _createIterator | 4835 * _createIterator |
4840 * | 4836 * |
4841 * @param {Document} root document/fragment to create iterator for | 4837 * @param {Document} root document/fragment to create iterator for |
4842 * @return {Iterator} iterator instance | 4838 * @return {Iterator} iterator instance |
4843 */ | 4839 */ |
4844 var _createIterator = function _createIterator(root) { | 4840 var _createIterator = function _createIterator(root) { |
4845 return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () { | 4841 let whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT; |
4846 return NodeFilter.FILTER_ACCEPT; | 4842 let filter = function () { |
4847 }, false); | 4843 return NodeFilter.FILTER_ACCEPT; |
4844 }; | |
4845 if(root.ownerDocument) { | |
4846 return root.ownerDocument.createNodeIterator(root,whatToShow,filter,false); | |
4847 } else { | |
4848 return root.createNodeIterator(root,whatToShow,filter,false); | |
4849 } | |
4848 }; | 4850 }; |
4849 | 4851 |
4850 /** | 4852 /** |
4851 * _isClobbered | 4853 * _isClobbered |
4852 * | 4854 * |
4853 * @param {Node} elm element to check for clobbering attacks | 4855 * @param {Node} elm element to check for clobbering attacks |
4854 * @return {Boolean} true if clobbered, false if safe | 4856 * @return {Boolean} true if clobbered, false if safe |
4855 */ | 4857 */ |
4856 var _isClobbered = function _isClobbered(elm) { | 4858 var _isClobbered = function _isClobbered(elm) { |
4857 if (elm instanceof Text || elm instanceof Comment) { | 4859 if (elm instanceof Text || elm instanceof Comment) { |
4858 return false; | 4860 return false; |
4859 } | 4861 } |
4860 | 4862 |
4861 if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function') { | 4863 if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function') { |
4862 return true; | 4864 return true; |
4863 } | 4865 } |
4864 | 4866 |
4865 return false; | 4867 return false; |
4866 }; | 4868 }; |
4867 | 4869 |
4868 /** | 4870 /** |
4869 * _isNode | 4871 * _isNode |
4870 * | 4872 * |
4871 * @param {Node} obj object to check whether it's a DOM node | 4873 * @param {Node} obj object to check whether it's a DOM node |
4872 * @return {Boolean} true is object is a DOM node | 4874 * @return {Boolean} true is object is a DOM node |
4873 */ | 4875 */ |
4874 var _isNode = function _isNode(object) { | 4876 var _isNode = function _isNode(object) { |
4875 return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; | 4877 return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; |
4876 }; | 4878 }; |
4877 | 4879 |
4878 /** | 4880 /** |
4879 * _executeHook | 4881 * _executeHook |
4880 * Execute user configurable hooks | 4882 * Execute user configurable hooks |
4882 * @param {String} entryPoint Name of the hook's entry point | 4884 * @param {String} entryPoint Name of the hook's entry point |
4883 * @param {Node} currentNode node to work on with the hook | 4885 * @param {Node} currentNode node to work on with the hook |
4884 * @param {Object} data additional hook parameters | 4886 * @param {Object} data additional hook parameters |
4885 */ | 4887 */ |
4886 var _executeHook = function _executeHook(entryPoint, currentNode, data) { | 4888 var _executeHook = function _executeHook(entryPoint, currentNode, data) { |
4887 if (!hooks[entryPoint]) { | 4889 if (!hooks[entryPoint]) { |
4888 return; | 4890 return; |
4889 } | 4891 } |
4890 | 4892 |
4891 arrayForEach(hooks[entryPoint], function (hook) { | 4893 arrayForEach(hooks[entryPoint], function (hook) { |
4892 hook.call(DOMPurify, currentNode, data, CONFIG); | 4894 hook.call(DOMPurify, currentNode, data, CONFIG); |
4893 }); | 4895 }); |
4894 }; | 4896 }; |
4895 | 4897 |
4896 /** | 4898 /** |
4897 * _sanitizeElements | 4899 * _sanitizeElements |
4898 * | 4900 * |
4902 * | 4904 * |
4903 * @param {Node} currentNode to check for permission to exist | 4905 * @param {Node} currentNode to check for permission to exist |
4904 * @return {Boolean} true if node was killed, false if left alive | 4906 * @return {Boolean} true if node was killed, false if left alive |
4905 */ | 4907 */ |
4906 var _sanitizeElements = function _sanitizeElements(currentNode) { | 4908 var _sanitizeElements = function _sanitizeElements(currentNode) { |
4907 var content = void 0; | 4909 var content = void 0; |
4908 | 4910 |
4909 /* Execute a hook if present */ | 4911 /* Execute a hook if present */ |
4910 _executeHook('beforeSanitizeElements', currentNode, null); | 4912 _executeHook('beforeSanitizeElements', currentNode, null); |
4911 | 4913 |
4912 /* Check if element is clobbered or can clobber */ | 4914 /* Check if element is clobbered or can clobber */ |
4913 if (_isClobbered(currentNode)) { | 4915 if (_isClobbered(currentNode)) { |
4914 _forceRemove(currentNode); | 4916 _forceRemove(currentNode); |
4915 return true; | 4917 return true; |
4916 } | 4918 } |
4917 | 4919 |
4918 /* Check if tagname contains Unicode */ | 4920 /* Check if tagname contains Unicode */ |
4919 if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) { | 4921 if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) { |
4920 _forceRemove(currentNode); | 4922 _forceRemove(currentNode); |
4921 return true; | 4923 return true; |
4922 } | 4924 } |
4923 | 4925 |
4924 /* Now let's check the element's type and name */ | 4926 /* Now let's check the element's type and name */ |
4925 var tagName = stringToLowerCase(currentNode.nodeName); | 4927 var tagName = stringToLowerCase(currentNode.nodeName); |
4926 | 4928 |
4927 /* Execute a hook if present */ | 4929 /* Execute a hook if present */ |
4928 _executeHook('uponSanitizeElement', currentNode, { | 4930 _executeHook('uponSanitizeElement', currentNode, { |
4929 tagName: tagName, | 4931 tagName: tagName, |
4930 allowedTags: ALLOWED_TAGS | 4932 allowedTags: ALLOWED_TAGS |
4931 }); | 4933 }); |
4932 | 4934 |
4933 /* Detect mXSS attempts abusing namespace confusion */ | 4935 /* Detect mXSS attempts abusing namespace confusion */ |
4934 if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { | 4936 if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { |
4935 _forceRemove(currentNode); | 4937 _forceRemove(currentNode); |
4936 return true; | 4938 return true; |
4937 } | 4939 } |
4938 | 4940 |
4939 /* Remove element if anything forbids its presence */ | 4941 /* Remove element if anything forbids its presence */ |
4940 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { | 4942 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { |
4941 /* Keep content except for bad-listed elements */ | 4943 /* Keep content except for bad-listed elements */ |
4942 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) { | 4944 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) { |
4943 var parentNode = getParentNode(currentNode); | 4945 var parentNode = getParentNode(currentNode); |
4944 var childNodes = getChildNodes(currentNode); | 4946 var childNodes = getChildNodes(currentNode); |
4945 var childCount = childNodes.length; | 4947 var childCount = childNodes.length; |
4946 for (var i = childCount - 1; i >= 0; --i) { | 4948 for (var i = childCount - 1; i >= 0; --i) { |
4947 parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); | 4949 parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); |
4948 } | 4950 } |
4949 } | 4951 } |
4950 | 4952 |
4951 _forceRemove(currentNode); | 4953 _forceRemove(currentNode); |
4952 return true; | 4954 return true; |
4953 } | 4955 } |
4954 | 4956 |
4955 /* Check whether element has a valid namespace */ | 4957 /* Check whether element has a valid namespace */ |
4956 if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) { | 4958 if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) { |
4957 _forceRemove(currentNode); | 4959 _forceRemove(currentNode); |
4958 return true; | 4960 return true; |
4959 } | 4961 } |
4960 | 4962 |
4961 if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) { | 4963 if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) { |
4962 _forceRemove(currentNode); | 4964 _forceRemove(currentNode); |
4963 return true; | 4965 return true; |
4964 } | 4966 } |
4965 | 4967 |
4966 /* Sanitize element content to be template-safe */ | 4968 /* Sanitize element content to be template-safe */ |
4967 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) { | 4969 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) { |
4968 /* Get the element's text content */ | 4970 /* Get the element's text content */ |
4969 content = currentNode.textContent; | 4971 content = currentNode.textContent; |
4970 content = stringReplace(content, MUSTACHE_EXPR$$1, ' '); | 4972 content = stringReplace(content, MUSTACHE_EXPR$$1, ' '); |
4971 content = stringReplace(content, ERB_EXPR$$1, ' '); | 4973 content = stringReplace(content, ERB_EXPR$$1, ' '); |
4972 if (currentNode.textContent !== content) { | 4974 if (currentNode.textContent !== content) { |
4973 arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() }); | 4975 arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() }); |
4974 currentNode.textContent = content; | 4976 currentNode.textContent = content; |
4975 } | 4977 } |
4976 } | 4978 } |
4977 | 4979 |
4978 /* Execute a hook if present */ | 4980 /* Execute a hook if present */ |
4979 _executeHook('afterSanitizeElements', currentNode, null); | 4981 _executeHook('afterSanitizeElements', currentNode, null); |
4980 | 4982 |
4981 return false; | 4983 return false; |
4982 }; | 4984 }; |
4983 | 4985 |
4984 /** | 4986 /** |
4985 * _isValidAttribute | 4987 * _isValidAttribute |
4986 * | 4988 * |
4989 * @param {string} value Attribute value. | 4991 * @param {string} value Attribute value. |
4990 * @return {Boolean} Returns true if `value` is valid, otherwise false. | 4992 * @return {Boolean} Returns true if `value` is valid, otherwise false. |
4991 */ | 4993 */ |
4992 // eslint-disable-next-line complexity | 4994 // eslint-disable-next-line complexity |
4993 var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { | 4995 var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { |
4994 /* Make sure attribute cannot clobber */ | 4996 /* Make sure attribute cannot clobber */ |
4995 if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) { | 4997 if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) { |
4996 return false; | 4998 return false; |
4997 } | 4999 } |
4998 | 5000 |
4999 /* Allow valid data-* attributes: At least one character after "-" | 5001 /* Allow valid data-* attributes: At least one character after "-" |
5000 (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes) | 5002 (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes) |
5001 XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804) | 5003 XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804) |
5002 We don't need to check the value; it's always URI safe. */ | 5004 We don't need to check the value; it's always URI safe. */ |
5003 if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) { | 5005 if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) { |
5004 return false; | 5006 return false; |
5005 | 5007 |
5006 /* Check value is safe. First, is attr inert? If so, is safe */ | 5008 /* Check value is safe. First, is attr inert? If so, is safe */ |
5007 } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if (!value) ; else { | 5009 } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if (!value) ; else { |
5008 return false; | 5010 return false; |
5009 } | 5011 } |
5010 | 5012 |
5011 return true; | 5013 return true; |
5012 }; | 5014 }; |
5013 | 5015 |
5014 /** | 5016 /** |
5015 * _sanitizeAttributes | 5017 * _sanitizeAttributes |
5016 * | 5018 * |
5020 * @protect setAttribute | 5022 * @protect setAttribute |
5021 * | 5023 * |
5022 * @param {Node} currentNode to sanitize | 5024 * @param {Node} currentNode to sanitize |
5023 */ | 5025 */ |
5024 var _sanitizeAttributes = function _sanitizeAttributes(currentNode) { | 5026 var _sanitizeAttributes = function _sanitizeAttributes(currentNode) { |
5025 var attr = void 0; | 5027 var attr = void 0; |
5026 var value = void 0; | 5028 var value = void 0; |
5027 var lcName = void 0; | 5029 var lcName = void 0; |
5028 var l = void 0; | 5030 var l = void 0; |
5029 /* Execute a hook if present */ | 5031 /* Execute a hook if present */ |
5030 _executeHook('beforeSanitizeAttributes', currentNode, null); | 5032 _executeHook('beforeSanitizeAttributes', currentNode, null); |
5031 | 5033 |
5032 var attributes = currentNode.attributes; | 5034 var attributes = currentNode.attributes; |
5033 | 5035 |
5034 /* Check if we have attributes; if not we might have a text node */ | 5036 /* Check if we have attributes; if not we might have a text node */ |
5035 | 5037 |
5036 if (!attributes) { | 5038 if (!attributes) { |
5037 return; | 5039 return; |
5038 } | 5040 } |
5039 | 5041 |
5040 var hookEvent = { | 5042 var hookEvent = { |
5041 attrName: '', | 5043 attrName: '', |
5042 attrValue: '', | 5044 attrValue: '', |
5043 keepAttr: true, | 5045 keepAttr: true, |
5044 allowedAttributes: ALLOWED_ATTR | 5046 allowedAttributes: ALLOWED_ATTR |
5045 }; | 5047 }; |
5046 l = attributes.length; | 5048 l = attributes.length; |
5047 | 5049 |
5048 /* Go backwards over all attributes; safely remove bad ones */ | 5050 /* Go backwards over all attributes; safely remove bad ones */ |
5049 while (l--) { | 5051 while (l--) { |
5050 attr = attributes[l]; | 5052 attr = attributes[l]; |
5051 var _attr = attr, | 5053 var _attr = attr, |
5052 name = _attr.name, | 5054 name = _attr.name, |
5053 namespaceURI = _attr.namespaceURI; | 5055 namespaceURI = _attr.namespaceURI; |
5054 | 5056 |
5055 value = stringTrim(attr.value); | 5057 value = stringTrim(attr.value); |
5056 lcName = stringToLowerCase(name); | 5058 lcName = stringToLowerCase(name); |
5057 | 5059 |
5058 /* Execute a hook if present */ | 5060 /* Execute a hook if present */ |
5059 hookEvent.attrName = lcName; | 5061 hookEvent.attrName = lcName; |
5060 hookEvent.attrValue = value; | 5062 hookEvent.attrValue = value; |
5061 hookEvent.keepAttr = true; | 5063 hookEvent.keepAttr = true; |
5062 hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set | 5064 hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set |
5063 _executeHook('uponSanitizeAttribute', currentNode, hookEvent); | 5065 _executeHook('uponSanitizeAttribute', currentNode, hookEvent); |
5064 value = hookEvent.attrValue; | 5066 value = hookEvent.attrValue; |
5065 /* Did the hooks approve of the attribute? */ | 5067 /* Did the hooks approve of the attribute? */ |
5066 if (hookEvent.forceKeepAttr) { | 5068 if (hookEvent.forceKeepAttr) { |
5067 continue; | 5069 continue; |
5068 } | 5070 } |
5069 | 5071 |
5070 /* Remove attribute */ | 5072 /* Remove attribute */ |
5071 _removeAttribute(name, currentNode); | 5073 _removeAttribute(name, currentNode); |
5072 | 5074 |
5073 /* Did the hooks approve of the attribute? */ | 5075 /* Did the hooks approve of the attribute? */ |
5074 if (!hookEvent.keepAttr) { | 5076 if (!hookEvent.keepAttr) { |
5075 continue; | 5077 continue; |
5076 } | 5078 } |
5077 | 5079 |
5078 /* Work around a security issue in jQuery 3.0 */ | 5080 /* Work around a security issue in jQuery 3.0 */ |
5079 if (regExpTest(/\/>/i, value)) { | 5081 if (regExpTest(/\/>/i, value)) { |
5080 _removeAttribute(name, currentNode); | 5082 _removeAttribute(name, currentNode); |
5081 continue; | 5083 continue; |
5082 } | 5084 } |
5083 | 5085 |
5084 /* Sanitize attribute content to be template-safe */ | 5086 /* Sanitize attribute content to be template-safe */ |
5085 if (SAFE_FOR_TEMPLATES) { | 5087 if (SAFE_FOR_TEMPLATES) { |
5086 value = stringReplace(value, MUSTACHE_EXPR$$1, ' '); | 5088 value = stringReplace(value, MUSTACHE_EXPR$$1, ' '); |
5087 value = stringReplace(value, ERB_EXPR$$1, ' '); | 5089 value = stringReplace(value, ERB_EXPR$$1, ' '); |
5088 } | 5090 } |
5089 | 5091 |
5090 /* Is `value` valid for this attribute? */ | 5092 /* Is `value` valid for this attribute? */ |
5091 var lcTag = currentNode.nodeName.toLowerCase(); | 5093 var lcTag = currentNode.nodeName.toLowerCase(); |
5092 if (!_isValidAttribute(lcTag, lcName, value)) { | 5094 if (!_isValidAttribute(lcTag, lcName, value)) { |
5093 continue; | 5095 continue; |
5094 } | 5096 } |
5095 | 5097 |
5096 /* Handle invalid data-* attribute set by try-catching it */ | 5098 /* Handle invalid data-* attribute set by try-catching it */ |
5097 try { | 5099 try { |
5098 if (namespaceURI) { | 5100 if (namespaceURI) { |
5099 currentNode.setAttributeNS(namespaceURI, name, value); | 5101 currentNode.setAttributeNS(namespaceURI, name, value); |
5100 } else { | 5102 } else { |
5101 /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */ | 5103 /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */ |
5102 currentNode.setAttribute(name, value); | 5104 currentNode.setAttribute(name, value); |
5103 } | 5105 } |
5104 | 5106 |
5105 arrayPop(DOMPurify.removed); | 5107 arrayPop(DOMPurify.removed); |
5106 } catch (_) {} | 5108 } catch (_) {} |
5107 } | 5109 } |
5108 | 5110 |
5109 /* Execute a hook if present */ | 5111 /* Execute a hook if present */ |
5110 _executeHook('afterSanitizeAttributes', currentNode, null); | 5112 _executeHook('afterSanitizeAttributes', currentNode, null); |
5111 }; | 5113 }; |
5112 | 5114 |
5113 /** | 5115 /** |
5114 * _sanitizeShadowDOM | 5116 * _sanitizeShadowDOM |
5115 * | 5117 * |
5116 * @param {DocumentFragment} fragment to iterate over recursively | 5118 * @param {DocumentFragment} fragment to iterate over recursively |
5117 */ | 5119 */ |
5118 var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { | 5120 var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { |
5119 var shadowNode = void 0; | 5121 var shadowNode = void 0; |
5120 var shadowIterator = _createIterator(fragment); | 5122 var shadowIterator = _createIterator(fragment); |
5121 | 5123 |
5122 /* Execute a hook if present */ | 5124 /* Execute a hook if present */ |
5123 _executeHook('beforeSanitizeShadowDOM', fragment, null); | 5125 _executeHook('beforeSanitizeShadowDOM', fragment, null); |
5124 | 5126 |
5125 while (shadowNode = shadowIterator.nextNode()) { | 5127 while (shadowNode = shadowIterator.nextNode()) { |
5126 /* Execute a hook if present */ | 5128 /* Execute a hook if present */ |
5127 _executeHook('uponSanitizeShadowNode', shadowNode, null); | 5129 _executeHook('uponSanitizeShadowNode', shadowNode, null); |
5128 | 5130 |
5129 /* Sanitize tags and elements */ | 5131 /* Sanitize tags and elements */ |
5130 if (_sanitizeElements(shadowNode)) { | 5132 if (_sanitizeElements(shadowNode)) { |
5131 continue; | 5133 continue; |
5132 } | 5134 } |
5133 | 5135 |
5134 /* Deep shadow DOM detected */ | 5136 /* Deep shadow DOM detected */ |
5135 if (shadowNode.content instanceof DocumentFragment) { | 5137 if (shadowNode.content instanceof DocumentFragment) { |
5136 _sanitizeShadowDOM(shadowNode.content); | 5138 _sanitizeShadowDOM(shadowNode.content); |
5137 } | 5139 } |
5138 | 5140 |
5139 /* Check attributes, sanitize if necessary */ | 5141 /* Check attributes, sanitize if necessary */ |
5140 _sanitizeAttributes(shadowNode); | 5142 _sanitizeAttributes(shadowNode); |
5141 } | 5143 } |
5142 | 5144 |
5143 /* Execute a hook if present */ | 5145 /* Execute a hook if present */ |
5144 _executeHook('afterSanitizeShadowDOM', fragment, null); | 5146 _executeHook('afterSanitizeShadowDOM', fragment, null); |
5145 }; | 5147 }; |
5146 | 5148 |
5147 /** | 5149 /** |
5148 * Sanitize | 5150 * Sanitize |
5149 * Public method providing core sanitation functionality | 5151 * Public method providing core sanitation functionality |
5151 * @param {String|Node} dirty string or DOM node | 5153 * @param {String|Node} dirty string or DOM node |
5152 * @param {Object} configuration object | 5154 * @param {Object} configuration object |
5153 */ | 5155 */ |
5154 // eslint-disable-next-line complexity | 5156 // eslint-disable-next-line complexity |
5155 DOMPurify.sanitize = function (dirty, cfg) { | 5157 DOMPurify.sanitize = function (dirty, cfg) { |
5156 var body = void 0; | 5158 var body = void 0; |
5157 var importedNode = void 0; | 5159 var importedNode = void 0; |
5158 var currentNode = void 0; | 5160 var currentNode = void 0; |
5159 var oldNode = void 0; | 5161 var oldNode = void 0; |
5160 var returnNode = void 0; | 5162 var returnNode = void 0; |
5161 /* Make sure we have a string to sanitize. | 5163 /* Make sure we have a string to sanitize. |
5162 DO NOT return early, as this will return the wrong type if | 5164 DO NOT return early, as this will return the wrong type if |
5163 the user has requested a DOM object rather than a string */ | 5165 the user has requested a DOM object rather than a string */ |
5164 if (!dirty) { | 5166 if (!dirty) { |
5165 dirty = '<!-->'; | 5167 dirty = '<!-->'; |
5166 } | 5168 } |
5167 | 5169 |
5168 /* Stringify, in case dirty is an object */ | 5170 /* Stringify, in case dirty is an object */ |
5169 if (typeof dirty !== 'string' && !_isNode(dirty)) { | 5171 if (typeof dirty !== 'string' && !_isNode(dirty)) { |
5170 // eslint-disable-next-line no-negated-condition | 5172 // eslint-disable-next-line no-negated-condition |
5171 if (typeof dirty.toString !== 'function') { | 5173 if (typeof dirty.toString !== 'function') { |
5172 throw typeErrorCreate('toString is not a function'); | 5174 throw typeErrorCreate('toString is not a function'); |
5173 } else { | 5175 } else { |
5174 dirty = dirty.toString(); | 5176 dirty = dirty.toString(); |
5175 if (typeof dirty !== 'string') { | 5177 if (typeof dirty !== 'string') { |
5176 throw typeErrorCreate('dirty is not a string, aborting'); | 5178 throw typeErrorCreate('dirty is not a string, aborting'); |
5177 } | 5179 } |
5178 } | 5180 } |
5179 } | 5181 } |
5180 | 5182 |
5181 /* Check we can run. Otherwise fall back or ignore */ | 5183 /* Check we can run. Otherwise fall back or ignore */ |
5182 if (!DOMPurify.isSupported) { | 5184 if (!DOMPurify.isSupported) { |
5183 if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') { | 5185 if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') { |
5184 if (typeof dirty === 'string') { | 5186 if (typeof dirty === 'string') { |
5185 return window.toStaticHTML(dirty); | 5187 return window.toStaticHTML(dirty); |
5186 } | 5188 } |
5187 | 5189 |
5188 if (_isNode(dirty)) { | 5190 if (_isNode(dirty)) { |
5189 return window.toStaticHTML(dirty.outerHTML); | 5191 return window.toStaticHTML(dirty.outerHTML); |
5190 } | 5192 } |
5191 } | 5193 } |
5192 | 5194 |
5193 return dirty; | 5195 return dirty; |
5194 } | 5196 } |
5195 | 5197 |
5196 /* Assign config vars */ | 5198 /* Assign config vars */ |
5197 if (!SET_CONFIG) { | 5199 if (!SET_CONFIG) { |
5198 _parseConfig(cfg); | 5200 _parseConfig(cfg); |
5199 } | 5201 } |
5200 | 5202 |
5201 /* Clean up removed elements */ | 5203 /* Clean up removed elements */ |
5202 DOMPurify.removed = []; | 5204 DOMPurify.removed = []; |
5203 | 5205 |
5204 /* Check if dirty is correctly typed for IN_PLACE */ | 5206 /* Check if dirty is correctly typed for IN_PLACE */ |
5205 if (typeof dirty === 'string') { | 5207 if (typeof dirty === 'string') { |
5206 IN_PLACE = false; | 5208 IN_PLACE = false; |
5207 } | 5209 } |
5208 | 5210 |
5209 if (IN_PLACE) ; else if (dirty instanceof Node) { | 5211 if (IN_PLACE) ; else if (dirty instanceof Node) { |
5210 /* If dirty is a DOM element, append to an empty document to avoid | 5212 /* If dirty is a DOM element, append to an empty document to avoid |
5211 elements being stripped by the parser */ | 5213 elements being stripped by the parser */ |
5212 body = _initDocument('<!---->'); | 5214 body = _initDocument('<!---->'); |
5213 importedNode = body.ownerDocument.importNode(dirty, true); | 5215 importedNode = body.ownerDocument.importNode(dirty, true); |
5214 if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') { | 5216 if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') { |
5215 /* Node is already a body, use as is */ | 5217 /* Node is already a body, use as is */ |
5216 body = importedNode; | 5218 body = importedNode; |
5217 } else if (importedNode.nodeName === 'HTML') { | 5219 } else if (importedNode.nodeName === 'HTML') { |
5218 body = importedNode; | 5220 body = importedNode; |
5219 } else { | 5221 } else { |
5220 // eslint-disable-next-line unicorn/prefer-node-append | 5222 // eslint-disable-next-line unicorn/prefer-node-append |
5221 body.appendChild(importedNode); | 5223 body.appendChild(importedNode); |
5222 } | 5224 } |
5223 } else { | 5225 } else { |
5224 /* Exit directly if we have nothing to do */ | 5226 /* Exit directly if we have nothing to do */ |
5225 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && | 5227 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && |
5226 // eslint-disable-next-line unicorn/prefer-includes | 5228 // eslint-disable-next-line unicorn/prefer-includes |
5227 dirty.indexOf('<') === -1) { | 5229 dirty.indexOf('<') === -1) { |
5228 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty; | 5230 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty; |
5229 } | 5231 } |
5230 | 5232 |
5231 /* Initialize the document to work on */ | 5233 /* Initialize the document to work on */ |
5232 body = _initDocument(dirty); | 5234 body = _initDocument(dirty); |
5233 | 5235 |
5234 /* Check we have a DOM node from the data */ | 5236 /* Check we have a DOM node from the data */ |
5235 if (!body) { | 5237 if (!body) { |
5236 return RETURN_DOM ? null : emptyHTML; | 5238 return RETURN_DOM ? null : emptyHTML; |
5237 } | 5239 } |
5238 } | 5240 } |
5239 | 5241 |
5240 /* Remove first element node (ours) if FORCE_BODY is set */ | 5242 /* Remove first element node (ours) if FORCE_BODY is set */ |
5241 if (body && FORCE_BODY) { | 5243 if (body && FORCE_BODY) { |
5242 _forceRemove(body.firstChild); | 5244 _forceRemove(body.firstChild); |
5243 } | 5245 } |
5244 | 5246 |
5245 /* Get node iterator */ | 5247 /* Get node iterator */ |
5246 var nodeIterator = _createIterator(IN_PLACE ? dirty : body); | 5248 var nodeIterator = _createIterator(IN_PLACE ? dirty : body); |
5247 | 5249 |
5248 /* Now start iterating over the created document */ | 5250 /* Now start iterating over the created document */ |
5249 while (currentNode = nodeIterator.nextNode()) { | 5251 while (currentNode = nodeIterator.nextNode()) { |
5250 /* Fix IE's strange behavior with manipulated textNodes #89 */ | 5252 /* Fix IE's strange behavior with manipulated textNodes #89 */ |
5251 if (currentNode.nodeType === 3 && currentNode === oldNode) { | 5253 if (currentNode.nodeType === 3 && currentNode === oldNode) { |
5252 continue; | 5254 continue; |
5253 } | 5255 } |
5254 | 5256 |
5255 /* Sanitize tags and elements */ | 5257 /* Sanitize tags and elements */ |
5256 if (_sanitizeElements(currentNode)) { | 5258 if (_sanitizeElements(currentNode)) { |
5257 continue; | 5259 continue; |
5258 } | 5260 } |
5259 | 5261 |
5260 /* Shadow DOM detected, sanitize it */ | 5262 /* Shadow DOM detected, sanitize it */ |
5261 if (currentNode.content instanceof DocumentFragment) { | 5263 if (currentNode.content instanceof DocumentFragment) { |
5262 _sanitizeShadowDOM(currentNode.content); | 5264 _sanitizeShadowDOM(currentNode.content); |
5263 } | 5265 } |
5264 | 5266 |
5265 /* Check attributes, sanitize if necessary */ | 5267 /* Check attributes, sanitize if necessary */ |
5266 _sanitizeAttributes(currentNode); | 5268 _sanitizeAttributes(currentNode); |
5267 | 5269 |
5268 oldNode = currentNode; | 5270 oldNode = currentNode; |
5269 } | 5271 } |
5270 | 5272 |
5271 oldNode = null; | 5273 oldNode = null; |
5272 | 5274 |
5273 /* If we sanitized `dirty` in-place, return it. */ | 5275 /* If we sanitized `dirty` in-place, return it. */ |
5274 if (IN_PLACE) { | 5276 if (IN_PLACE) { |
5275 return dirty; | 5277 return dirty; |
5276 } | 5278 } |
5277 | 5279 |
5278 /* Return sanitized string or DOM */ | 5280 /* Return sanitized string or DOM */ |
5279 if (RETURN_DOM) { | 5281 if (RETURN_DOM) { |
5280 if (RETURN_DOM_FRAGMENT) { | 5282 if (RETURN_DOM_FRAGMENT) { |
5281 returnNode = createDocumentFragment.call(body.ownerDocument); | 5283 //returnNode = createDocumentFragment.call(body.ownerDocument); |
5282 | 5284 // untested |
5283 while (body.firstChild) { | 5285 returnNode = body.ownerDocument.createDocumentFragment.call(); |
5284 // eslint-disable-next-line unicorn/prefer-node-append | 5286 |
5285 returnNode.appendChild(body.firstChild); | 5287 while (body.firstChild) { |
5286 } | 5288 // eslint-disable-next-line unicorn/prefer-node-append |
5287 } else { | 5289 returnNode.appendChild(body.firstChild); |
5288 returnNode = body; | 5290 } |
5289 } | 5291 } else { |
5290 | 5292 returnNode = body; |
5291 if (RETURN_DOM_IMPORT) { | 5293 } |
5292 /* | 5294 |
5293 AdoptNode() is not used because internal state is not reset | 5295 if (RETURN_DOM_IMPORT) { |
5294 (e.g. the past names map of a HTMLFormElement), this is safe | 5296 /* |
5295 in theory but we would rather not risk another attack vector. | 5297 AdoptNode() is not used because internal state is not reset |
5296 The state that is cloned by importNode() is explicitly defined | 5298 (e.g. the past names map of a HTMLFormElement), this is safe |
5297 by the specs. | 5299 in theory but we would rather not risk another attack vector. |
5298 */ | 5300 The state that is cloned by importNode() is explicitly defined |
5299 returnNode = importNode.call(originalDocument, returnNode, true); | 5301 by the specs. |
5300 } | 5302 */ |
5301 | 5303 //returnNode = importNode.call(originalDocument, returnNode, true); |
5302 return returnNode; | 5304 // untested |
5303 } | 5305 returnNode = originalDocument.importNode(returnNode, true); |
5304 | 5306 } |
5305 var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML; | 5307 |
5306 | 5308 return returnNode; |
5307 /* Sanitize final string template-safe */ | 5309 } |
5308 if (SAFE_FOR_TEMPLATES) { | 5310 |
5309 serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' '); | 5311 var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML; |
5310 serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' '); | 5312 |
5311 } | 5313 /* Sanitize final string template-safe */ |
5312 | 5314 if (SAFE_FOR_TEMPLATES) { |
5313 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML; | 5315 serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' '); |
5316 serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' '); | |
5317 } | |
5318 | |
5319 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML; | |
5314 }; | 5320 }; |
5315 | 5321 |
5316 /** | 5322 /** |
5317 * Public method to set the configuration once | 5323 * Public method to set the configuration once |
5318 * setConfig | 5324 * setConfig |
5319 * | 5325 * |
5320 * @param {Object} cfg configuration object | 5326 * @param {Object} cfg configuration object |
5321 */ | 5327 */ |
5322 DOMPurify.setConfig = function (cfg) { | 5328 DOMPurify.setConfig = function (cfg) { |
5323 _parseConfig(cfg); | 5329 _parseConfig(cfg); |
5324 SET_CONFIG = true; | 5330 SET_CONFIG = true; |
5325 }; | 5331 }; |
5326 | 5332 |
5327 /** | 5333 /** |
5328 * Public method to remove the configuration | 5334 * Public method to remove the configuration |
5329 * clearConfig | 5335 * clearConfig |
5330 * | 5336 * |
5331 */ | 5337 */ |
5332 DOMPurify.clearConfig = function () { | 5338 DOMPurify.clearConfig = function () { |
5333 CONFIG = null; | 5339 CONFIG = null; |
5334 SET_CONFIG = false; | 5340 SET_CONFIG = false; |
5335 }; | 5341 }; |
5336 | 5342 |
5337 /** | 5343 /** |
5338 * Public method to check if an attribute value is valid. | 5344 * Public method to check if an attribute value is valid. |
5339 * Uses last set config, if any. Otherwise, uses config defaults. | 5345 * Uses last set config, if any. Otherwise, uses config defaults. |
5343 * @param {string} attr Attribute name. | 5349 * @param {string} attr Attribute name. |
5344 * @param {string} value Attribute value. | 5350 * @param {string} value Attribute value. |
5345 * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false. | 5351 * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false. |
5346 */ | 5352 */ |
5347 DOMPurify.isValidAttribute = function (tag, attr, value) { | 5353 DOMPurify.isValidAttribute = function (tag, attr, value) { |
5348 /* Initialize shared config vars if necessary. */ | 5354 /* Initialize shared config vars if necessary. */ |
5349 if (!CONFIG) { | 5355 if (!CONFIG) { |
5350 _parseConfig({}); | 5356 _parseConfig({}); |
5351 } | 5357 } |
5352 | 5358 |
5353 var lcTag = stringToLowerCase(tag); | 5359 var lcTag = stringToLowerCase(tag); |
5354 var lcName = stringToLowerCase(attr); | 5360 var lcName = stringToLowerCase(attr); |
5355 return _isValidAttribute(lcTag, lcName, value); | 5361 return _isValidAttribute(lcTag, lcName, value); |
5356 }; | 5362 }; |
5357 | 5363 |
5358 /** | 5364 /** |
5359 * AddHook | 5365 * AddHook |
5360 * Public method to add DOMPurify hooks | 5366 * Public method to add DOMPurify hooks |
5361 * | 5367 * |
5362 * @param {String} entryPoint entry point for the hook to add | 5368 * @param {String} entryPoint entry point for the hook to add |
5363 * @param {Function} hookFunction function to execute | 5369 * @param {Function} hookFunction function to execute |
5364 */ | 5370 */ |
5365 DOMPurify.addHook = function (entryPoint, hookFunction) { | 5371 DOMPurify.addHook = function (entryPoint, hookFunction) { |
5366 if (typeof hookFunction !== 'function') { | 5372 if (typeof hookFunction !== 'function') { |
5367 return; | 5373 return; |
5368 } | 5374 } |
5369 | 5375 |
5370 hooks[entryPoint] = hooks[entryPoint] || []; | 5376 hooks[entryPoint] = hooks[entryPoint] || []; |
5371 arrayPush(hooks[entryPoint], hookFunction); | 5377 arrayPush(hooks[entryPoint], hookFunction); |
5372 }; | 5378 }; |
5373 | 5379 |
5374 /** | 5380 /** |
5375 * RemoveHook | 5381 * RemoveHook |
5376 * Public method to remove a DOMPurify hook at a given entryPoint | 5382 * Public method to remove a DOMPurify hook at a given entryPoint |
5377 * (pops it from the stack of hooks if more are present) | 5383 * (pops it from the stack of hooks if more are present) |
5378 * | 5384 * |
5379 * @param {String} entryPoint entry point for the hook to remove | 5385 * @param {String} entryPoint entry point for the hook to remove |
5380 */ | 5386 */ |
5381 DOMPurify.removeHook = function (entryPoint) { | 5387 DOMPurify.removeHook = function (entryPoint) { |
5382 if (hooks[entryPoint]) { | 5388 if (hooks[entryPoint]) { |
5383 arrayPop(hooks[entryPoint]); | 5389 arrayPop(hooks[entryPoint]); |
5384 } | 5390 } |
5385 }; | 5391 }; |
5386 | 5392 |
5387 /** | 5393 /** |
5388 * RemoveHooks | 5394 * RemoveHooks |
5389 * Public method to remove all DOMPurify hooks at a given entryPoint | 5395 * Public method to remove all DOMPurify hooks at a given entryPoint |
5390 * | 5396 * |
5391 * @param {String} entryPoint entry point for the hooks to remove | 5397 * @param {String} entryPoint entry point for the hooks to remove |
5392 */ | 5398 */ |
5393 DOMPurify.removeHooks = function (entryPoint) { | 5399 DOMPurify.removeHooks = function (entryPoint) { |
5394 if (hooks[entryPoint]) { | 5400 if (hooks[entryPoint]) { |
5395 hooks[entryPoint] = []; | 5401 hooks[entryPoint] = []; |
5396 } | 5402 } |
5397 }; | 5403 }; |
5398 | 5404 |
5399 /** | 5405 /** |
5400 * RemoveAllHooks | 5406 * RemoveAllHooks |
5401 * Public method to remove all DOMPurify hooks | 5407 * Public method to remove all DOMPurify hooks |
5402 * | 5408 * |
5403 */ | 5409 */ |
5404 DOMPurify.removeAllHooks = function () { | 5410 DOMPurify.removeAllHooks = function () { |
5405 hooks = {}; | 5411 hooks = {}; |
5406 }; | 5412 }; |
5407 | 5413 |
5408 return DOMPurify; | 5414 return DOMPurify; |
5409 } | 5415 } |
5410 | 5416 |