Socket
Socket
Sign inDemoInstall

dompurify

Package Overview
Dependencies
0
Maintainers
2
Versions
118
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.6.2 to 0.6.3

2

bower.json
{
"name": "DOMPurify",
"version": "0.6.2",
"version": "0.6.3",
"homepage": "https://github.com/cure53/DOMPurify",

@@ -5,0 +5,0 @@ "author": "Cure53 <info@cure53.de>",

@@ -15,3 +15,3 @@ {

"description": "DOMPurify is a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. It's written in JavaScript and works in all modern browsers (Safari, Opera (15+), Internet Explorer (10+), Firefox and Chrome - as well as almost anything else using Blink or WebKit). DOMPurify is written by security people who have vast background in web attacks and XSS. Fear not.",
"version": "0.6.2",
"version": "0.6.3",
"main": "purify.js",

@@ -18,0 +18,0 @@ "directories": {

@@ -19,470 +19,496 @@ /* jshint boss: true */

/**
* Version label, exposed for easier checks
* Version label, exposed for easier checks
* if DOMPurfy is up to date or not
*/
DOMPurify.version = '0.6.2';
DOMPurify.version = '0.6.3';
/**
* sanitize
* Public method providing core sanitation functionality
*
* @param {mixed} dirty string or DOM
* @param {Object} configuration object
* Expose whether this browser supports running the full DOMPurify.
*/
DOMPurify.sanitize = function(dirty, cfg) {
DOMPurify.isSupported =
typeof document.implementation.createHTMLDocument !== 'undefined' &&
document.documentMode !== 9;
/**
* We consider the elements and attributes below to be safe. Ideally
* don't add any new ones but feel free to remove unwanted ones.
*/
/* Add properties to a lookup table */
var _addToSet = function(set, array) {
var l = array.length;
while (l--) {
set[array[l]] = true;
}
return set;
};
/* allowed element names */
var ALLOWED_TAGS = [
/* Shallow clone an object */
var _cloneObj = function(object) {
var newObject = {};
var property;
for (property in object) {
if (object.hasOwnProperty(property)) {
newObject[property] = object[property];
}
}
return newObject;
};
// HTML
'a','abbr','acronym','address','area','article','aside','audio','b',
'bdi','bdo','big','blink','blockquote','body','br','button','canvas',
'caption','center','cite','code','col','colgroup','content','data',
'datalist','dd','decorator','del','details','dfn','dir','div','dl','dt',
'element','em','fieldset','figcaption','figure','font','footer','form',
'h1','h2','h3','h4','h5','h6','head','header','hgroup','hr','html','i',
'img','input','ins','kbd','label','legend','li','main','map','mark',
'marquee','menu','menuitem','meter','nav','nobr','ol','optgroup',
'option','output','p','pre','progress','q','rp','rt','ruby','s','samp',
'section','select','shadow','small','source','spacer','span','strike',
'strong','style','sub','summary','sup','table','tbody','td','template',
'textarea','tfoot','th','thead','time','tr','track','tt','u','ul','var',
'video','wbr',
/**
* We consider the elements and attributes below to be safe. Ideally
* don't add any new ones but feel free to remove unwanted ones.
*/
// SVG
'svg','altglyph','altglyphdef','altglyphitem','animatecolor',
'animatemotion','animatetransform','circle','clippath','defs','desc',
'ellipse','font','g','glyph','glyphref','hkern','image','line',
'lineargradient','marker','mask','metadata','mpath','path','pattern',
'polygon','polyline','radialgradient','rect','stop','switch','symbol',
'text','textpath','title','tref','tspan','view','vkern',
/* allowed element names */
var ALLOWED_TAGS = null;
var DEFAULT_ALLOWED_TAGS = _addToSet({}, [
//MathML
'math','menclose','merror','mfenced','mfrac','mglyph','mi','mlabeledtr',
'mmuliscripts','mn','mo','mover','mpadded','mphantom','mroot','mrow',
'ms','mpspace','msqrt','mystyle','msub','msup','msubsup','mtable','mtd',
'mtext','mtr','munder','munderover',
// HTML
'a','abbr','acronym','address','area','article','aside','audio','b',
'bdi','bdo','big','blink','blockquote','body','br','button','canvas',
'caption','center','cite','code','col','colgroup','content','data',
'datalist','dd','decorator','del','details','dfn','dir','div','dl','dt',
'element','em','fieldset','figcaption','figure','font','footer','form',
'h1','h2','h3','h4','h5','h6','head','header','hgroup','hr','html','i',
'img','input','ins','kbd','label','legend','li','main','map','mark',
'marquee','menu','menuitem','meter','nav','nobr','ol','optgroup',
'option','output','p','pre','progress','q','rp','rt','ruby','s','samp',
'section','select','shadow','small','source','spacer','span','strike',
'strong','style','sub','summary','sup','table','tbody','td','template',
'textarea','tfoot','th','thead','time','tr','track','tt','u','ul','var',
'video','wbr',
//Text
'#text'
];
// SVG
'svg','altglyph','altglyphdef','altglyphitem','animatecolor',
'animatemotion','animatetransform','circle','clippath','defs','desc',
'ellipse','font','g','glyph','glyphref','hkern','image','line',
'lineargradient','marker','mask','metadata','mpath','path','pattern',
'polygon','polyline','radialgradient','rect','stop','switch','symbol',
'text','textpath','title','tref','tspan','view','vkern',
/* Decide if custom data attributes are okay */
var ALLOW_DATA_ATTR = true;
//MathML
'math','menclose','merror','mfenced','mfrac','mglyph','mi','mlabeledtr',
'mmuliscripts','mn','mo','mover','mpadded','mphantom','mroot','mrow',
'ms','mpspace','msqrt','mystyle','msub','msup','msubsup','mtable','mtd',
'mtext','mtr','munder','munderover',
/* Allowed attribute names */
var ALLOWED_ATTR = [
//Text
'#text'
]);
// HTML
'accept','action','align','alt','autocomplete','bgcolor','border',
'checked','cite','class','color','cols','colspan','coords','datetime',
'default','dir','disabled','download','enctype','for','headers','height',
'hidden','high','href','hreflang','id','ismap','label','lang','list',
'loop', 'low','max','maxlength','media','method','min','multiple',
'name','novalidate','open','optimum','pattern','placeholder','poster',
'preload','pubdate','radiogroup','readonly','rel','required','rev',
'reversed','rows','rowspan','spellcheck','scope','selected','shape',
'size','span','srclang','start','src','step','style','summary','tabindex',
'title','type','usemap','valign','value','width','xmlns',
/* Allowed attribute names */
var ALLOWED_ATTR = null;
var DEFAULT_ALLOWED_ATTR = _addToSet({}, [
// SVG
'accent-height','accumulate','additivive','alignment-baseline',
'ascent','azimuth','baseline-shift','bias','clip','clip-path',
'clip-rule','color','color-interpolation','color-interpolation-filters',
'color-profile','color-rendering','cx','cy','d','dy','dy','direction',
'display','divisor','dur','elevation','end','fill','fill-opacity',
'fill-rule','filter','flood-color','flood-opacity','font-family',
'font-size','font-size-adjust','font-stretch','font-style','font-variant',
'front-weight','image-rendering','in','in2','k1','k2','k3','k4','kerning',
'letter-spacing','lighting-color','local','marker-end','marker-mid',
'marker-start','max','mask','mode','min','operator','opacity','order',
'overflow','paint-order','path','points','r','rx','ry','radius','restart',
'scale','seed','shape-rendering','stop-color','stop-opacity',
'stroke-dasharray','stroke-dashoffset','stroke-linecap','stroke-linejoin',
'stroke-miterlimit','stroke-opacity','stroke','stroke-width','transform',
'text-anchor','text-decoration','text-rendering','u1','u2','viewbox',
'visibility','word-spacing','wrap','writing-mode','x','x1','x2','y',
'y1','y2','z',
// HTML
'accept','action','align','alt','autocomplete','bgcolor','border',
'checked','cite','class','color','cols','colspan','coords','datetime',
'default','dir','disabled','download','enctype','for','headers','height',
'hidden','high','href','hreflang','id','ismap','label','lang','list',
'loop', 'low','max','maxlength','media','method','min','multiple',
'name','novalidate','open','optimum','pattern','placeholder','poster',
'preload','pubdate','radiogroup','readonly','rel','required','rev',
'reversed','rows','rowspan','spellcheck','scope','selected','shape',
'size','span','srclang','start','src','step','style','summary','tabindex',
'title','type','usemap','valign','value','width','xmlns',
// MathML
'accent','accentunder','bevelled','close','columnsalign','columnlines',
'columnspan','denomalign','depth','display','displaystyle','fence',
'frame','largeop','length','linethickness','lspace','lquote',
'mathbackground','mathcolor','mathsize','mathvariant','maxsize',
'minsize','movablelimits','notation','numalign','open','rowalign',
'rowlines','rowspacing','rowspan','rspace','rquote','scriptlevel',
'scriptminsize','scriptsizemultiplier','selection','separator',
'separators','stretchy','subscriptshift','supscriptshift','symmetric',
'voffset',
// SVG
'accent-height','accumulate','additivive','alignment-baseline',
'ascent','azimuth','baseline-shift','bias','clip','clip-path',
'clip-rule','color','color-interpolation','color-interpolation-filters',
'color-profile','color-rendering','cx','cy','d','dy','dy','direction',
'display','divisor','dur','elevation','end','fill','fill-opacity',
'fill-rule','filter','flood-color','flood-opacity','font-family',
'font-size','font-size-adjust','font-stretch','font-style','font-variant',
'front-weight','image-rendering','in','in2','k1','k2','k3','k4','kerning',
'letter-spacing','lighting-color','local','marker-end','marker-mid',
'marker-start','max','mask','mode','min','operator','opacity','order',
'overflow','paint-order','path','points','r','rx','ry','radius','restart',
'scale','seed','shape-rendering','stop-color','stop-opacity',
'stroke-dasharray','stroke-dashoffset','stroke-linecap','stroke-linejoin',
'stroke-miterlimit','stroke-opacity','stroke','stroke-width','transform',
'text-anchor','text-decoration','text-rendering','u1','u2','viewbox',
'visibility','word-spacing','wrap','writing-mode','x','x1','x2','y',
'y1','y2','z',
// XML
'xlink:href','xml:id','xlink:title','xml:space'
];
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
var FORBID_ATTR = [];
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
var FORBID_TAGS = [];
// MathML
'accent','accentunder','bevelled','close','columnsalign','columnlines',
'columnspan','denomalign','depth','display','displaystyle','fence',
'frame','largeop','length','linethickness','lspace','lquote',
'mathbackground','mathcolor','mathsize','mathvariant','maxsize',
'minsize','movablelimits','notation','numalign','open','rowalign',
'rowlines','rowspacing','rowspan','rspace','rquote','scriptlevel',
'scriptminsize','scriptsizemultiplier','selection','separator',
'separators','stretchy','subscriptshift','supscriptshift','symmetric',
'voffset',
/* Decide if document with <html>... should be returned */
var WHOLE_DOCUMENT = false;
// XML
'xlink:href','xml:id','xlink:title','xml:space'
]);
/* Decide if a DOM node or a string should be returned */
var RETURN_DOM = false;
/* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
var FORBID_TAGS = null;
/* Output should be safe for jQuery's $() factory? */
var SAFE_FOR_JQUERY = false;
/* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
var FORBID_ATTR = null;
/* Output should be free from DOM clobbering attacks? */
var SANITIZE_DOM = true;
/* Decide if custom data attributes are okay */
var ALLOW_DATA_ATTR = true;
/* Keep element content when removing element? */
var KEEP_CONTENT = true;
/* Output should be safe for jQuery's $() factory? */
var SAFE_FOR_JQUERY = false;
/* Tags to keep content from (when KEEP_CONTENT is true) */
var CONTENT_TAGS = [
'a','abbr','acronym','address','article','aside','b','bdi','bdo',
'big','blink','blockquote','caption','center','cite','code','col',
'dd','del','details','dfn','dir','div','dl','dt','em','figcaption',
'figure','footer','h1','h2','h3','h4','h5','h6','header','i','ins',
'kbd','label','legend','li','main','mark','marquee','nav','ol',
'output','p','pre','q','rp','rt','ruby','s','samp','section','small',
'span','strike','strong','sub','summary','sup','table','tbody','td',
'tfoot','th','thead','time','tr','tt','u','ul','var'
];
/* Decide if document with <html>... should be returned */
var WHOLE_DOCUMENT = false;
var DEBUG_OUTPUT = false;
/* Decide if a DOM node or a string should be returned */
var RETURN_DOM = false;
/* Ideally, do not touch anything below this line */
/* ______________________________________________ */
/* Output should be free from DOM clobbering attacks? */
var SANITIZE_DOM = true;
/**
* _parseConfig
*
* @param optional config literal
*/
var _parseConfig = function(cfg) {
/* Keep element content when removing element? */
var KEEP_CONTENT = true;
/* Shield configuration object from tampering */
if (typeof cfg !== 'object') {
cfg = {};
}
/* Tags to keep content from (when KEEP_CONTENT is true) */
var CONTENT_TAGS = _addToSet({}, [
'a','abbr','acronym','address','article','aside','b','bdi','bdo',
'big','blink','blockquote','caption','center','cite','code','col',
'dd','del','details','dfn','dir','div','dl','dt','em','figcaption',
'figure','footer','h1','h2','h3','h4','h5','h6','header','i','ins',
'kbd','label','legend','li','main','mark','marquee','nav','ol',
'output','p','pre','q','rp','rt','ruby','s','samp','section','small',
'span','strike','strong','sub','summary','sup','table','tbody','td',
'tfoot','th','thead','time','tr','tt','u','ul','var'
]);
/* Set configuration parameters */
'ALLOWED_ATTR' in cfg ? ALLOWED_ATTR = cfg.ALLOWED_ATTR : null;
'ALLOWED_TAGS' in cfg ? ALLOWED_TAGS = cfg.ALLOWED_TAGS : null;
'FORBID_ATTR' in cfg ? FORBID_ATTR = cfg.FORBID_ATTR : null;
'FORBID_TAGS' in cfg ? FORBID_TAGS = cfg.FORBID_TAGS : null;
'ALLOW_DATA_ATTR' in cfg ? ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR : null;
'SAFE_FOR_JQUERY' in cfg ? SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY : null;
'WHOLE_DOCUMENT' in cfg ? WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT : null;
'RETURN_DOM' in cfg ? RETURN_DOM = cfg.RETURN_DOM : null;
'SANITIZE_DOM' in cfg ? SANITIZE_DOM = cfg.SANITIZE_DOM : null;
'KEEP_CONTENT' in cfg ? KEEP_CONTENT = cfg.KEEP_CONTENT : null;
'DEBUG_OUTPUT' in cfg ? DEBUG_OUTPUT = cfg.DEBUG_OUTPUT : null;
/* Keep a reference to config to pass to hooks */
var CONFIG = null;
/* Merge configuration parameters */
cfg.ADD_ATTR ? ALLOWED_ATTR = ALLOWED_ATTR.concat(cfg.ADD_ATTR) : null;
cfg.ADD_TAGS ? ALLOWED_TAGS = ALLOWED_TAGS.concat(cfg.ADD_TAGS) : null;
/* Add #text in case KEEP_CONTENT is set to true */
KEEP_CONTENT ? ALLOWED_TAGS.push('#text') : null;
/* Ideally, do not touch anything below this line */
/* ______________________________________________ */
// Prevent further manipulation of configuration.
// Not available in IE8, Safari 5, etc.
if (Object && 'freeze' in Object) { Object.freeze(cfg); }
};
/**
* _parseConfig
*
* @param optional config literal
*/
var _parseConfig = function(cfg) {
/* Shield configuration object from tampering */
if (typeof cfg !== 'object') {
cfg = {};
}
/**
* _initDocument
*
* @param a string of dirty markup
* @return a DOM, filled with the dirty markup
*/
var _initDocument = function(dirty) {
/* Set configuration parameters */
ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ?
_addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ?
_addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
FORBID_TAGS = 'FORBID_TAGS' in cfg ?
_addToSet({}, cfg.FORBID_TAGS) : {};
FORBID_ATTR = 'FORBID_ATTR' in cfg ?
_addToSet({}, cfg.FORBID_ATTR) : {};
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY || false; // Default false
WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
RETURN_DOM = cfg.RETURN_DOM || false; // Default false
SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
/* Exit directly if we have nothing to do */
if (typeof dirty === 'string' && dirty.indexOf('<') === -1) {
return dirty;
/* Merge configuration parameters */
if (cfg.ADD_TAGS) {
if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
ALLOWED_TAGS = _cloneObj(ALLOWED_TAGS);
}
/* Create documents to map markup to */
var dom = document.implementation.createHTMLDocument('');
dom.body.parentNode.removeChild(dom.body.parentNode.firstElementChild);
dom.body.outerHTML = dirty;
/* Work on whole document or just its body */
var body = WHOLE_DOCUMENT ? dom.body.parentNode : dom.body;
if (
!(dom.body instanceof HTMLBodyElement) ||
!(dom.body instanceof HTMLHtmlElement)
) {
var doc = (typeof HTMLTemplateElement === 'function') ?
document.createElement('template').content.ownerDocument :
document;
var freshdom = doc.implementation.createHTMLDocument('');
body = WHOLE_DOCUMENT
? freshdom.getElementsByTagName.call(dom,'html')[0]
: freshdom.getElementsByTagName.call(dom,'body')[0];
_addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
}
if (cfg.ADD_ATTR) {
if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
ALLOWED_ATTR = _cloneObj(ALLOWED_ATTR);
}
return body;
};
_addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
}
/**
* _createIterator
*
* @param document/fragment to create iterator for
* @return iterator instance
*/
var _createIterator = function(doc) {
return document.createNodeIterator(
doc,
NodeFilter.SHOW_ELEMENT
| NodeFilter.SHOW_COMMENT
| NodeFilter.SHOW_TEXT,
function() { return NodeFilter.FILTER_ACCEPT; },
false
);
};
/* Add #text in case KEEP_CONTENT is set to true */
if (KEEP_CONTENT) { ALLOWED_TAGS['#text'] = true; }
/**
* _isClobbered
*
* @param element to check for clobbering attacks
* @return true if clobbered, false if safe
*/
var _isClobbered = function(elm) {
if (elm instanceof Text) {
return false;
}
if (
(elm.children && !(elm.children instanceof HTMLCollection))
|| (elm.attributes instanceof HTMLCollection)
|| (elm.attributes instanceof NodeList)
|| (elm.insertAdjacentHTML && typeof elm.insertAdjacentHTML !== 'function')
|| (elm.outerHTML && typeof elm.outerHTML !== 'string')
|| typeof elm.nodeName !== 'string'
|| typeof elm.textContent !== 'string'
|| typeof elm.nodeType !== 'number'
|| typeof elm.COMMENT_NODE !== 'number'
|| typeof elm.setAttribute !== 'function'
|| typeof elm.hasAttribute !== 'function'
|| typeof elm.cloneNode !== 'function'
|| typeof elm.removeAttributeNode !== 'function'
|| typeof elm.removeChild !== 'function'
|| typeof elm.attributes.item !== 'function'
|| (elm.id === 'createElement' || elm.name === 'createElement')
|| (elm.id === 'implementation' || elm.name === 'implementation')
|| (elm.id === 'createNodeIterator' || elm.name === 'createNodeIterator')
) {
return true;
}
return false;
};
// Prevent further manipulation of configuration.
// Not available in IE8, Safari 5, etc.
if (Object && 'freeze' in Object) { Object.freeze(cfg); }
/**
* _sanitizeElements
*
* @protect removeChild
* @protect nodeType
* @protect nodeName
* @protect textContent
* @protect outerHTML
* @protect currentNode
* @protect insertAdjacentHTML
*
* @param node to check for permission to exist
* @return true if node was killed, false if left alive
*/
var _sanitizeElements = function(currentNode) {
CONFIG = cfg;
};
/* Execute a hook if present */
_executeHook('beforeSanitizeElements', currentNode, null);
/**
* _initDocument
*
* @param a string of dirty markup
* @return a DOM, filled with the dirty markup
*/
var _initDocument = function(dirty) {
/* Create documents to map markup to */
var dom = document.implementation.createHTMLDocument('');
var freshdom, doc;
/* Check if element is clobbered or can clobber */
if (_isClobbered(currentNode)) {
/* Set content */
var body = dom.body;
body.parentNode.removeChild(body.parentNode.firstElementChild);
body.outerHTML = dirty;
/* Be harsh with clobbered content, element has to go! */
try{
currentNode.parentNode.removeChild(currentNode);
} catch(e){
currentNode.outerHTML='';
}
return true;
}
/* Work on whole document or just its body */
body = WHOLE_DOCUMENT ? dom.body.parentNode : dom.body;
if (!(body instanceof (WHOLE_DOCUMENT ? HTMLHtmlElement : HTMLBodyElement))) {
doc = (typeof HTMLTemplateElement === 'function') ?
document.createElement('template').content.ownerDocument :
document;
freshdom = doc.implementation.createHTMLDocument('');
body = WHOLE_DOCUMENT ?
freshdom.getElementsByTagName.call(dom,'html')[0] :
freshdom.getElementsByTagName.call(dom,'body')[0];
}
return body;
};
/* Now let's check the element's type and name */
var tagName = currentNode.nodeName.toLowerCase();
/**
* _createIterator
*
* @param document/fragment to create iterator for
* @return iterator instance
*/
var _createIterator = function(doc) {
return document.createNodeIterator(
doc,
NodeFilter.SHOW_ELEMENT
| NodeFilter.SHOW_COMMENT
| NodeFilter.SHOW_TEXT,
function() { return NodeFilter.FILTER_ACCEPT; },
false
);
};
/* Execute a hook if present */
_executeHook('uponSanitizeElement', currentNode, {
tagName: tagName
});
/**
* _isClobbered
*
* @param element to check for clobbering attacks
* @return true if clobbered, false if safe
*/
var _isClobbered = function(elm) {
if (elm instanceof Text) {
return false;
}
if (
(elm.children && !(elm.children instanceof HTMLCollection))
|| (elm.attributes instanceof HTMLCollection)
|| (elm.attributes instanceof NodeList)
|| (elm.insertAdjacentHTML && typeof elm.insertAdjacentHTML !== 'function')
|| (elm.outerHTML && typeof elm.outerHTML !== 'string')
|| typeof elm.nodeName !== 'string'
|| typeof elm.textContent !== 'string'
|| typeof elm.nodeType !== 'number'
|| typeof elm.COMMENT_NODE !== 'number'
|| typeof elm.setAttribute !== 'function'
|| typeof elm.hasAttribute !== 'function'
|| typeof elm.cloneNode !== 'function'
|| typeof elm.removeAttributeNode !== 'function'
|| typeof elm.removeChild !== 'function'
|| typeof elm.attributes.item !== 'function'
|| (elm.id === 'createElement' || elm.name === 'createElement')
|| (elm.id === 'implementation' || elm.name === 'implementation')
|| (elm.id === 'createNodeIterator' || elm.name === 'createNodeIterator')
) {
return true;
}
return false;
};
if (currentNode.nodeType === currentNode.COMMENT_NODE
|| ALLOWED_TAGS.indexOf(tagName) === -1
|| FORBID_TAGS.indexOf(tagName) > -1
) {
/* Keep content for white-listed elements */
if (KEEP_CONTENT && currentNode.insertAdjacentHTML
&& currentNode.nodeName.toLowerCase
&& CONTENT_TAGS.indexOf(tagName) !== -1){
try {
currentNode.insertAdjacentHTML('AfterEnd', currentNode.innerHTML);
} catch(e) {}
}
/**
* _sanitizeElements
*
* @protect removeChild
* @protect nodeType
* @protect nodeName
* @protect textContent
* @protect outerHTML
* @protect currentNode
* @protect insertAdjacentHTML
*
* @param node to check for permission to exist
* @return true if node was killed, false if left alive
*/
var _sanitizeElements = function(currentNode) {
/* Execute a hook if present */
_executeHook('beforeSanitizeElements', currentNode, null);
/* Remove element if anything permits its presence */
/* Check if element is clobbered or can clobber */
if (_isClobbered(currentNode)) {
/* Be harsh with clobbered content, element has to go! */
try {
currentNode.parentNode.removeChild(currentNode);
return true;
} catch (e) {
currentNode.outerHTML = '';
}
return true;
}
/* Finally, convert markup to cover jQuery behavior */
if (SAFE_FOR_JQUERY && !currentNode.firstElementChild) {
currentNode.innerHTML = currentNode.textContent.replace(/</g, '&lt;');
/* Now let's check the element's type and name */
var tagName = currentNode.nodeName.toLowerCase();
/* Execute a hook if present */
_executeHook('uponSanitizeElement', currentNode, {
tagName: tagName
});
/* Remove element if anything forbids its presence */
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
/* Keep content for white-listed elements */
if (KEEP_CONTENT && currentNode.insertAdjacentHTML
&& CONTENT_TAGS[tagName]){
try {
currentNode.insertAdjacentHTML('AfterEnd', currentNode.innerHTML);
} catch (e) {}
}
currentNode.parentNode.removeChild(currentNode);
return true;
}
/* Execute a hook if present */
_executeHook('afterSanitizeElements', currentNode, null);
/* Finally, convert markup to cover jQuery behavior */
if (SAFE_FOR_JQUERY && !currentNode.firstElementChild) {
currentNode.innerHTML = currentNode.textContent.replace(/</g, '&lt;');
}
return false;
};
/* Execute a hook if present */
_executeHook('afterSanitizeElements', currentNode, null);
/**
* _sanitizeAttributes
*
* @protect attributes
* @protect removeAttribiuteNode
* @protect setAttribute
* @protect cloneNode
*
* @param node to sanitize
* @return void
*/
var _sanitizeAttributes = function(currentNode) {
/* Execute a hook if present */
_executeHook('beforeSanitizeAttributes', currentNode, null);
var regex = /^(\w+script|data):/gi,
clonedNode = currentNode.cloneNode(true),
tmp, clobbering;
return false;
};
/* This needs to be extensive thanks to Webkit/Blink's behavior */
var whitespace = /[\x00-\x20\xA0\u1680\u180E\u2000-\u2029\u205f\u3000]/g;
/**
* _sanitizeAttributes
*
* @protect attributes
* @protect removeAttributeNode
* @protect setAttribute
* @protect cloneNode
*
* @param node to sanitize
* @return void
*/
var _sanitizeAttributes = function(currentNode) {
/* Execute a hook if present */
_executeHook('beforeSanitizeAttributes', currentNode, null);
/* Check if we have attributes; if not we might have a text node */
if (!currentNode.attributes) { return; }
var isScriptOrData = /^(?:\w+script|data):/gi,
clonedNode = currentNode.cloneNode(false),
tmp, clobbering;
/* Go backwards over all attributes; safely remove bad ones */
for (var attr = currentNode.attributes.length-1; attr >= 0; attr--) {
/* This needs to be extensive thanks to Webkit/Blink's behavior */
var whitespace = /[\x00-\x20\xA0\u1680\u180E\u2000-\u2029\u205f\u3000]/g;
tmp = clonedNode.attributes[attr];
clobbering = false;
currentNode.removeAttribute(currentNode.attributes[attr].name);
/* Check if we have attributes; if not we might have a text node */
if (!currentNode.attributes) { return; }
if (!tmp instanceof Attr) { continue; }
/* Go backwards over all attributes; safely remove bad ones */
for (var attr = currentNode.attributes.length-1; attr >= 0; attr--) {
tmp = clonedNode.attributes[attr];
clobbering = false;
currentNode.removeAttribute(currentNode.attributes[attr].name);
if(SANITIZE_DOM) {
if((tmp.name === 'id' || tmp.name === 'name')
&& (tmp.value in window || tmp.value in document)) {
clobbering = true;
}
if (!tmp instanceof Attr) { continue; }
if (SANITIZE_DOM) {
if ((tmp.name === 'id' || tmp.name === 'name')
&& (tmp.value in window || tmp.value in document)) {
clobbering = true;
}
/* Safely handle attributes */
var attrName = tmp.name.toLowerCase();
}
/* Safely handle attributes */
var attrName = tmp.name.toLowerCase();
/* Execute a hook if present */
_executeHook('uponSanitizeAttribute', currentNode, {
attrName: attrName, attrValue: tmp.value, attr: tmp
});
/* Execute a hook if present */
_executeHook('uponSanitizeAttribute', currentNode, {
attrName: attrName, attrValue: tmp.value, attr: tmp
});
if (
((ALLOWED_ATTR.indexOf(attrName) > -1 &&
FORBID_ATTR.indexOf(attrName) === -1) ||
(ALLOW_DATA_ATTR && tmp.name.match(/^data-[\w-]+/i)))
if (
((ALLOWED_ATTR[attrName] && !FORBID_ATTR[attrName]) ||
(ALLOW_DATA_ATTR && /^data-[\w-]+/i.test(tmp.name)))
/* Get rid of script and data URIs */
&& (!tmp.value.replace(whitespace,'').match(regex)
/* Get rid of script and data URIs */
&& (!isScriptOrData.test(tmp.value.replace(whitespace,''))
/* Keep image data URIs alive if src is allowed */
|| (tmp.name === 'src'
&& tmp.value.indexOf('data:') === 0
&& currentNode.nodeName === 'IMG'))
/* Keep image data URIs alive if src is allowed */
|| (tmp.name === 'src'
&& tmp.value.indexOf('data:') === 0
&& currentNode.nodeName === 'IMG'))
/* Make sure attribute cannot clobber */
&& !clobbering
) {
/* Handle invalid data attribute set by try-catching it */
try {
currentNode.setAttribute(tmp.name, tmp.value);
} catch (e) {}
}
/* Make sure attribute cannot clobber */
&& !clobbering
) {
/* Handle invalid data attribute set by try-catching it */
try {
currentNode.setAttribute(tmp.name, tmp.value);
} catch (e) {}
}
}
/* Execute a hook if present */
_executeHook('afterSanitizeAttributes', currentNode, null);
};
/* Execute a hook if present */
_executeHook('afterSanitizeAttributes', currentNode, null);
};
/**
* _sanitizeShadowDOM
*
* @param fragment to iterate over recursively
* @return void
*/
var _sanitizeShadowDOM = function(fragment) {
var shadowNode;
var shadowIterator = _createIterator(fragment);
/* Execute a hook if present */
_executeHook('beforeSanitizeShadowDOM', fragment, null);
/**
* _sanitizeShadowDOM
*
* @param fragment to iterate over recursively
* @return void
*/
var _sanitizeShadowDOM = function(fragment) {
var shadowNode;
var shadowIterator = _createIterator(fragment);
while (shadowNode = shadowIterator.nextNode()) {
/* Execute a hook if present */
_executeHook('uponSanitizeShadowNode', shadowNode, null);
/* Execute a hook if present */
_executeHook('beforeSanitizeShadowDOM', fragment, null);
/* Sanitize tags and elements */
if (_sanitizeElements(shadowNode)) {
continue;
}
while (shadowNode = shadowIterator.nextNode()) {
/* Execute a hook if present */
_executeHook('uponSanitizeShadowNode', shadowNode, null);
/* Deep shadow DOM detected */
if (shadowNode.content instanceof DocumentFragment) {
_sanitizeShadowDOM(shadowNode.content);
}
/* Sanitize tags and elements */
if (_sanitizeElements(shadowNode)) {
continue;
}
/* Check attributes, sanitize if necessary */
_sanitizeAttributes(shadowNode);
/* Deep shadow DOM detected */
if (shadowNode.content instanceof DocumentFragment) {
_sanitizeShadowDOM(shadowNode.content);
}
/* Execute a hook if present */
_executeHook('afterSanitizeShadowDOM', fragment, null);
};
/**
* _executeHook
* Execute user configurable hooks
*
* @param {String} entryPoint Name of the hook's entry point
* @param {Node} currentNode
*/
var _executeHook = function(entryPoint, currentNode, data) {
if (!hooks[entryPoint]) { return; }
/* Check attributes, sanitize if necessary */
_sanitizeAttributes(shadowNode);
}
hooks[entryPoint].forEach(function(hook) {
hook.call(DOMPurify, currentNode, data, cfg);
});
};
/* Execute a hook if present */
_executeHook('afterSanitizeShadowDOM', fragment, null);
};
/* Feature check and untouched opt-out return */
if (typeof document.implementation.createHTMLDocument === 'undefined'
|| (typeof document.documentMode === 'number' && document.documentMode === 9)) {
/**
* _executeHook
* Execute user configurable hooks
*
* @param {String} entryPoint Name of the hook's entry point
* @param {Node} currentNode
*/
var _executeHook = function(entryPoint, currentNode, data) {
if (!hooks[entryPoint]) { return; }
hooks[entryPoint].forEach(function(hook) {
hook.call(DOMPurify, currentNode, data, CONFIG);
});
};
/**
* sanitize
* Public method providing core sanitation functionality
*
* @param {String} dirty string
* @param {Object} configuration object
*/
DOMPurify.sanitize = function(dirty, cfg) {
/* Check we can run. Otherwise fall back or ignore */
if (!DOMPurify.isSupported) {
if (typeof window.toStaticHTML === 'function' && typeof dirty === 'string') {

@@ -495,10 +521,15 @@ return window.toStaticHTML(dirty);

/* Assign config vars */
cfg ? _parseConfig(cfg) : null;
_parseConfig(cfg);
/* Exit directly if we have nothing to do */
if (!RETURN_DOM && !WHOLE_DOCUMENT && dirty.indexOf('<') === -1) {
return dirty;
}
/* Initialize the document to work on */
var body = _initDocument(dirty);
/* Early exit in case document is empty */
if (typeof body !== 'object') {
return body ? body : '';
/* Check we have a DOM node from the data */
if (!body) {
return RETURN_DOM ? null : '';
}

@@ -541,3 +572,2 @@

DOMPurify.addHook = function(entryPoint, hookFunction) {
if (typeof hookFunction !== 'function') { return; }

@@ -544,0 +574,0 @@ hooks[entryPoint] = hooks[entryPoint] || [];

@@ -162,2 +162,2 @@ # DOMPurify [![NPM version](http://img.shields.io/npm/v/dompurify.svg)](https://www.npmjs.org/package/dompurify)

Big thanks also go to [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro) and [@fhemberger](https://twitter.com/fhemberger)!
Big thanks also go to [@asutherland](https://twitter.com/asutherland), [@mathias](https://twitter.com/mathias), [@cgvwzq](https://twitter.com/cgvwzq), [@robbertatwork](https://twitter.com/robbertatwork), [@giutro](https://twitter.com/giutro) and [@fhemberger](https://twitter.com/fhemberger)! Further, thanks [@neilj](https://twitter.com/neilj) for his code review and countless small optimizations, fixes and beautifications.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc