I am trying to get a library with WebAssembly running with manifest v3 in a Chrome extension.
I have a basic file (call it sandbox.html) which executes some JavaScript:
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
}
This is embedded in a page with this tag:
<iframe
sandbox="allow-same-origin allow-scripts"
csp="script-src 'self' 'unsafe-eval'; object-src 'self'" src="sandbox.html">
</iframe>
I have not added sandbox or content_security_policy to my manifest.json because I require allow-same-origin, see this comment.
However I get the following error:
Uncaught CompileError: WebAssembly.Module(): Wasm code generation disallowed by embedder
According to this and this, it's suggested that using unsafe-eval should be enough to make this run. However as far as I am aware, I am doing that but I still get this error.
(I believe I should be using wasm-eval over unsafe-eval; I have tried both and neither work. I have also tried including content_security_policy without allow-same-origin in my manifest.json but excluding the sandbox key.)
Am I missing something?
Related
I'm building a chrome extension using manifest version 3. The entire point of the extension is to inject an external dynamic script into the page (for example, https://example.com/).
I keep receiving the following message:
Refused to load the script 'https://example.com/' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
I tried to add a content_security_policy entry to my manifest file, but I can't seem to get it to work. Assuming that this script that I'm loading will call several additional scripts from different domains, what can be done to enable this?
manifest V2
In Chrome extension with manifest V2 you can do it like this:
manifest.json
"content_security_policy": "script-src 'self' https://example.com/;
manifest V3
Remotely hosted code
In Manifest V3, all of your extension's logic must be bundled with the extension. You can no longer load and execute a remotely hosted file. A number of alternative approaches are available, depending on your use case and the reason for remote hosting.
source: https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/#remotely-hosted-code
Manifest v3 Method
I could not find a way to include a remote script but if it is possible that you can download the external JS file and place it as part of the extension, it will be bundled along with the extension's code and you can refer to it from within your content-scripts as well using a relative URL. Here is how you can do it.
Assuming that you have example.js downloaded and placed alongside your content script files. In your manifest.json, add the file to web_accessible_resources
web_accessible_resources: [
...
{
resources: ['example.js'],
matches: ['*://*/*'],
},
],
Next, add the following code to you content-script.
const inject = () => {
var r = document.createElement('script');
r.setAttribute('src', 'chrome-extension://' + chrome.runtime.id + '/example.js');
(document.head || document.documentElement).appendChild(r);
};
inject();
Note that the example.js file will be immediately injected and executed as soon as the content-script that has the above code runs. Also make sure that if you have a bundler like webpack in place, you need to configure it to include the JS file in the final bundle.
You could use the declarativeNetRequest API to add a rule to modify the CSP header to whatever you need it to be to accept that script. For example something like:
{
"id": 11,
"priority": 1,
"action": {
"type": "modifyHeaders",
"responseHeaders": [
{ "header": "Content-Security-Policy", "operation": "append", "value": "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://example.com" }
]
},
"condition": { "urlFilter": "my-domain.com", "resourceTypes": ["sub_frame"] }
}
I'm building a chrome extension using manifest version 3. The entire point of the extension is to inject an external dynamic script into the page (for example, https://example.com/).
I keep receiving the following message:
Refused to load the script 'https://example.com/' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
I tried to add a content_security_policy entry to my manifest file, but I can't seem to get it to work. Assuming that this script that I'm loading will call several additional scripts from different domains, what can be done to enable this?
manifest V2
In Chrome extension with manifest V2 you can do it like this:
manifest.json
"content_security_policy": "script-src 'self' https://example.com/;
manifest V3
Remotely hosted code
In Manifest V3, all of your extension's logic must be bundled with the extension. You can no longer load and execute a remotely hosted file. A number of alternative approaches are available, depending on your use case and the reason for remote hosting.
source: https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/#remotely-hosted-code
Manifest v3 Method
I could not find a way to include a remote script but if it is possible that you can download the external JS file and place it as part of the extension, it will be bundled along with the extension's code and you can refer to it from within your content-scripts as well using a relative URL. Here is how you can do it.
Assuming that you have example.js downloaded and placed alongside your content script files. In your manifest.json, add the file to web_accessible_resources
web_accessible_resources: [
...
{
resources: ['example.js'],
matches: ['*://*/*'],
},
],
Next, add the following code to you content-script.
const inject = () => {
var r = document.createElement('script');
r.setAttribute('src', 'chrome-extension://' + chrome.runtime.id + '/example.js');
(document.head || document.documentElement).appendChild(r);
};
inject();
Note that the example.js file will be immediately injected and executed as soon as the content-script that has the above code runs. Also make sure that if you have a bundler like webpack in place, you need to configure it to include the JS file in the final bundle.
You could use the declarativeNetRequest API to add a rule to modify the CSP header to whatever you need it to be to accept that script. For example something like:
{
"id": 11,
"priority": 1,
"action": {
"type": "modifyHeaders",
"responseHeaders": [
{ "header": "Content-Security-Policy", "operation": "append", "value": "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://example.com" }
]
},
"condition": { "urlFilter": "my-domain.com", "resourceTypes": ["sub_frame"] }
}
I am trying to access a script from YouTube's iframe player API in order to play/pause a video, for a chrome extension. My JS is below (from https://developers.google.com/youtube/iframe_api_reference).
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('video', {
events: {
'onReady': onPlayerReady
}
});
}
function onPlayerReady(event) {
player.playVideo();
}
However, when I load the extension to Chrome, I get the following error:
Refused to load the script 'https://www.youtube.com/iframe_api' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
I tried adding the URL to the CSP in Manifest.json (below). I also removed all inline JS.
"content_security_policy": "script-src 'self' https://www.youtube.com/iframe_api; object-src 'self'"
Now, the extension fails to load with an error that my CSP value is invalid. Any idea what I'm doing wrong?
You can check the chrome.scripting.executeScript(), this allows you to load the library and then execute some code that uses it. To do so you need to have it in the same folder as the rest of the extension (just copy the library to a js file). The code that uses the library must be executed after the initial promise of chrome.scripting.executeScript is resolved.
https://developer.chrome.com/docs/extensions/reference/scripting/#usage
I'm building a Chrome Extension that interacts with an API that I wish to protect using Google recatcha, as I'm intending for it to be used beyond just the Chrome Extension. The API side is working, correctly verifying a recaptcha response with Google, however, displaying the recatcha widget inside the extension's browser action window is resulting in the following javascript error:
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('chrome-extension://pjhcgfibbbmibojnlkgjejaojpokgldl:80') does not match the recipient window's origin ('chrome-extension://pjhcgfibbbmibojnlkgjejaojpokgldl').
which is generated in recaptcha_en.js at the line:
(a.window || ne(a.Tg)).postMessage(JSON.stringify({
message: c || null ,
messageType: b
}), Zj(a.path));
The problem seems to be that the value of a.path is chrome-extension://pjhcgfibbbmibojnlkgjejaojpokgldl:80 but I don't know how I can affect this value.
The result of this error is that the recaptcha widget displays a message saying:
Please upgrade to a supported browser to get a reCAPTCHA challenge.
I'm displaying the widget via the auto render method, but have also tried the explicit method which results in the same error. For info, I'm using the following content_security_policy:
script-src 'self' https://*.google.com https://*.gstatic.com; object-src 'self'
Is there any way I can get this to work in my extension? And if not, what is the best alternative method for integrating recaptcha?
In recaptcha v3
manifest.json
"content_security_policy": "script-src 'self' https://.google.com https://.gstatic.com; object-src 'self'"
ADD CHROME EXTENCION ID TO ALLOWED DOMAINS IN RECAPTCHA ADMIN CONSOLE
chrome-extension://aailnablglhloogfnpkgddnjjfimhhhg
so you just put "aailnablglhloogfnpkgddnjjfimhhhg" without chrome-extension://
The chrome extension guide has a tutorial for the old analytics install: https://developer.chrome.com/extensions/tut_analytics.html
The instructions just say to link to the https version and update the manifest to allow loading scripts from that URL. So those should still apply to the new version. And in fact I can see the script loading from the server.
Once the script loads analytics does not properly initialize it self and never processes it's internal queue (ga.f) to send those events to the server. There is no error in the console. It's just quietly does nothing.
My guess is that the new Universal Analytics is just not set up to run in the the extension environment but the universal docs make no mention of that: https://developers.google.com/analytics/devguides/collection/analyticsjs/
does anyone know if it's even possible to add Universal Analytics to an extension yet and when that might be added?
There's an issue for that on Google code: The solution is to pass analytics your own protocol check function or simply null for no checking, in an official way.
This has to come after ga('create', ...) :
ga('set', 'checkProtocolTask', null); // Disable file protocol checking.
So you don't need to modify the original analytics.js script. Just include the standard tracking code snippet (dont' forget to add the "https:" prefix) and add "https://www.google-analytics.com" to your Content Security Policy.
A note to ayal gelles' solution:
It is not necessary to add chrome-extension://... to the Content Security Policy since it's already included in the 'self' statement. Also, instead of loading the script via hardcoded URL you should use chrome.runtime.getURL("path/to/analytics.js"). This way you don't need to know your extension's ID, Chrome will fill it in for you.
I wrote up a blog post on this - How to add Google’s Universal Analytics tracking to a Chrome extension
Here's the guts of it:
// Standard Google Universal Analytics code
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); // Note: https protocol here
ga('create', 'UA-XXXXX-YY', 'auto');
ga('set', 'checkProtocolTask', function(){});
ga('send', 'pageview', '/options.html');
There are 3 points I’d particularly like to highlight:
Specify “https” at the start of the script address to match with the listing in the manifest.json file
Override checkProtocolTask with an empty function
Send a virtual pageview by specifying the path – /options.html – otherwise Google Analytics will reject a URL in the format chrome-extension://gdocgfhmbfbbbmhnhmmejncjdcbjkhfc/options.html
I just encountered this and seem to have hacked my way through. This might break at some point or not be fully functional, but here goes:
Download the GA uglified+minified source code from here: https://www.google-analytics.com/analytics.js, put in your chrome extension folder, where it could be later loaded by the background page.
In it, find a function that looks something like this:
function Oa(){var a=M[B][E];if("http:"!=a&&"https:"!=a)throw"abort";}.
This is the "point of failure" since our "protocol" is "chrome-extension:" and not either of the two.
So.. change this function to be something like:
function Oa(){var a=M[B][E];if("chrome-extension:"!=a&&"http:"!=a&&"https:"!=a)throw"abort";}
add a "Content Security Policy" of this sort to your manifest file, make sure it points to YOUR LOCAL version of analytics.js you have just modified:
"content_security_policy": "script-src 'self' chrome-extension://EXTENSIONID/path/to/analytics.js; object-src 'self'",
Change the GA snippet to ALSO point to that same file, something like this:
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','chrome-extension://EXTENSIONID/path/to/analytics.js','ga');
hope this helps.
I managed to get Google Analytics up and running using Chrome Platform Analytics (CPA). The only confusing part is how to set up a property in the administration console of GA. I had to create a Mobile Application property, which is not too intuitive.
Also, I created an options page that lets users disable analytics if desired, to comply with the opt-out requirements.
I hope that helps!
Regarding new analytics.js (as opposite to old ga.js) this example works for me:
function setupGoogleAnalytics() {
if (!window.ga) {
(function(){
window.ga = function() {
(window.ga.q = window.ga.q || []).push(arguments);
}, window.ga.l = 1 * new Date();
var tag = 'script';
var a = document.createElement(tag);
var m = document.getElementsByTagName(tag)[0];
a.async = 1;
a.src = 'https://www.google-analytics.com/analytics.js';
m.parentNode.insertBefore(a, m);
})();
ga('create', 'UA-XXXXXXX-Y', 'auto');
ga('set', 'checkProtocolTask', null);
}
}
Please note that you need to add following content_security_policy snippet to the manifest.json:
{
...
"content_security_policy": "script-src 'self' https://www.google-analytics.com; object-src 'self'"
...
}
There's a way to use the Measurement Protocol to communicate with Google Analytics.
I have developed a script for that :
https://github.com/melalj/universal-ga-extension