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/. */
"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 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) => {
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");
if (navigator.userAgent.includes("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
// be defined.
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");
}
});
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",
"version": "0.1",
"description": "test-detect-browser-api-object-in-content-script",
"background": {
"page": "background.html"
},
"content_scripts": [
{
"matches": [
"http://localhost/*"
],
"js": [
"copy-original-api-objects.js",
"browser-polyfill.js",
"tape.js",
"content.js"

View File

@ -6,5 +6,13 @@
</head>
<body>
<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>
</html>

View File

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

View File

@ -43,7 +43,10 @@ function setupTestDOMWindow(chromeObject, browserObject = undefined) {
// Set (or reset) the browser property.
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 {
delete window.browser;
}