P5 sound content secure policy chrome extension [duplicate] - google-chrome-extension

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"] }
}

Related

how can i get the local file in chrome extension v3 [duplicate]

I have a Chrome extension that can (if you allow access to file URLs) grab your local pdf file that you have open in chrome and send it on to our API for processing. This is done by fetching the pdf with XMLHttpRequest to file:///Users/user/whatever/testfile.pdf from the background script.
When migrating to manifest v3 for a Chrome extension the background script becomes a service worker. In a service worker only fetch is available, not XMLHttpRequest. Problem is, fetch only supports http and https, not file:// urls. So how can I make the same feature of having the Chrome extension fetching/getting the local file?
EDIT: Things I also tried:
Making the XMLHttpRequest from injected iframe as suggested by answer.
This gives error net:ERR_UNKNOWN_URL_SCHEME when making the request
Making the XMLHttpRequest from injected content script.
This gives error Access to XMLHttpRequest at 'file:///.../testfile1.docx.pdf' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
From what I can understand from a lot of research access to file:// is in general blocked and Chrome extension background scripts used to be an exception to this. Seems to me it was never allowed from content scripts or action popups.
My manifest.json for reference:
{
"manifest_version": 3,
"name": "..",
"version": "0.1",
"icons": {
"16": "assets/icon-16x16.png",
"48": "assets/icon-48x48.png",
"128": "assets/icon-128x128.png"
},
"action": {
"default_title": ".."
},
"background": {
"service_worker": "background.js"
},
"permissions": [
"webRequest",
"activeTab",
"scripting",
"storage",
"unlimitedStorage",
"identity",
"pageCapture"
],
"host_permissions": [
"<all_urls>"
],
"web_accessible_resources": [{
"resources": ["iframe.html"],
"matches": [],
"extension_ids": []
}]
}
The content script is injected programmatically (using webextension-polyfill for promise support)
browser.action.onClicked.addListener(async (tab: Tab) => {
await browser.scripting.executeScript({files: [ "inject.js" ], target: {tabId: tab.id}});
});
Chrome 98 and older can't do it in the background service worker for the reasons you mentioned.
There was also a bug that prevented doing it in a normal visible chrome-extension:// page or iframe. It's fixed in Chrome 91.
Solution
Use fetch in Chrome 99 and newer.
In older versions use the following workarounds.
Workaround 1: File System API, Chrome 86+
A ManifestV3 extension can use the new File System API to read the contents of the file, for example inside the iframe exposed via web_accessible_resources.
Workaround 2. Extension frame, Chrome 91+
Use a content script that runs in the tab with that pdf:
matches in manifest.json should contain <all_urls> or file://*/* and file access should be enabled by the user in
chrome://extensions UI for your extension. Alternatively you can use
activeTab permission and programmatic injection when the user
clicks your extension's icon or invokes it through the context menu.
The content script adds an invisible iframe that points to iframe.html file exposed in web_accessible_resources
The iframe.html loads iframe.js which uses XMLHttpRequest as usual. Since the iframe has chrome-extension:// URL its environment
is the same as your old background script so you can do everything
you did before right there.
Workaround 3. Extension window/tab, Chrome 91+
An alternative solution is to use any other visible page of your
extension like the action popup or options page or any other
chrome-extension:// page belonging to your extension as they can
access the file:// URL via XMLHttpRequest same as before.
Notes
File access should be enabled in chrome://extensions page for this extension.

Chrome extension refusing to load external scripts

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"] }
}

How do I allow a chrome extension to access a third-party API script?

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

Why the extension with specific key declared can access chrome:// pages?

