fix: Prevent a webpage document element to be detected as the Extension API object (#153)

This commit is contained in:
Luca Greco 2018-08-15 22:33:37 +02:00 committed by GitHub
parent 450eee59a3
commit ebd28186a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 79 additions and 5 deletions

View File

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"; "use strict";
if (typeof browser === "undefined") { if (typeof browser === "undefined" || Object.getPrototypeOf(browser) !== Object.prototype) {
const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received."; const CHROME_SEND_MESSAGE_CALLBACK_NO_RESPONSE_MESSAGE = "The message port closed before a response was received.";
const SEND_RESPONSE_DEPRECATION_WARNING = "Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)"; const SEND_RESPONSE_DEPRECATION_WARNING = "Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)";

View File

@ -0,0 +1,20 @@
<!DOCTYPE>
<html>
<head>
<title>Test Extension Background page</title>
<meta charset="utf-8">
</head>
<body>
<!--
The following two DOM elements ensure that the polyfill doesn't detect the
globals defined for these DOM element ids as the Extensions API objects.
-->
<div id="browser"></div>
<div id="browser"></div>
<div id="chrome"></div>
<script src="copy-original-api-objects.js"></script>
<script src="browser-polyfill.js"></script>
<script src="background.js"></script>
</body>
</html>

View File

@ -0,0 +1,14 @@
/* global originalAPIObjects */
browser.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
if (msg !== "test-api-object-in-background-page") {
throw new Error(`Unexpected message received: ${msg}`);
}
return {
browserIsDefined: !!browser,
chromeIsDefined: !!chrome,
browserIsUnchanged: browser === originalAPIObjects.browser,
windowBrowserIsUnchanged: window.browser === originalAPIObjects.browser,
};
});

View File

@ -1,10 +1,12 @@
/* global originalAPIObjects */
test("browser api object in content script", (t) => { test("browser api object in content script", (t) => {
t.ok(browser && browser.runtime, "a global browser API object should be defined"); t.ok(browser && browser.runtime, "a global browser API object should be defined");
t.ok(chrome && chrome.runtime, "a global chrome API object should be defined"); t.ok(chrome && chrome.runtime, "a global chrome API object should be defined");
if (navigator.userAgent.includes("Firefox/")) { if (navigator.userAgent.includes("Firefox/")) {
// Check that the polyfill didn't create a polyfill wrapped browser API object on Firefox. // Check that the polyfill didn't create a polyfill wrapped browser API object on Firefox.
t.equal(browser.runtime, chrome.runtime, "browser.runtime and chrome.runtime should be equal on Firefox"); t.equal(browser, originalAPIObjects.browser, "browser API object should not be changed on Firefox");
// On Firefox, window is not the global object for content scripts, and so we expect window.browser to not // On Firefox, window is not the global object for content scripts, and so we expect window.browser to not
// be defined. // be defined.
t.equal(window.browser, undefined, "window.browser is expected to be undefined on Firefox"); t.equal(window.browser, undefined, "window.browser is expected to be undefined on Firefox");
@ -16,3 +18,18 @@ test("browser api object in content script", (t) => {
t.equal(browser, window.browser, "browser and window.browser should be the same object"); t.equal(browser, window.browser, "browser and window.browser should be the same object");
} }
}); });
test("browser api object in background page", async (t) => {
const reply = await browser.runtime.sendMessage("test-api-object-in-background-page");
t.ok(reply.browserIsDefined, "a global browser API object should be defined");
t.ok(reply.chromeIsDefined, "a global chrome API object should be defined");
if (navigator.userAgent.includes("Firefox/")) {
t.ok(reply.browserIsUnchanged, "browser API object should not be changed on Firefox");
t.ok(reply.windowBrowserIsUnchanged, "window.browser API object should not be changed on Firefox");
} else {
t.ok(!reply.browserIsUnchanged, "browser API object should have been defined by the polyfill");
t.ok(!reply.windowBrowserIsUnchanged, "window.browser API object should have been defined by the polyfill");
}
});

View File

@ -0,0 +1,8 @@
// Store a copy of the references to the original API objects.
const originalAPIObjects = { // eslint-disable-line no-unused-vars
// NOTE: on the Browsers that do not provide the browser API object natively,
// this will initially point to the HTMLCollection of the <div id="browser"> elements
// that are part of the background page.
browser,
chrome,
};

View File

@ -3,12 +3,16 @@
"name": "test-detect-browser-api-object-in-content-script", "name": "test-detect-browser-api-object-in-content-script",
"version": "0.1", "version": "0.1",
"description": "test-detect-browser-api-object-in-content-script", "description": "test-detect-browser-api-object-in-content-script",
"background": {
"page": "background.html"
},
"content_scripts": [ "content_scripts": [
{ {
"matches": [ "matches": [
"http://localhost/*" "http://localhost/*"
], ],
"js": [ "js": [
"copy-original-api-objects.js",
"browser-polyfill.js", "browser-polyfill.js",
"tape.js", "tape.js",
"content.js" "content.js"

View File

@ -6,5 +6,13 @@
</head> </head>
<body> <body>
<h1>Browser Polyfill Test Page</h1> <h1>Browser Polyfill Test Page</h1>
<!--
The following two DOM elements ensure that the polyfill doesn't detect the
globals defined for these DOM element ids as the Extensions API objects.
-->
<div id="browser"></div>
<div id="browser"></div>
<div id="chrome"></div>
</body> </body>
</html> </html>

View File

@ -18,8 +18,8 @@ defineExtensionTests({
}); });
defineExtensionTests({ defineExtensionTests({
description: "polyfill should detect an existent browser API object in content scripts", description: "polyfill should detect an existing browser API object in content scripts and extension pages",
extensions: ["detect-browser-api-object-in-content-script"], extensions: ["detect-existing-browser-api-object"],
}); });
defineExtensionTests({ defineExtensionTests({

View File

@ -43,7 +43,10 @@ function setupTestDOMWindow(chromeObject, browserObject = undefined) {
// Set (or reset) the browser property. // Set (or reset) the browser property.
if (browserObject) { if (browserObject) {
window.browser = browserObject; // Make the fake browser object a `window.Object` instance, so that
// it passes the `Object.getPrototypeOf(browser) !== Object.prototype`
// check, otherwise it is going to be overridden by the polyfill (See #153).
window.browser = Object.assign(window.Object(), browserObject);
} else { } else {
delete window.browser; delete window.browser;
} }