michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: const Cr = Components.results; michael@0: michael@0: Cu.import('resource://gre/modules/CSPUtils.jsm'); michael@0: michael@0: var httpserv = null; michael@0: michael@0: const POLICY_FROM_URI = "allow 'self'; img-src *"; michael@0: const POLICY_PORT = 9000; michael@0: const POLICY_URI = "http://localhost:" + POLICY_PORT + "/policy"; michael@0: const POLICY_URI_RELATIVE = "/policy"; michael@0: const DOCUMENT_URI = "http://localhost:" + POLICY_PORT + "/document"; michael@0: const CSP_DOC_BODY = "CSP doc content"; michael@0: const SD = CSPRep.SRC_DIRECTIVES; michael@0: michael@0: // this will get populated by run_tests() michael@0: var TESTS = []; michael@0: michael@0: // helper to make URIs michael@0: function mkuri(foo) { michael@0: return Cc["@mozilla.org/network/io-service;1"] michael@0: .getService(Ci.nsIIOService) michael@0: .newURI(foo, null, null); michael@0: } michael@0: michael@0: // helper to use .equals on stuff michael@0: function do_check_equivalent(foo, bar, stack) { michael@0: if (!stack) michael@0: stack = Components.stack.caller; michael@0: michael@0: var text = foo + ".equals(" + bar + ")"; michael@0: michael@0: if (foo.equals && foo.equals(bar)) { michael@0: dump("TEST-PASS | " + stack.filename + " | [" + stack.name + " : " + michael@0: stack.lineNumber + "] " + text + "\n"); michael@0: return; michael@0: } michael@0: do_throw(text, stack); michael@0: } michael@0: michael@0: function listener(csp, cspr_static) { michael@0: this.buffer = ""; michael@0: this._csp = csp; michael@0: this._cspr_static = cspr_static; michael@0: } michael@0: michael@0: listener.prototype = { michael@0: onStartRequest: function (request, ctx) { michael@0: }, michael@0: michael@0: onDataAvailable: function (request, ctx, stream, offset, count) { michael@0: var sInputStream = Cc["@mozilla.org/scriptableinputstream;1"] michael@0: .createInstance(Ci.nsIScriptableInputStream); michael@0: sInputStream.init(stream); michael@0: this.buffer = this.buffer.concat(sInputStream.read(count)); michael@0: }, michael@0: michael@0: onStopRequest: function (request, ctx, status) { michael@0: // make sure that we have the full document content, guaranteeing that michael@0: // the document channel has been resumed, before we do the comparisons michael@0: if (this.buffer == CSP_DOC_BODY) { michael@0: michael@0: // need to re-grab cspr since it may have changed inside the document's michael@0: // nsIContentSecurityPolicy instance. The problem is, this cspr_str is a michael@0: // string and not a policy due to the way it's exposed from michael@0: // nsIContentSecurityPolicy, so we have to re-parse it. michael@0: let cspr_str = this._csp.getPolicy(0); michael@0: let cspr = CSPRep.fromString(cspr_str, mkuri(DOCUMENT_URI)); michael@0: michael@0: // and in reparsing it, we lose the 'self' relationships, so need to also michael@0: // reparse the static one (or find a way to resolve 'self' in the parsed michael@0: // policy when doing comparisons). michael@0: let cspr_static_str = this._cspr_static.toString(); michael@0: let cspr_static_reparse = CSPRep.fromString(cspr_static_str, mkuri(DOCUMENT_URI)); michael@0: michael@0: // not null, and one policy .equals the other one michael@0: do_check_neq(null, cspr); michael@0: do_check_true(cspr.equals(cspr_static_reparse)); michael@0: michael@0: // final teardown michael@0: if (TESTS.length == 0) { michael@0: httpserv.stop(do_test_finished); michael@0: } else { michael@0: do_test_finished(); michael@0: (TESTS.shift())(); michael@0: } michael@0: } michael@0: } michael@0: }; michael@0: michael@0: function run_test() { michael@0: httpserv = new HttpServer(); michael@0: httpserv.registerPathHandler("/document", csp_doc_response); michael@0: httpserv.registerPathHandler("/policy", csp_policy_response); michael@0: httpserv.start(POLICY_PORT); michael@0: TESTS = [ test_CSPRep_fromPolicyURI, test_CSPRep_fromRelativePolicyURI ]; michael@0: michael@0: // when this triggers the "onStopRequest" callback, it'll michael@0: // go to the next test. michael@0: (TESTS.shift())(); michael@0: } michael@0: michael@0: function makeChan(url) { michael@0: var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); michael@0: var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel); michael@0: return chan; michael@0: } michael@0: michael@0: function csp_doc_response(metadata, response) { michael@0: response.setStatusLine(metadata.httpVersion, 200, "OK"); michael@0: response.setHeader("Content-Type", "text/html", false); michael@0: response.bodyOutputStream.write(CSP_DOC_BODY, CSP_DOC_BODY.length); michael@0: } michael@0: michael@0: function csp_policy_response(metadata, response) { michael@0: response.setStatusLine(metadata.httpVersion, 200, "OK"); michael@0: response.setHeader("Content-Type", "text/csp", false); michael@0: response.bodyOutputStream.write(POLICY_FROM_URI, POLICY_FROM_URI.length); michael@0: } michael@0: michael@0: ///////////////////// TEST POLICY_URI ////////////////////// michael@0: function test_CSPRep_fromPolicyURI() { michael@0: do_test_pending(); michael@0: let csp = Cc["@mozilla.org/contentsecuritypolicy;1"] michael@0: .createInstance(Ci.nsIContentSecurityPolicy); michael@0: // once the policy-uri is returned we will compare our static CSPRep with one michael@0: // we generated from the content we got back from the network to make sure michael@0: // they are equivalent michael@0: let cspr_static = CSPRep.fromString(POLICY_FROM_URI, mkuri(DOCUMENT_URI)); michael@0: michael@0: // simulates the request for the parent document michael@0: var docChan = makeChan(DOCUMENT_URI); michael@0: docChan.asyncOpen(new listener(csp, cspr_static), null); michael@0: michael@0: // the resulting policy here can be discarded, since it's going to be michael@0: // "allow *"; when the policy-uri fetching call-back happens, the *real* michael@0: // policy will be in csp.policy michael@0: CSPRep.fromString("policy-uri " + POLICY_URI, michael@0: mkuri(DOCUMENT_URI), false, docChan, csp); michael@0: } michael@0: michael@0: function test_CSPRep_fromRelativePolicyURI() { michael@0: do_test_pending(); michael@0: let csp = Cc["@mozilla.org/contentsecuritypolicy;1"] michael@0: .createInstance(Ci.nsIContentSecurityPolicy); michael@0: // once the policy-uri is returned we will compare our static CSPRep with one michael@0: // we generated from the content we got back from the network to make sure michael@0: // they are equivalent michael@0: let cspr_static = CSPRep.fromString(POLICY_FROM_URI, mkuri(DOCUMENT_URI)); michael@0: michael@0: // simulates the request for the parent document michael@0: var docChan = makeChan(DOCUMENT_URI); michael@0: docChan.asyncOpen(new listener(csp, cspr_static), null); michael@0: michael@0: // the resulting policy here can be discarded, since it's going to be michael@0: // "allow *"; when the policy-uri fetching call-back happens, the *real* michael@0: // policy will be in csp.policy michael@0: CSPRep.fromString("policy-uri " + POLICY_URI_RELATIVE, michael@0: mkuri(DOCUMENT_URI), false, docChan, csp); michael@0: }