As we know, by default chrome extensions doesn't have access to chrome:// pages such as chrome://extensions and chrome://settings. ( Of course we can change chrome://flags/#extensions-on-chrome-urls flags however the following question is based on that we didn't change the default flags).
Recently I happen to find ChromeVox (offered by chrome.google.com) can work well in all pages including chrome:// pages. I checked the source code for this extension and find as long as we add the following line in manifest.json for any extension, the extension can work well in chrome:// pages.
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEGBi/oD7Yl/Y16w3+gee/95/EUpRZ2U6c+8orV5ei+3CRsBsoXI/DPGBauZ3rWQ47aQnfoG00sXigFdJA2NhNK9OgmRA2evnsRRbjYm2BG1twpaLsgQPPus3PyczbDCvhFu8k24wzFyEtxLrfxAGBseBPb9QrCz7B4k2QgxD/CwIDAQAB"
So it looks like chrome has something like whitelist to allow specific extensions to break the default restrictions. Am I right? Is there official guide to clarify this behavior?
Appendix:
The following is a sample extension, you will find with the key, console will output test even in chrome://extensions pages; however once removing the key, nothing happens.
manifest.json:
{
"manifest_version": 2,
"name": "Test",
"version": "1.0",
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"content.js"
]
}
],
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEGBi/oD7Yl/Y16w3+gee/95/EUpRZ2U6c+8orV5ei+3CRsBsoXI/DPGBauZ3rWQ47aQnfoG00sXigFdJA2NhNK9OgmRA2evnsRRbjYm2BG1twpaLsgQPPus3PyczbDCvhFu8k24wzFyEtxLrfxAGBseBPb9QrCz7B4k2QgxD/CwIDAQAB"
}
content.js:
console.log('test');
"key" property in manifest.json uniquely defines the extension's ID in encrypted form.
Some Google extensions are unfairly(?) whitelisted by ID in the source code of chromium.
In this case, ChromeVox:
scripting_whitelist_.push_back(extension_misc::kChromeVoxExtensionId);
And then this whitelist is checked to see whether an extension can run everywhere in PermissionsData::CanExecuteScriptEverywhere, which is accessed in CheckRestrictedUrls where we can see restricted schemes: chrome://, chrome-extension://, chrome-debugger://

Chrome extension bug that could be related to cross-origin permissions

We run an extension that requires fetching and searching for data on multiple websites.
We have been using cross-origin XMLHttpRequests using Jquery, and have not faced an issue until now.
The asynchronous requests are being executed successfully. This has been the case even though we have not explicitly requested cross-origin permissions as suggested here: https://developer.chrome.com/extensions/xhr
This is what the relevant portions of our manifest currently look like:
{
"background" : {
"scripts": ["background.js"]
},
"permissions" : ["storage" ],
"content_scripts" : [
{
"matches" : ["<all_urls>"],
"js" : [ "jquery-2.0.0.min.js","jquery-ui-1.10.3.custom.min.js","date.js",
"file1.js","file2.js",
"fileN.js"],
"run_at" : "document_idle",
"all_frames" : false
},
],
"content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'",
"web_accessible_resources" : [ "icona.png" , "iconb.png","iconc.png"],
"manifest_version": 2
}
Even though the permissions do not explicitly request access to urls from which data is asynchronously fetched, the extension has worked fine.
Off late, we have had a few complaints from users that the extension no longer works and no data is being displayed. We have not been able to replicate this issue in Chrome on Linux (Version 34.0.1847.132). The users who seem to be facing this issue seem to be using Mac OS X or, less frequently, Windows.
We cannot figure out why this issue is OS specific, or if that's a curious correlation.
If the problem is indeed one of wrong permissions, can we set the permission to
["http://*/","https://*/"]
without having the extension disabled automatically for manual re-enabling by the user?
We already require permissions for all urls through "matches" : ["<all_urls>"] Does this ensure that the addition of permissions as above will not trigger automatic disabling of the extension?
Chrome extensions allow for cross-origin requests, but you have to declare the hosts you want to access in the permissions section of your manifest. The matches section of content scripts shouldn't give you host permissions.
You should add host permissions to your manifest. I don't know what will happen on update. Considering that the user was already prompted to allow your extension access to all their web data, maybe your extension won't be disabled on update. You can simply test that by creating a testers only extension on the webstore with your original version, install it, update it, and see what happens.

Resources