Skip to content

Commit

Permalink
* Fixed a performance issue on IE
Browse files Browse the repository at this point in the history
 * Fixed a bug in the config var assignemnt
 * Added config flag tests
 * Added additional test cases
  • Loading branch information
mario committed May 9, 2014
1 parent 839915a commit e3af0f9
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 11 deletions.
18 changes: 9 additions & 9 deletions purify.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,14 @@
var _parseConfig = function(cfg) {

/* Set configuration parameters */
cfg.ALLOWED_ATTR ? ALLOWED_ATTR = cfg.ALLOWED_ATTR : null;
cfg.ALLOWED_TAGS ? ALLOWED_TAGS = cfg.ALLOWED_TAGS : null;
cfg.ALLOW_DATA_ATTR ? ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR : null;
cfg.SAFE_FOR_JQUERY ? SAFE_FOR_JQUERY = cfg.SAFE_FOR_JQUERY : null;
cfg.WHOLE_DOCUMENT ? WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT : null;
cfg.RETURN_DOM ? RETURN_DOM = cfg.RETURN_DOM : null;
cfg.SANITIZE_DOM ? SANITIZE_DOM = cfg.SANITIZE_DOM : null;
cfg.KEEP_CONTENT ? KEEP_CONTENT = cfg.KEEP_CONTENT : null;
'ALLOWED_ATTR' in cfg ? ALLOWED_ATTR = cfg.ALLOWED_ATTR : null;
'ALLOWED_TAGS' in cfg ? ALLOWED_TAGS = cfg.ALLOWED_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;

/* Merge configuration parameters */
cfg.ADD_ATTR ? ALLOWED_ATTR = ALLOWED_ATTR.concat(cfg.ADD_ATTR) : null;
Expand Down Expand Up @@ -294,7 +294,7 @@

/* Finally, convert markup to cover jQuery behavior */
if (SAFE_FOR_JQUERY && !currentNode.firstElementChild) {
currentNode.textContent = currentNode.textContent.replace(/</g, '&lt;');
currentNode.innerHTML = currentNode.textContent.replace(/</g, '&lt;');
}
return false;
};
Expand Down
10 changes: 10 additions & 0 deletions test/expect.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@
"payload": "<p>hello</p>",
"expected": "<p>hello</p>"
},
{
"title": "mXSS Variation I",
"payload": "<listing>&lt;img onerror=\"alert(1);//\" src=1&gt;<t t></listing>",
"expected": ""
},
{
"title": "mXSS Variation II",
"payload": "<img src=x id/=' onerror=alert(1)//'>",
"expected": ["<img src=\"x\">", "<img id=\"\" src=\"x\">"]
},
{
"title": "Textarea and comments enabling img element",
"payload": "<textarea>@shafigullin</textarea><!--</textarea><img src=x onerror=alert(1)>-->",
Expand Down
85 changes: 83 additions & 2 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,94 @@
var xssTests = tests.filter( function( element ) {
if ( /alert/.test( element.payload ) ) { return element; }
});

// Sanitization tests
QUnit
.cases(tests)
.test( 'Sanitization test', function(params, assert) {
assert.contains( DOMPurify.sanitize( params.payload ), params.expected );
});


// Config-Flag Tests
QUnit.test( 'Config-Flag tests: KEEP_CONTENT + ALLOWED_TAGS / ALLOWED_ATTR', function(assert) {
// KEEP_CONTENT + ALLOWED_TAGS / ALLOWED_ATTR
assert.equal( DOMPurify.sanitize( '<iframe>Hello</iframe>', {KEEP_CONTENT: false} ), "");
assert.contains( DOMPurify.sanitize( '<a href="#">abc<b style="color:red">123</b><q class="cite">123</b></a>', {ALLOWED_TAGS: ['b', 'q'], ALLOWED_ATTR: ['style'], KEEP_CONTENT: true}),
["abc<b style=\"color:red\">123</b><q>123</q>", "abc<b style=\"color: red;\">123</b><q>123</q>"]
);
assert.equal( DOMPurify.sanitize( '<a href="#">abc<b style="color:red">123</b><q class="cite">123</b></a>', {ALLOWED_TAGS: ['b', 'q'], ALLOWED_ATTR: ['style'], KEEP_CONTENT: false}), "");
assert.equal( DOMPurify.sanitize( '<a href="#">abc</a>', {ALLOWED_TAGS: ['b', 'q'], KEEP_CONTENT: false}), "");
});

QUnit.test( 'Config-Flag tests: ALLOW_DATA_ATTR', function(assert) {
// ALLOW_DATA_ATTR
assert.equal( DOMPurify.sanitize( '<a href="#" data-abc="foo">abc</a>', {ALLOW_DATA_ATTR: false}), "<a href=\"#\">abc</a>" );
assert.contains( DOMPurify.sanitize( '<a href="#" data-abc="foo">abc</a>', {ALLOW_DATA_ATTR: true}),
["<a data-abc=\"foo\" href=\"#\">abc</a>", "<a href=\"#\" data-abc=\"foo\">abc</a>"]
);
assert.contains( DOMPurify.sanitize( '<a href="#" data-abc-1-2-3="foo">abc</a>', {ALLOW_DATA_ATTR: true}),
["<a data-abc-1-2-3=\"foo\" href=\"#\">abc</a>", "<a href=\"#\" data-abc-1-2-3=\"foo\">abc</a>"]
);
assert.equal( DOMPurify.sanitize( '<a href="#" data-""="foo">abc</a>', {ALLOW_DATA_ATTR: true}), "<a href=\"#\">abc</a>" );
});

QUnit.test( 'Config-Flag tests: ADD_TAGS', function(assert) {
// ADD_TAGS
assert.equal( DOMPurify.sanitize( '<my-component>abc</my-component>', {ADD_TAGS: ['my-component']}), "<my-component>abc</my-component>" );
assert.equal( DOMPurify.sanitize( '<my-ĸompønent>abc</my-ĸompønent>', {ADD_TAGS: ['my-ĸompønent']}), "<my-ĸompønent>abc</my-ĸompønent>" );
});

QUnit.test( 'Config-Flag tests: ADD_TAGS + ADD_ATTR', function(assert) {
// ADD_TAGS + ADD_ATTR
assert.equal( DOMPurify.sanitize( '<my-component my-attr="foo">abc</my-component>', {ADD_TAGS: ['my-component']}), "<my-component>abc</my-component>" );
assert.equal( DOMPurify.sanitize( '<my-component my-attr="foo">abc</my-component>', {ADD_TAGS: ['my-component'], ADD_ATTR: ['my-attr']}), "<my-component my-attr=\"foo\">abc</my-component>" );
assert.equal( DOMPurify.sanitize( '<my-ĸompønent my-æŧŧr="foo">abc</my-ĸompønent>', {ADD_TAGS: ['my-ĸompønent']}), "<my-ĸompønent>abc</my-ĸompønent>" );
assert.equal( DOMPurify.sanitize( '<my-ĸompønent my-æŧŧr="foo">abc</my-ĸompønent>', {ADD_TAGS: ['my-ĸompønent'], ADD_ATTR: ['my-æŧŧr']}), "<my-ĸompønent my-æŧŧr=\"foo\">abc</my-ĸompønent>" );
});

QUnit.test( 'Config-Flag tests: SAFE_FOR_JQUERY', function(assert) {
//SAFE_FOR_JQUERY
assert.equal( DOMPurify.sanitize( '<a>123</a><option><style><img src=x onerror=alert(1)>', {SAFE_FOR_JQUERY: false}), "<a>123</a><option><style><img src=x onerror=alert(1)></style></option>" );
assert.equal( DOMPurify.sanitize( '<a>123</a><option><style><img src=x onerror=alert(1)>', {SAFE_FOR_JQUERY: true}), "<a>123</a><option><style>&lt;img src=x onerror=alert(1)></style></option>" );
assert.equal( DOMPurify.sanitize( '<option><style></option></select><b><img src=xx: onerror=alert(1)></style></option>', {SAFE_FOR_JQUERY: false}), "<option><style></option></select><b><img src=xx: onerror=alert(1)></style></option>" );
assert.equal( DOMPurify.sanitize( '<option><style></option></select><b><img src=xx: onerror=alert(1)></style></option>', {SAFE_FOR_JQUERY: true}), "<option><style>&lt;/option>&lt;/select>&lt;b>&lt;img src=xx: onerror=alert(1)></style></option>" );
assert.equal( DOMPurify.sanitize( '<option><iframe></select><b><script>alert(1)<\/script>', {SAFE_FOR_JQUERY: false}), "<option></option>" );
assert.equal( DOMPurify.sanitize( '<option><iframe></select><b><script>alert(1)<\/script>', {SAFE_FOR_JQUERY: true}), "<option></option>" );
assert.equal( DOMPurify.sanitize( '<b><style><style/><img src=xx: onerror=alert(1)>', {SAFE_FOR_JQUERY: false}), "<b><style><style/><img src=xx: onerror=alert(1)></style></b>" );
assert.equal( DOMPurify.sanitize( '<b><style><style/><img src=xx: onerror=alert(1)>', {SAFE_FOR_JQUERY: true}), "<b><style>&lt;style/>&lt;img src=xx: onerror=alert(1)></style></b>" );
});

QUnit.test( 'Config-Flag tests: SANITIZE_DOM', function(assert) {
// SANITIZE_DOM
assert.equal( DOMPurify.sanitize( '<img src="x" name="getElementById">', {SANITIZE_DOM: false}), "<img name=\"getElementById\" src=\"x\">" );
assert.equal( DOMPurify.sanitize( '<img src="x" name="getElementById">', {SANITIZE_DOM: true}), "<img src=\"x\">" );
assert.equal( DOMPurify.sanitize( '<a href="x" id="location">click</a>', {SANITIZE_DOM: true}), "<a href=\"x\">click</a>" );
assert.contains( DOMPurify.sanitize( '<form><input name="attributes"></form>', {ADD_TAGS: ['form'], SANITIZE_DOM: false}),
["", "<form><input name=\"attributes\"></form>"]
);
assert.contains( DOMPurify.sanitize( '<form><input name="attributes"></form>', {ADD_TAGS: ['form'], SANITIZE_DOM: true}),
["", "<form><input name=\"attributes\"></form>"]
);
});

QUnit.test( 'Config-Flag tests: WHOLE_DOCUMENT', function(assert) {
//WHOLE_DOCUMENT
assert.equal( DOMPurify.sanitize( '123', {WHOLE_DOCUMENT: false}), "123" );
assert.equal( DOMPurify.sanitize( '123', {WHOLE_DOCUMENT: true}), "<html><head></head><body>123</body></html>" );
assert.equal( DOMPurify.sanitize( '<style>*{color:red}</style>', {WHOLE_DOCUMENT: false}), "" );
assert.equal( DOMPurify.sanitize( '<style>*{color:red}</style>', {WHOLE_DOCUMENT: true}), "<html><head><style>*{color:red}</style></head><body></body></html>" );
assert.equal( DOMPurify.sanitize( '123<style>*{color:red}</style>', {WHOLE_DOCUMENT: false}), "123<style>*{color:red}</style>" );
assert.equal( DOMPurify.sanitize( '123<style>*{color:red}</style>', {WHOLE_DOCUMENT: true}), "<html><head></head><body>123<style>*{color:red}</style></body></html>" );
});

QUnit.test( 'Config-Flag tests: RETURN_DOM', function(assert) {
//RETURN_DOM
assert.equal( DOMPurify.sanitize( '<a>123<b>456</b></a>', {RETURN_DOM: true}).outerHTML, "<body><a>123<b>456</b></a></body>" );
assert.equal( DOMPurify.sanitize( '<a>123<b>456<script>alert(1)<\/script></b></a>', {RETURN_DOM: true}).outerHTML, "<body><a>123<b>456</b></a></body>" );
assert.equal( DOMPurify.sanitize( '<a>123<b>456</b></a>', {RETURN_DOM: true, WHOLE_DOCUMENT: true}).outerHTML, "<html><head></head><body><a>123<b>456</b></a></body></html>" );
assert.equal( DOMPurify.sanitize( '<a>123<b>456<script>alert(1)<\/script></b></a>', {RETURN_DOM: true, WHOLE_DOCUMENT: true}).outerHTML, "<html><head></head><body><a>123<b>456</b></a></body></html>" );
});

// XSS tests: Native DOM methods (alert() should not be called)
QUnit
.cases(xssTests)
Expand All @@ -63,6 +143,7 @@
window.xssed = false;
}, 100);
});


// XSS tests: jQuery (alert() should not be called)
QUnit
Expand Down

0 comments on commit e3af0f9

Please sign in to comment.