Chrome extension - Write to local storage of a different domain - javascript

How can I write to a local storage of a different domain. The idea is that I need my chrome extension to write something in the local storage and when the user visits the associated website, site site can read the contents of the local storage. I am trying to do a sync between the user's personal data without having to store them in the server.

Content scripts have direct acccess to a page's localStorage.
If you want to store a value for a specific domain, just open a window or frame, then write to the window/page's local storage.
Pick your favorite option to activate the page:
chrome.tabs.create to create an inactive tab, perhaps in the non-active window found by using chrome.tabs.query.
The experimental offscreenTabs API (experimental, so not ready for use in production). See some of the examples I posted on SO.
Create an IFrame and insert it in the current page.
window.open (not recommended...)
When you decide to open a page, pick one which is light-weight. /robots.txt is usually a good choice: It exists on most sites, and it is a plain text file.
Activate the content script for the page. You can either use the manifest file and make use of the messaging APIs, or add a callback to the chrome.tabs.create method which programatically inserts a content script (chrome.tabs.executeScript).
Here's a simple example. It requires the tabs API because I'm using chrome.tabs.executeScript. Furthermore, the origin you want to access should also be added to the permissions list.
chrome.tabs.create({
active: false,
url: 'http://stackoveflow.com/robots.txt'
}, function(tab) {
chrome.tabs.executeScript(tab.id, {
code: 'localStorage.setItem("key", "value");'
}, function() {
chrome.tabs.remove(tab.id);
});
});
Update (2022)
Since Manifest version 3 the chrome.tabs.executeScript API was replaced with the chrome.scripting.executeScript API, hence the code above can be changed to be like this:
function saveToLocalStorage() {
localStorage.setItem("key", "value");
}
chrome.tabs.create({
active: false,
url: 'http://stackoveflow.com/robots.txt'
}, function(tab) {
chrome.scripting.executeScript({
target: {tabId: tab.id},
func: saveToLocalStorage,
}, function() {
chrome.tabs.remove(tab.id);
});
});
And please note, you need to add the "scripting" permission to your manifest.json file in order to use this API.

I think the only solution to do this is via script injection due to cross domain restrictions.
var s = document.createElement('script');
s.src = chrome.extension.getURL("script.js");
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
Add a file script.js to your extension accessing the other sites localStorage, you have to add script.js to "web_accessible_resources" in your manifest file.
Note that the script is automatically removed after the script was completely loaded, so make sure that the code you want to execute is contained in a self executable function.

Related

Update chrome extension manifest permissions from options page

I am trying to update the permissions in the chrome extension manifest file from the options page.
Basically the user should be able to input the url for the extension to run on and that url will update in the extensions manifest file.
I am currently storing all my options using chrome.storage.sync for use in multiple files.
I am looking for a secure solution to give only the chosen url access. Any suggestions?
That's not possible (to update the manifest).
However, the particular use case you explain is:
Have a list of "allowed" websites.
When the extension is invoked by pressing the button, if the website is allowed - inject a content script that does something.
In this case "activeTab" permission and Programmatic Injection should solve your problem.
You do not declare any content scripts in the manifest. This ensures code runs only when you want to.
You do not declare any host permissions in the manifest. Your extension will ONLY work on the current tab at the moment of Browser Action press.
You ask the user for allowed URLs and store them in chrome.storage.
When your extension is invoked using the Browser Action, you can query the currently active tab and you will get its URL (because of the "activeTab" permission).
Your logic then compares it to stored whitelisted URLs.
If there's a match - you use chrome.tabs.executeScript to inject your content script.
If there's no match - do nothing, or somehow ask user to confirm whitelisting the new domain.
Here's some sample code:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var currentTab = tabs[0];
// Pseudocode
if (whitelisted(currentTab.url)) {
chrome.tabs.executeScript(currentTab.id, {file: "content.js"});
} else {
// Do nothing or show some warning
}
});
Alternatively, you can look at Optional Permissions API.

Getting the HTML page from my website and inject it inside my chrome extension

