User configurable url permissions in chrome extension - google-chrome-extension

I am creating a chrome extension that adds an action with a global hotkey to JIRA. I can hard code the url for my own instance of JIRA into my extension, but I would like this url to be user configurable as other users will have different urls for their own JIRA instances.
I would like to know if there is a better way of doing this than just giving my extension permission for all urls and checking in my background script to compare the current url to the one the user has chosen as a setting. Ideally my background/inject script would only run on the users chosen url. I looked at the permissions api but can't figure it out.
My current manifest.json looks as follows, I have hard coded permission to my jira url, is there a way to make this permission user configurable?
{
"permissions": [
"contentSettings",
"storage",
"commands",
"http://myjiraurl/*"
],
"background": {
"matches": [
"http://myjiraurl/*"
],
"scripts": ["src/background/background.js"]
},
"content_scripts": [
{
"matches": [
"http://myjiraurl/*"
],
"js": [
"js/jquery.min.js",
"src/inject/inject.js"
]
}
]
}

Whatever you do, in order to inject into arbitrary hosts, you have to have http://*/ and https://*/ in your permissions, preferably optional permissions. Your proposal - comparing the current url with the one in the settings - sounds very reasonable: Just compare the current url with the one in the setting and make a chrome.permissions.request if it matches, silently stop running your code if it fails.
I can think of one work-around to avoid that, but honestly it's not giving you much:
Use http://*/ and https://*/ as optional permissions (you should really really make them optional anyway).
Make the chrome.permissions.request call at the time the user is modifying the options.
In your background script, make a chrome.permissions.contains call for the current url and silently stop running your code if there's no permission.
The result of the above is that instead of comparing the current url to the one in the setting, it will compare the current url with whatever the extension has previously got permission for. I guess it simplifies the background script a bit (you don't have to get the url stored in the settings), but makes the options page code a bit more complicated.

Related

Chrome extension: How to inject HTML into any tab w/o using <all_urls>

I am trying to inject a content script to the current tab of the user once the timer is done. Thus, my manifest is:
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["jquery-3.5.1.min.js", "flipclock-min.js", "content.js"],
"css": ["normalize.min.css", "styles2.css", "flipclock.css", "css.css"]
}]
According to web store publishing process:
Broad Host Permissions
Instead of requesting broad host permissions, consider using the
activeTab permission, or specify the sites that your extension needs
access to. Both options are more secure than allowing full access to
an indeterminate number of sites, and they may help to minimise review
times.
The activeTab permission allows access to a tab in response to an
explicit user gesture.
{
...
"permissions": ["activeTab"]
}
It makes sense for me to use activeTab instead. However, as they mentioned, I can use it in permission not content_scripts or else I would get:
Invalid value for 'content_scripts[0].matches[1]': Missing scheme separator.
How should I go about this?
activeTab won't work on timer fully automatically because it needs an explicit user action: the user must click your extension icon (or interact via a commands API hotkey, a contextMenus API menu, or an omnibox API keyword, more info in the documentation) to start or set the timer.
Through such user action activeTab will grant access to the current tab so your background script can use chrome.tabs.executeScript to inject the scripts (use one call per each script).
And, remove content_scripts in manifest.json.

Intercepting requests and returning a local file using chrome extensions

