Skip to content

Commit

Permalink
XSS: escape < and > characters in written out XHR response
Browse files Browse the repository at this point in the history
This escapes < and > to prevent XHR responses from containing script
injections.
  • Loading branch information
matthewp committed Dec 21, 2016
1 parent 0ac4d50 commit a7f82c5
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
node_modules/
dist/
doc/
test/xss/out.html
6 changes: 5 additions & 1 deletion lib/zones/xhr.js
Expand Up @@ -136,5 +136,9 @@ function XHR(){
}

XHR.prototype.toString = function(){
return "XHR_CACHE = " + JSON.stringify(this.data) + ";";
var json = JSON.stringify(this.data);
json = json
.replace(/</g, "\\u003c")
.replace(/>/g, "\\u003e");
return "XHR_CACHE = " + json + ";";
};
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -9,7 +9,8 @@
"document": "bit-docs",
"test:node": "mocha test/test.js && mocha test/test_register_node.js",
"test:browser": "testee test/test.html test/register.html --browsers firefox --reporter Spec",
"test": "npm run test:node && npm run test:browser",
"test:xss": "mocha test/xss/test.js",
"test": "npm run test:node && npm run test:browser && npm run test:xss",
"version": "git commit -am \"Update dist for release\" && git checkout -b release && git add -f dist/",
"postversion": "git push --tags && git checkout master && git branch -D release && git push",
"release:pre": "npm version prerelease && npm publish",
Expand Down
14 changes: 14 additions & 0 deletions test/xss/browser-test.js
@@ -0,0 +1,14 @@
var assert = require("assert");
require("steal-mocha");

describe("An injected script", function(){
it("doesn't run", function(){
assert.equal(window.FOO, undefined, "script not injected");
});

it("Has the correct value", function(){
var resp = XHR_CACHE[0].response.responseText;
var expected = '</script><script>window.FOO=\'bar\';</script>';
assert.equal(resp, expected);
});
});
2 changes: 2 additions & 0 deletions test/xss/inj.html
@@ -0,0 +1,2 @@
<script src="../../node_modules/steal/steal.js" main="MAIN"
data-mocha="bdd"></script>
38 changes: 38 additions & 0 deletions test/xss/test.js
@@ -0,0 +1,38 @@
var Zone = require("../../lib/zone");
var xhrZone = require("../../xhr");
var testee = require("testee");

var utils = require("./utils");
var mockXHR = utils.mockXHR;
var injectTest = utils.injectTest;

describe("Script injection", function(){
this.timeout(50000);

beforeEach(function(){
var resp = '</script><script>window.FOO=\'bar\';</script>';
this.resetXHR = mockXHR(resp);
});

afterEach(function(){
this.resetXHR();
});

it("Correctly escapes scripts", function(done){
var zone = new Zone({
plugins: [xhrZone]
});

zone.run(function(){
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com");
xhr.send();
}).then(function(data){
injectTest(data.xhr, "test/xss/browser-test");

return testee.test(["test/xss/out.html"], 'firefox', {
reporter: 'Spec'
});
}).then(function() { done(); }, done);
});
});
44 changes: 44 additions & 0 deletions test/xss/utils.js
@@ -0,0 +1,44 @@
var env = require("../../lib/env");
var g = env.global;
var fs = require("fs");

exports.mockXHR = mockXHR;

function mockXHR(mockedResponse) {
var globalSetTimeout = g.setTimeout;
var OldXHR = g.XMLHttpRequest;
var XHR = g.XMLHttpRequest = function(){
this.onload = null;
};

XHR.prototype.getAllResponseHeaders = function(){
return "Content-Type: application/json";
};

XHR.prototype.open = function(){};

XHR.prototype.send = function(){
var onload = this.onload;
var xhr = this;

globalSetTimeout(function(){
xhr.responseText = mockedResponse;
onload && onload({ target: xhr });
}, 10);
};

return function(){
g.XMLHttpRequest = OldXHR;
};
}

exports.injectTest = injectTest;

function injectTest(cache, testJs) {
var testPth = __dirname + "/inj.html"
var testPage = fs.readFileSync(testPth, "utf8");
var xhrScript = "<script>" + cache + "</script>";
testPage = testPage.replace("MAIN", testJs);
testPage = testPage + xhrScript;
fs.writeFileSync(__dirname + "/out.html", testPage, "utf8");
}

0 comments on commit a7f82c5

Please sign in to comment.