From 12f6f37ec3afee6ec038cafbb3792a114926edfc Mon Sep 17 00:00:00 2001 From: Bob Silverberg Date: Mon, 11 Apr 2016 18:08:54 -0400 Subject: [PATCH] Bug 1207394 - Make sure web_accessible_resources work with CSP/mixed content blocking, r=kmag MozReview-Commit-ID: 7UJ1IHwMosh --HG-- extra : transplant_source : R%A0F%AAOcn%92%2C%10%B3%09%A2%99J%EF%FE%EFy%8C --- .../extensions/test/mochitest/file_csp.html | 14 ++ .../test/mochitest/file_csp.html^headers^ | 1 + .../extensions/test/mochitest/file_mixed.html | 13 ++ .../extensions/test/mochitest/mochitest.ini | 3 + .../test_ext_web_accessible_resources.html | 206 ++++++++++++++++++ 5 files changed, 237 insertions(+) create mode 100644 toolkit/components/extensions/test/mochitest/file_csp.html create mode 100644 toolkit/components/extensions/test/mochitest/file_csp.html^headers^ create mode 100644 toolkit/components/extensions/test/mochitest/file_mixed.html diff --git a/toolkit/components/extensions/test/mochitest/file_csp.html b/toolkit/components/extensions/test/mochitest/file_csp.html new file mode 100644 index 000000000000..206e44390421 --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/file_csp.html @@ -0,0 +1,14 @@ + + + + + + + + +
Sample text
+ + + + + diff --git a/toolkit/components/extensions/test/mochitest/file_csp.html^headers^ b/toolkit/components/extensions/test/mochitest/file_csp.html^headers^ new file mode 100644 index 000000000000..4c6fa3c26a77 --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/file_csp.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: default-src 'self' diff --git a/toolkit/components/extensions/test/mochitest/file_mixed.html b/toolkit/components/extensions/test/mochitest/file_mixed.html new file mode 100644 index 000000000000..f3c7dda58054 --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/file_mixed.html @@ -0,0 +1,13 @@ + + + + + + + + +
Sample text
+ + + + diff --git a/toolkit/components/extensions/test/mochitest/mochitest.ini b/toolkit/components/extensions/test/mochitest/mochitest.ini index 08a2fd8bf181..b0efc0a11ade 100644 --- a/toolkit/components/extensions/test/mochitest/mochitest.ini +++ b/toolkit/components/extensions/test/mochitest/mochitest.ini @@ -2,6 +2,9 @@ skip-if = buildapp == 'mulet' || asan support-files = head.js + file_mixed.html + file_csp.html + file_csp.html^headers^ file_WebRequest_page1.html file_WebRequest_page2.html file_WebRequest_page3.html diff --git a/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html b/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html index 8d83dbd0ca08..0225df688c38 100644 --- a/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_web_accessible_resources.html @@ -15,6 +15,46 @@ /* eslint-disable mozilla/balanced-listeners */ +SimpleTest.registerCleanupFunction(() => { + SpecialPowers.clearUserPref("security.mixed_content.block_display_content"); +}); + +let image = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" + + "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="); +const IMAGE_ARRAYBUFFER = Uint8Array.from(image, byte => byte.charCodeAt(0)).buffer; + +function testImageLoading(src, expectedAction) { + let imageLoadingPromise = new Promise((resolve, reject) => { + let cleanupListeners; + let testImage = document.createElement("img"); + testImage.setAttribute("src", src); + + let loadListener = () => { + cleanupListeners(); + resolve(expectedAction === "loaded"); + }; + + let errorListener = () => { + cleanupListeners(); + resolve(expectedAction === "blocked"); + }; + + cleanupListeners = () => { + testImage.removeEventListener("load", loadListener); + testImage.removeEventListener("error", errorListener); + }; + + testImage.addEventListener("load", loadListener); + testImage.addEventListener("error", errorListener); + + document.body.appendChild(testImage); + }); + + imageLoadingPromise.then(success => { + browser.runtime.sendMessage({name: "image-loading", expectedAction, success}); + }); +} + add_task(function* test_web_accessible_resources() { function background() { let gotURL; @@ -140,6 +180,172 @@ add_task(function* test_web_accessible_resources() { yield extension.unload(); }); + +add_task(function* test_web_accessible_resources_csp() { + function background() { + browser.runtime.onMessage.addListener((msg, sender) => { + if (msg.name === "image-loading") { + browser.test.assertTrue(msg.success, `Image was ${msg.expectedAction}`); + browser.test.sendMessage(`image-${msg.expectedAction}`); + } else { + browser.test.sendMessage(msg); + } + }); + + browser.test.sendMessage("background-ready"); + } + + function content() { + window.addEventListener("message", function rcv(event) { + browser.runtime.sendMessage("script-ran"); + window.removeEventListener("message", rcv, false); + }, false); + + testImageLoading(browser.extension.getURL("image.png"), "loaded"); + + let testScript = document.createElement("script"); + testScript.setAttribute("src", browser.extension.getURL("test_script.js")); + document.head.appendChild(testScript); + browser.runtime.sendMessage("script-loaded"); + } + + function testScript() { + window.postMessage("test-script-loaded", "*"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "content_scripts": [{ + "matches": ["http://example.com/*/file_csp.html"], + "run_at": "document_start", + "js": ["content_script_helper.js", "content_script.js"], + }], + "web_accessible_resources": [ + "image.png", + "test_script.js", + ], + }, + background: `(${background})()`, + files: { + "content_script_helper.js": `${testImageLoading}`, + "content_script.js": `(${content})()`, + "test_script.js": `(${testScript})()`, + "image.png": IMAGE_ARRAYBUFFER, + }, + }); + + // This is used to watch the blocked data bounce off CSP. + function examiner() { + SpecialPowers.addObserver(this, "csp-on-violate-policy", false); + } + + let cspEventCount = 0; + + examiner.prototype = { + observe: function(subject, topic, data) { + cspEventCount++; + let spec = SpecialPowers.wrap(subject).QueryInterface(SpecialPowers.Ci.nsIURI).spec; + ok(spec.includes("file_image_bad.png") || spec.includes("file_script_bad.js"), + `Expected file: ${spec} rejected by CSP`); + }, + + // We must eventually call this to remove the listener, + // or mochitests might get borked. + remove: function() { + SpecialPowers.removeObserver(this, "csp-on-violate-policy"); + }, + }; + + let observer = new examiner(); + + yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]); + + let win = window.open("http://example.com/tests/toolkit/components/extensions/test/mochitest/file_csp.html"); + + yield Promise.all([ + extension.awaitMessage("image-loaded"), + extension.awaitMessage("script-loaded"), + extension.awaitMessage("script-ran"), + ]); + is(cspEventCount, 2, "Two items were rejected by CSP"); + win.close(); + + observer.remove(); + yield extension.unload(); +}); + +add_task(function* test_web_accessible_resources_mixed_content() { + function background() { + browser.runtime.onMessage.addListener(msg => { + if (msg.name === "image-loading") { + browser.test.assertTrue(msg.success, `Image was ${msg.expectedAction}`); + browser.test.sendMessage(`image-${msg.expectedAction}`); + } else { + browser.test.sendMessage(msg); + if (msg === "accessible-script-loaded") { + browser.test.notifyPass("mixed-test"); + } + } + }); + + browser.test.sendMessage("background-ready"); + } + + function content() { + testImageLoading("http://example.com/tests/toolkit/components/extensions/test/mochitest/file_image_bad.png", "blocked"); + testImageLoading(browser.extension.getURL("image.png"), "loaded"); + + let testScript = document.createElement("script"); + testScript.setAttribute("src", browser.extension.getURL("test_script.js")); + document.head.appendChild(testScript); + + window.addEventListener("message", event => { + browser.runtime.sendMessage(event.data); + }); + } + + function testScript() { + window.postMessage("accessible-script-loaded", "*"); + } + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "content_scripts": [{ + "matches": ["https://example.com/*/file_mixed.html"], + "run_at": "document_start", + "js": ["content_script_helper.js", "content_script.js"], + }], + "web_accessible_resources": [ + "image.png", + "test_script.js", + ], + }, + background: `(${background})()`, + files: { + "content_script_helper.js": `${testImageLoading}`, + "content_script.js": `(${content})()`, + "test_script.js": `(${testScript})()`, + "image.png": IMAGE_ARRAYBUFFER, + }, + }); + + SpecialPowers.setBoolPref("security.mixed_content.block_display_content", true); + + yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]); + + let win = window.open("https://example.com/tests/toolkit/components/extensions/test/mochitest/file_mixed.html"); + + yield Promise.all([ + extension.awaitMessage("image-blocked"), + extension.awaitMessage("image-loaded"), + extension.awaitMessage("accessible-script-loaded"), + ]); + yield extension.awaitFinish("mixed-test"); + win.close(); + + yield extension.unload(); +}); +