I want to intercept a request and return a local file as the response using a chrome extension. The user is going to provide the local file path.
Requirements
I want to do this only through the extension (ideally). No additional installations, no chrome apps, other 3rd party apps should be needed. I'm happy to provide whatever permissions are needed in the manifest, but this will ship, so it can't be in dev-mode / i.e. can't have dev-only APIs.
The files that the user can select aren't going to be from the extension package, I think the chrome.runtime.getURL allows reading from the relative URLs within the package, but it didn't work for exact paths for other files.
I'm aware that by setting up a server in either on localhost, or elsewhere and by redirecting to that URL; this could be achieved, but I'm not interested in that; I'm looking for an out of the box solution which will help me pass local files as responses.
Also - I'd be happy if there's a workaround - one that I thought was reading the contents of the file that user selected and streaming those contents from memory / or putting them into a temporary location that the file:// can work with, but couldn't find any way around this yet.
Current state
I'm using the snippet below to handle interception:
// API that I'm using
chrome.webRequest.onBeforeRequest.addListener(function(request){...})
Manifest looks like this:
{
...
"background": {
"scripts": ["background.js"],
"persistent": true
},
"permissions": [
"activeTab",
"tabs",
"http://*/*",
"https://*/*",
"file://*/*",
"webRequest",
"webRequestBlocking"
]
}
I've been able to intercept requests and redirect to other http(s) URLs - this works fine. I'm also receiving the user input for the file path just fine, using <input type="file"/> for this.
Redirecting the request path to be a file doesn't work though; for obvious security reasons. This is the bit that I'm stuck. Being a bit more descriptive below:
chrome.webRequest.onBeforeRequest.addListener(function(request){
// works fine
if (request.url === 'https://someurl') {
return {redirectUrl : 'https://someotherurl'};
}
// doesn't work - and I'm looking for a solution for this scenario
else if (request.url === 'https://someurl2') {
return {redirectUrl: 'file://somefileondisk' };
}
});
and this doesn't:

How to use activeTab to avoid extension publishing delays in the Chrome Web Store