i want to get the html page from link of my website and inject it like html again inside my popup.html page in google chrome extension
i need the code to be by using JavaScript and i don't want any jquery because
i did part of it before with jquery with this
$.get('some website ', function(data) {
$('.result').html(data);
alert('Load was performed.');
});
but i want JavaScript code to get the html from link which i want to define it inside the code
for example i have website like www.something.com/page.html
and i need to take the sourse code of page.html and inject it inside popup.html
that mean the extension will show me page.html
i hope that wanna be good explanation
This should do the trick, but be aware of the security implications of injecting a bunch of HTML into another document -- it's an easy way to cause XSS:
var element = ... // some element in your popup.html document
var request = new XMLHttpRequest();
request.onload = function() {
element.innerHTML = this.responseText;
};
request.open("get", "http://www.some.com/page.html", true);
request.send();
You'll also need to add a permission to your manifest to allow your extension to do cross-site XHRs to "http://www.some.com". Assuming no other permissions:
"permissions": [
"http://www.somecom/"
],
From a security POV, it makes more sense to just ask some.com for JSON data, parse that through JSON.parse() and then build whatever UI you need in the Chrome extension. Makes your extension also more stable/flexible because all UI is contained in the Chrome extension, not spread across extension and some.com.
Alternatively, if page.html is sufficiently static, bundle it into your extension and inject it with a code snippet similar to the one above, but use chrome.extension.getUrl('page.html') as the URL for request.open().
Regardless of whether your page.html comes from another site or from within the extension, you might need to fix relative URLs of images, scripts, style sheets (Chrome won't do this for you here), e.g. by prefixing their src/href attributes with http://www.some.com/ to point them to the intended resources again.

How to make contentScript run before contentScriptFile?

I am developing a FF addon recently. My addon will download some configuration files before the contentScriptFile been evaluated. My code is like this:
panel.on("show", function() {
var url = "http://domain/config.js";
request({
url:url,
onComplete: function(response) {
worker = tabs.activeTab.attach({
contentScript:response.text,
contentScriptFile: [data.url("js/do1.js"), data.url("js/do2.js")]
});
...
}
})
}
But as your document has said that contentScriptFile will be run before contentScript, so if I do want to run contentScript first, how should I do?
I have tried to attach twice, first attach contentScript only, then attach contentScriptFile, but not success.
Thank you for your time.
You must not execute remote scripts in a privileged context, or execute insecure remote scripts in a potentially secure context. What you're asking fails both:
Executing remote scripts in a somewhat privileged context, namely a content script, which has some privileged capabilities regular web site scripts have not.
Execute an insecure http:// script (you gave that in the example), in the current tab, which might be https://. This would allow Man-in-the-Middle attacks. (Even if you served your "config" from https, the add-on site would still reject your submission, BTW, because it cannot be reviewed as the active content is not complete).
So, effectively, you got two security vulnerabilities, both of which can and should make your add-on end up on the blocklist.
However, you can retrieve your configuration using a non-active/non-executable format, such as JSON or XML (or plain text, if you like).
Passing these configuration values can be achieved by
using either contentScriptOptions (which is not documented in tabs nor worker, but in page-mod but should work for all, as a cursory test shows). Full example:
var tabs = require("sdk/tabs");
tabs.on('activate', function(tab) {
tab.attach({
contentScript: 'console.log(self.options);',
contentScriptOptions: {"abc": 123}
});
});
Or regular messaging.
You could use data.load from the self SDK module to load the content script in a string and append the values to the contentScript array:
worker = tabs.activeTab.attach({
contentScript: [
response.text,
data.load("js/do1.js"),
data.load("js/do2.js")
]
});

Scriptish / Greasemonkey GM_setValue and GM_getValue equivalents for Google Chrome Extensions

I'm developing a Google Chrome Extension which basically has two different content scripts in it for two different domains (let's say google.com and yahoo.com):
manifest.json
...
"content_scripts" : [{
"matches" : ["https://*google.com/*"],
"js": ["my_google.js"],
"run_at":"document_end"
}, {
"matches" : ["https://*yahoo.com/*"],
"js": ["my_yahoo.js"],
"run_at":"document_end"
}],
"permissions": ["storage"],
...
I need a permanent (not just for one session) storage where both these scripts could update and retrieve some commonly shared data. I know, this can be done with chrome.storage API like this:
chrome.storage.local.set( { 'string1' : 'value1'}, function(){
console.log("string1 with value = value1 has been added to the chrome.storage");
// continue with script code...
});
chrome.storage.local.get( 'string1', function (data){
console.log("string1 with value = " + data['string1'] + " has been retrieved from the chrome.storage" );
// continue with script code...
});
chrome.storage.local.remove( 'string1', function () {
console.log("string1 has been removed from chrome.storage") ;
// continue with script code...
});
Now, comes the funny part...
Is there any replacement for this API which would work like cross-domain chrome.storage but with synchronous calls to it? What I'm actually trying to achieve is to reproduce the behavior of Scriptish/Greasemonkey GM_setValue and GM_getValue APIs without need to change the whole code of both scripts previously used in Mozilla Firefox and now transferred to Google Chrome.
optionally:
also, it would be nice to be able to remove string1 (or probably clear all storage) from browser action icon pop-up page - popup.html (popup.js) .
any ideas?
There is no synchronous cross-domain storage mechanism for content scripts.
However, if you really want to get such a feature, then you could implement such a storage yourself, by maintaining a local copy of the storage backed by chrome.storage.
Initialization: Use chrome.storage.local.get(null, callback) to fetch all stored data and store it in a local variable.
Maintenance:
Use the chrome.storage.onChanged event to watch for changes and update the local copy when needed.
Whenever GM_setValue (etc.) is used, immediately update the local copy, so that calling GM_getValue immediately after GM_setValue gives the expected result.
Ideally, you should defer the execution of the other scripts until the storage is initialized. If this is not possible, create a separate script, and let it "run_at": "document_start". Using this method, it's likely (although not guaranteed) that the storage is ready when the rest of your code runs at "document_end"`.
There is another way to synchronously get/set storage values. I strongly, strongly discourage you from using this method. Synchronous XMLHttpRequest together with the webRequest API (chrome.webRequest.onBeforeRequest) can be used to communicate data from the background page to the content script, and vice versa.
Sending data is trivial (e.g. set a value in the URL),
getting data back is a bit more difficult. You can use URL.createObjectURL to get a blob: URL of your data, and return {redirectUrl: ...} to reply the just-created URL in the content script.

Chrome extension to modify page's script includes and JS

I work on a javascript library that customers include on their site to embed a UI widget. I want a way to test dev versions of the library live on the customer's site without requiring them to make any changes to their code. This would make it easy to debug issues and test new versions.
To do this I need to change the script include to point to my dev server, and then override the load() method that's called in the page to add an extra parameter to tell it what server to point to when making remote calls.
It looks like I can add JS to the page using a chrome extension, but I don't see any way to modify the page before it's loaded. Is there something I'm missing, or are chrome extensions not allowed to do this kind of thing?
I've done a fair amount of Chrome extension development, and I don't think there's any way to edit a page source before it's rendered by the browser. The two closest options are:
Content scripts allow you to toss in extra JavaScript and CSS files. You might be able to use these scripts to rewrite existing script tags in the page, but I'm not sure it would work out, since any script tags visible to your script through the DOM are already loaded or are being loaded.
WebRequest allows you to hijack HTTP requests, so you could have an extension reroute a request for library.js to library_dev.js.
Assuming your site is www.mysite.com and you keep your scripts in the /js directory:
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if( details.url == "http://www.mysite.com/js/library.js" )
return {redirectUrl: "http://www.mysite.com/js/library_dev.js" };
},
{urls: ["*://www.mysite.com/*.js"]},
["blocking"]);
The HTML source will look the same, but the document pulled in by <script src="library.js"></script> will now be a different file. This should achieve what you want.
Here's a way to modify content before it is loaded on the page using the WebRequest API. This requires the content to be loaded into a string variable before the onBeforeRequest listener returns. This example is for javascript, but it should work equally well for other types of content.
chrome.webRequest.onBeforeRequest.addListener(
function (details) {
var javascriptCode = loadSynchronously(details.url);
// modify javascriptCode here
return { redirectUrl: "data:text/javascript,"
+ encodeURIComponent(javascriptCode) };
},
{ urls: ["*://*.example.com/*.js"] },
["blocking"]);
loadSynchronously() can be implemented with a regular XMLHttpRequest. Synchronous loading will block the event loop and is deprecated in XMLHttpRequest, but it is unfortunately hard to avoid with this solution.
You might be interested in the hooks available in the Opera browser. Opera used to have* very powerful hooks, available both to User JavaScript files (single-file things, very easy to write and deploy) and Extensions. Some of these are:
BeforeExternalScript:
This event is fired when a script element with a src attribute is encountered. You may examine the element, including its src attribute, change it, add more specific event listeners to it, or cancel its loading altogether.
One nice trick is to cancel its loading, load the external script in an AJAX call, perform text replacement on it, and then re-inject it into the webpage as a script tag, or using eval.
window.opera.defineMagicVariable:
This method can be used by User JavaScripts to override global variables defined by regular scripts. Any reference to the global name being overridden will call the provided getter and setter functions.
window.opera.defineMagicFunction:
This method can be used by User JavaScripts to override global functions defined by regular scripts. Any invocation of the global name being overridden will call the provided implementation.
*: Opera recently switched over to the Webkit engine, and it seems they have removed some of these hooks. You can still find Opera 12 for download on their website, though.
I had an idea, but I didn't try it, but it worked in theory.
Run content_script that was executed before the document was loaded, and register a ServiceWorker to replace page's requested file content in real time. (ServiceWorker can intercept all requests in the page, including those initiated directly through the dom)
Chrome extension (manifest v3) allow us to add rules for declarativeNetRequest:
chrome.declarativeNetRequest.updateDynamicRules({
addRules: [
{
"id": 1002,
"priority": 1,
"action": {
"type": "redirect",
"redirect": {
"url": "https://example.com/script.js"
}
},
"condition": {
"urlFilter": 'https://www.replaceme.com/js/some_script_to_replace.js',
"resourceTypes": [
'csp_report',
'font',
'image',
'main_frame',
'media',
'object',
'other',
'ping',
'script',
'stylesheet',
'sub_frame',
'webbundle',
'websocket',
'webtransport',
'xmlhttprequest'
]
}
},
],
removeRuleIds: [1002]
});
and debug it by adding listener:
chrome.declarativeNetRequest.onRuleMatchedDebug.addListener(
c => console.log('onRuleMatchedDebug', c)
)
It's not a Chrome extension, but Fiddler can change the script to point to your development server (see this answer for setup instructions from the author of Fiddler). Also, with Fiddler you can setup a search and replace to add that extra parameter that you need.

Categories

Resources