Thursday, April 26, 2012

Unit Testing JavaScript Using Js-Test-Driver

Javascript can be easy to unit test and there are several unit test frameworks that allow this to happen.  Js-test-driver is an easy to use and actively developed framework that is fairly easy to learn and integrates well with Eclipse.

During a recent BSides conference the topic of cross server scripting (XSS) with JQuery was discussed.  This topic piqued some interest because I was interested in automating unit testing scenarios in JavaScript.  What I found was that it was easy to build JS unit tests using JS-Test-Driver test cases around JQuery to test XSS. Here's an example of a JS-test-driver test case:

/**
 * appendTest - Unit test for XSS vulnerabilities
 *
 * Copyright (C) 2012 @rcastellow
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see  *
 */

appendTest = TestCase("appendTest");

appendTest.prototype.name = "append()";

appendTest.prototype.setUp = function() {
    this.testUtils = new TestUtils();
};

appendTest.prototype.testXSS = function() {
    /*:DOC htmlTest = <div class="page"><div id="testDiv">abc</div> </div>"
     */

    var child = this.testUtils.findFirstChildByTagName(this.htmlTest, 'DIV');
    assertNotNull(child);
    assertEquals("DIV", child.tagName);

    // Add Sample XSS
    var xssElement = $(child).append("<script id='bad' src='http://badStuff.js' />");
    assertEquals('
<div id="testDiv">abc</div>', xssElement.html().trim());
  
    xssElement = $(child).append("
<h1 id='bad' >abc</h1>");
    assertEquals('
<div id="testDiv">abc</div> <h1 id="bad">abc</h1>', xssElement.html().trim());
  
  
    // 3. Test if XSS modified and made the script part of the DOM
    if (xssElement.html().trim() != '
<div id="testDiv">abc</div>') {
        jstestdriver.console.log("jQueryXSS", this.name + " has successfully modified the DOM.");
    }
};





The results of my unit tests showed me that JQuery is indeed handling some of the more common forms of XSS in more recent versions of JQuery (1.6.2+).  jQuery looks for a '<' in its parameters because it is not very common start of a parameter in Javascript, but is very common in html.  


The code that handles XSS can be found in the jQuery library in the following lines of code:


	// A simple way to check for HTML strings or ID strings
	// Prioritize #id over  to avoid XSS via location.hash (#9521)
	quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
  

Will it stop all forms of XSS? Probably not, since there are so many ways besides adding a straight html script entry into a jQuery parameter. A future exercise might be to extend my tests to include a wider variety of XSS vectors, many of which can be found here.

No comments: