Reputation: 34013
I am working with some code that needs to be run as a page, and if it's being run as a Chrome Extension, I want to be able to do additional things. What I'm using is:
<script>
if (chrome && chrome.extension) {
// extension stuff
}
</script>
This seems like a good capability detection. Using user agent string causes me trouble because it's the same no matter the context (web page vs. extension).
Question: are there other more reliable techniques for detecting if a piece of code is running inside a Chrome extension?
Update: I'm wondering if there's something I can put into my manifest.json
file that I can then read back. Note, that the extension I'm working on is not intended as a persistent thing that runs all the time, it's a content application that runs in a single window or browser tab and has no need to interact with other windows or tabs or anything else.
Upvotes: 25
Views: 7332
Reputation: 6836
Credit to Chad Scira
for the original answer mine is based of.
I grabbed Chad's answer and I compacted it to ES2021 standards. Removed a lot of repeated content required for previous versions too.
const runningAt = (() => {
let getBackgroundPage = chrome?.extension?.getBackgroundPage;
if (getBackgroundPage){
return getBackgroundPage() === window ? 'BACKGROUND' : 'POPUP';
}
return chrome?.runtime?.onMessage ? 'CONTENT' : 'WEB';
})();
The above should detect the 4 scenarios
Upvotes: 1
Reputation: 9859
I had the need for something similar.
But I didn't need to care about sites attempting to trick the code.
const SCRIPT_TYPE = (() => {
if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window) {
return 'BACKGROUND';
} else if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() !== window) {
return 'POPUP';
} else if (!chrome || !chrome.runtime || !chrome.runtime.onMessage) {
return 'WEB';
} else {
return 'CONTENT';
}
})();
The above should detect the 4 scenarios
Upvotes: 8
Reputation: 898
Chrome does not provide any direct API to check the running status of the application that is it running in extension popup or in chrome page view. however, One indirect trick can work is we can match the extension body resolution that is equal or less than as specified in CSS (in this case, extension popup will be open) or greater than that (in this case, web page view will be open).
Upvotes: 1
Reputation: 51
I noticed that in Chrome, the chrome object, that is property of the global window object, could not be deleted. If it is user-defined property delete operation is successful. So you could test in this way:
var isRunningInExtension = (!(delete window.chrome) && chrome.extension) ?
true : false;
UPDATE: Line above does not really guarantee that code is running in chrome extension. Everyone can create an object called 'chrome' with an 'extension' property and then freeze/seal that object - doing this will be enough to pass the check and have an incorrect result that your code is running inside a chrome extension.
The ensure you are running your code in an extension, you have to test the global chrome object before any javascript to be run - that way you will have guarantee that no fake chrome object is created before testing.
One possible solution is to use an iframe - in my example below i use iframe's sandbox property to instruct the iframe not to execute any scripts(even scripts included with script tag) - in that way i can ensure no script will be able to modify the global window.chrome object.
(function() {
var isChromeExtension = (function () {
var contentWindow,
iframe = document.createElement("iframe"),
isChromeExtension;
// test for sandbox support. It is supported in most recent version of Chrome
if ("sandbox" in iframe) {
try {
iframe.sandbox = "allow-same-origin";
iframe.src=location.href;
iframe.style="display: none";
document.body.appendChild(iframe);
contentWindow = iframe.contentWindow;
isChromeExtension = !!(contentWindow.chrome && contentWindow.chrome.extension);
document.body.removeChild(iframe);
} catch(e) {}
}
return isChromeExtension;
}());
}());
result could be:
Upvotes: 1
Reputation: 348972
So many complicated answers here, while you can easily detect whether you're running in a Chrome extension by checking for existence and non-emptiness of chrome.runtime.id
:
if (window.chrome && chrome.runtime && chrome.runtime.id) {
// Code running in a Chrome extension (content script, background page, etc.)
}
Upvotes: 44
Reputation: 791
I know this is old, but just thought I'd offer an alternative. You can add another javascript file to the chrome extension, so it contains two .js files. manifest.json would have:
"js": ["additionalscript.js", "yourscript.js"]
The additionalscript.js could simply declare a variable var isextension = true
. yourscript.js can check typeof isextension != 'undefined'
.
But perhaps a more interesting example, it could declare
var adminpassword='letmein'
Now yourscript.js only has access to adminpassword when running in the extension.
(Of course you would not embed a password in a script file if the plugin were on a machine that could be compromised)
Upvotes: 1
Reputation: 45525
Pragmatically that's a good approach. Theoretically (not sure if this is relevant or not, e.g. may provide vulnerabilities) it can be spoofed very easily. I suppose it depends on your context how relevant that is.
Here's a slightly stronger idea:
if (chrome &&
chrome.windows &&
chrome.windows.get &&
typeof chrome.windows.get === 'function' &&
chrome.windows.get.toString() === 'function get() { [native code] }')
The idea is the same as yours, although it's slightly stronger, since AFAIK having an object be a function and having it's toString()
value have that value is impossible since it's not valid syntax, so even trying to spoof that value wouldn't work unless you altered the native code (which requires a whole different level of hacker).
Don't offhand remember if checking things like this requires permissions or not, but the idea is clear I hope.
UPDATE
I just realised that the "native code" syntax idea can be fooled, by aliasing an existing function. E.g.
var FakeFn = Object.create;
FakeFn.toString(); // "function create() { [native code] }"
But that can be taken care of by careful selection of which function we use, since the name appears in the string. get
is probably too common, but if we take an obscure function name (like captureVisibleTab
of chrome.tabs.captureVisibleTab
) that is implemented only in chrome extensions, it is still a very portable solution, because unlike the basic check where code can be fooled by other local user code, it is known in advance that the browsers don't implement any native functions with this name, so it's still safe in all browsers and with all user code.
UPDATE
As @Mathew pointed out, this idea is foolable (although seemingly only maliciously). I thought I could patch the problem by comparing to Function.prototype.toString
but figured that even that can be fooled by aliasing the original toString
method and creating a new one that for certain functions returns false strings and for others returns the original string.
In conclusion, my idea is slightly stronger than the original, in that it will rule out practically all chance of unintentional collision (slightly more than the OP's idea), but is certainly no defence against a malicious attack as I first thought it might be.
Upvotes: 5