I have a chrome extension built for scraping a few particular pages, and then generating docs with that data on screens built into the extension. It requires regular updates. I keep getting the "Publishing will be delayed warning" below when I go to publish in the Chrome Web Store. The message suggests that I use active tab and narrower host permissions even though my manifest contains the following:
"permissions": ["storage",
"declarativeContent",
"activeTab",
"downloads"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
In the background.js, I have a chrome.declarativeContent.onPageChanged.addRules statement with the following chrome.declarativeContent.PageStateMatcher conditions:
pageUrl: {hostContains: ''}
pageUrl: {hostContains: 'secure.vermont.gov'}
pageUrl: {urlContains: 'chrome-extension://'}
I replaced first one (intended for local files) with codeforbtv.org so there was no wildcard. Nonetheless, I got the same warning in the store.
The only tabs function I use is in the following code:
chrome.tabs.executeScript(null, { file: 'payload.js' });
Payload.js is two lines of code which grabs a large html block and sends it via chrome.runtime.sendMessage.
The relevant codebase can be found here in the extensionDirectory folder: https://github.com/codeforbtv/expunge-vt.
The extension can work on the sample HTML files in the sampleDocketHTML folder.
hostContains: '' matches every URL because '' is present in every string so it's a broad host permission.
To match local files you can probably use schemes: ['file'] but that will be still a broad host permission so I guess you'll have to forget about files.
urlContains: 'chrome-extension://' from the point of view of the web store's automatic detector is also a broad host permission because evidently the script doesn't analyze the pattern so it's considered just a substring match.
An extension can't work on other extension's pages anyway normally so you probably don't need this.
hostContains: 'secure.vermont.gov' is also a broad host permission because this pattern is not anchored to the TLD (top-level domain) so it may occur anywhere and thus match totally irrelevant hosts.
Either use hostSuffix: '.secure.vermont.gov' which will also match the dot-less version and any subdomain or hostEquals.
The warning is permissions based and is a general warning the review will take longer if you use more sensitive permissions for your extension.
Anecdotally I recently used wildcard host permissions (:///*) for a published extension and received the same warning. The review process ended up taking 3 days until it was approved.
You should in general expect longer review times when using sensitive permissions as Google's bandwidth to manually review extensions is currently reduced.

Background script only for specific domain in Chrome extension

I would like to know how to run the background script of a Chrome extension for only specific/specified domain/s please?
For example, if an extension is meant to run only on pages of Google.com, so there is no reason to keep the background script running on any other domains.
In my manifest file I have set "matches" but I can still see the background script running on every domain and tab.
...
"background": {
"matches": [
"*://*.google.com/*"
],
"scripts": ["scripts/background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": [
"*://*.google.com/*"
],
"css": ["styles/default.css"],
"js": ["scripts/jquery.js", "scripts/default.js"]
}
],
"permissions": [
"storage",
"declarativeContent",
"tabs"
],
...
Edit
To add more info that will help understand my goal:
I have a content script and a popup, the popup is being used as a remote control with options to choose from and play with, that will eventually effects the page.
When changing an option in the popup, it sends it's value to the background/event script, there it being temporary saved in a variable, and then being sent to the content script, where it actually being executing and show up on the page to the user.
And I want that only when the user leaves the specific domain, the background/event script will save the settings to storage, so by that there will be only a single storage saving task and NOT each time the user is changing a setting in the popup.
After the settings got saved to storage and the user left, I want nothing to run it the background anymore please.
You are misunderstanding what the background page is for, and what "persistent": false means.
The background page does not run "for" any domain; it just runs. A single copy per extension. Have a look at the Architecture Overview.
However, if you are concerned that it consumes resources, you add "persistent": false to the manifest. This makes it an Event page, that is unloaded when it's not doing work.
If your event page is woken up only by content scripts, then you have achieved your goal: it won't be running when it's not needed.
It's entirely up to you to properly construct the background page so it's idle when you don't need it. Since you haven't told what it's supposed to be doing - well..
Do read the Event page documentation, there are important restrictions you need to understand.

chrome.tabs.executeScript not working?

I am trying to learn to use the chrome.tabs.executeScript commend. I've created a simple extension with a browser action. My background.html file currently looks like this:
<html>
<script>
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null,{code:"document.body.bgColor='red'"});
chrome.tabs.executeScript(null, {file: "content_script.js"});
});
</script>
</html>
The "content_script.js" file contains document.body.bgColor='red'.
When pushing the browser action's button nothing happens. Obviously I'm missing something very basic.
I've checked with console.log that indeed control reaches the chrome.tabs.executeScript calls when the browser action is pressed. Otherwise I'm not sure how to even check if my content script's code is run (it seems not; console.log I put in the content script has no effect, but maybe it shouldn't have one even if the script is run successfully).
Make sure you have domain and tab permissions in the manifest:
"permissions": [
"tabs", "http://*/*", "https://*/*"
]
Then to change body color try:
chrome.tabs.executeScript(null,{code:"document.body.style.backgroundColor='red'"});
Also keep in mind that content scripts are not injected into any chrome:// or extension gallery pages.
For those of you still having issues, you need to make sure to reload the extension's permissions in Chrome.
Go to chrome://extensions , scroll to your extension, and click on "reload". Make sure that your permissions have been updated by clicking on the permissions link right next to your extension.
You actually don't need and don't want the 'tabs' permission for executeScript.
"permissions": [
"http://*/*",
"https://*/*"
]
Should be enough.
It's not recommended to use http://*/* and https://*/*. From the Google documentation:
To inject a programmatic content script, provide the activeTab permission in the manifest. This grants secure access to the active site's host and temporary access to the tabs permission, enabling the content script to run on the current active tab without specifying cross-origin permissions.
Instead, (as suggested in the page) just use activeTab permission.
Remark: more explanation for the security issue
Without activeTab, this extension would need to request full, persistent access to every web site, just so that it could do its work if it happened to be called upon by the user. This is a lot of power to entrust to such a simple extension. And if the extension is ever compromised, the attacker gets access to everything the extension had.
In contrast, an extension with the activeTab permission only obtains access to a tab in response to an explicit user gesture. If the extension is compromised the attacker would need to wait for the user to invoke the extension before obtaining access. And that access only lasts until the tab is navigated or is closed.
(emphasis mine)
In the example code posted by the OP, activeTab is sufficient.
However, if the extension is more complex and needs to work "automatically" (i.e. without the user clicking the button); then this method will not work and additional permission is required.
Most of the answers above seems to be working fine for manifest version 2 but when it comes manifest-3 their seems to be some workaround to make the content-script load in the latest manifest 3.We need to use the following steps to execute content script in manifest 3
First adding permission "scripting" in manifest
"permissions": [
"storage",
"tabs",
"activeTab",
"scripting"
]
Once the scripting perimission is provided, we can use the scripting api like below
In background.js,
chrome.tabs.query({}, (tabList) => {
if (!tabList.length) return;
tabList.forEach((tab) => {
chrome.scripting.executeScript(
{
files: ['contentScript.js'],
target: {
tabId: tab.id,
allFrames: true
}
}
);
});
});
In the above code we are executing the contentScript for all the available tabs in tab browser.

Resources