Loading external javascript in a Chrome Extension - javascript

I'm trying to load some external javascript code in my Chrome extension. However, due to the sandboxed environment of the extension, I can't see any of the functions defined by the external code.
The external code implements a dependency mechanism, whereby one javascript file may require another, and so forth. It also looks at arguments to the URL used to load the javascript to determine the top level javascript file to load. So it basically is able to load any arbitrary web app, and it is not known in advance all the files that will be used. So I can't use any static definitions in the extension.
There is also the issue that since all extension code is sandboxed, I don't have complete access to the document - for instance, it can't access the window variable.
But if I put all the code in the external code, I run into content security problems if one script tries to load another. The whole reason I went was an extension is because of the bone-headed misimplementation of CSP by every single browser in existence whereby bookmarklets can't access external resources.
What's the best practice for bypassing or working around the extension sandbox to basically be able to run code as if the page itself had loaded it, without any issues with CSP?

In the content script you could do something like this to load the js file
function inject(url, exteral) {
// 1. Build the absolute URL
// 2. Create a script tag and set src attribute
// 3. Append script tag to thw window
if (!external){
url = chrome.extension.getURL(url);
}
var scriptElement = document.createElement('script');
scriptElement.src = url;
(document.body || document.head || document.documentElement).appendChild(scriptElement);
}
If the js file is packged with the extension, it must be placed in the
manifest.json under web_accesible_resources.
othwerwise it need to have the same protocol that the page it is injected in (http | https)
Since the content script is not allowed to call the window functions, you could
call window.postMessage to send data from the actual window to content script.

Related

Is there a way to inject a script from a file before any other scripts?

I am attempting to inject vue into a page from a content script before other scripts run. I have the framework saved as a file in my extension directory and I can inject it with this code:
let s = document.createElement('link');
s.as = 'script';
s.rel = 'preload';
s.href = chrome.runtime.getURL('vue.min.js');
document.documentElement.insertBefore(s, document.documentElement.firstChild);
The content script is run at document_start, so I would expect it to inject this script into the DOM before it is even built and so be the first script to load. This is not true:
It's loaded later. And even though it has the higher priority it still runs after the script even if I set the script to defer and vue is finished loading way before. Why does this happen and is there any way to ensure my script loads/executes first?
Loading a separate script file via a URL puts it into the shared queue so it competes with the page scripts, see https://crbug.com/634381.
The only solution is to use a script element with textContent as a literal string (example: see method 2). And of course the content script should be declared with "run_at": "document_start" in manifest.json.
If your code is big, in order to keep it maintainable you would want to use a separate file naturally so you can configure your webpack (or whatever else) and use a special placeholder in the content script which will be then replaced with a literal string containing the file text.

Execute a page script, which was inserted in the page from a content script, prior to other JavaScript in the page

I am trying to analyze some JavaScript code for which I make use of function rewriting so that calls to a JavaScript library go through my JavaScript code. My JavaScript code is part of a Chrome Extension. From a Chrome extension content script, I install/inject the code into the target page's DOM.
This works fine for functions that are induced after the load of page. The library calls go through my function. But, there's JavaScript code that runs while the page is actually loading (probably while the DOM is being rendered). This happens before my custom script is injected. This way, the function calls before the custom script is injected are lost to me, or those JavaScript calls do not go through my function.
I make use of Content Script to actually inject other JavaScript by appending to the DOM as mentioned in the following Stack Exchange question:
Insert code into the page context using a content script
I know I can cause the loading time of Content Script to be at the start/end of the DOM but this is another script file that I append to the DOM of the target page. I do not seem to understand how to control it.
The problem explained in Is it possible to run a script in context of a webpage, before any of the webpage's scripts run, using a chrome extension?
is exactly the same, but the solution does not seem to work. My intention is to make the injected script execute before any JavaScript code executes from the webpage. By specifying document_start in manifest.json, content script execution can be made to run before the webpage, but not the script that I inject through the content script (injecting script as explained in first link). This injected script is not running in any specific manner with respect to the webpage
Manifest.json:
Manifest file has the content script content.js added at document_start, so content.js is run before the target webpage (underlying page) runs.
"content_scripts":[
{
"matches":["<all_urls>"],
"js":["content.js"],
"run_at":"document_start",
"all_frames":false
}
],
content.js:
content.js has the below code with which I add the main.js to the DOM, so that I am actually able to interact with the JavaScript that is in the target page's environment. I do this from a different file and attach it to the DOM because I cannot interact with the target page's JavaScript through the Content Scripts, since they both do not interfere with each other.
To explain further, main.js has some JavaScript that intercepts JavaScript calls during the execution of JavaScript in target page. JavaScript in target page makes calls to a library and I intend just to write a wrapper on those library functions.
var u = document.createElement('script');
u.src = chrome.extension.getURL('main.js');
(document.head||document.documentElement).appendChild(u);
u.onload = function() {
u.parentNode.removeChild(u);
};
I expect that main.js is available in the target page's domain and any of the scripts in the target page, since I inject it through the content script that is run at document_start.
Assume I have a call to some JavaScript function like this in my target page HTML, someJSCall() is defined by the target page's domain.
<html onLoad="someJSCall( )">
In this scenario, main.js (code injected through my Chrome extension) is already available. So calls to the JavaScript library from someJSCall() function go through main.js wrapper functions.
This works fine.
The problem is when there are IIFE (immediately invoked function expressions) defined in the target page's JavaScript. If these IIFE calls make library calls, this does not go through my main.js interceptions. If I look at the files loaded in the browser through Chrome Dev Tools, I see that main.js is still not loaded while IIFE calls are executing.
I hope I have explained the problem in detail.
Based on the additional information you added to the question about 2.5 weeks after I answered, you are adding code to the page context by including a "main.js", which is a separate file in your extension, using a <script> that looks something like:
<script src="URL_to_file_in_extension/main.js"/>
However, when you do that you introduce an asynchronous delay between when the <script> is inserted into the page and when the "main.js" is fetched from the extension and executed in the page context. You will not be able to control how long this delay is and it may, or may not, result in your code running prior to any particular code in the page. It will probably run prior to code that has to be fetched from external URLs, but may not.
In order to guarantee that your code runs synchronously, you must insert it in a <script> tag as actual code, not using the src attribute to pull in another file. That means the code which you want to execute in the page must exist within the content script file you are loading into the page.
Needing to execute code in the page context is a fairly common requirement. I've needed to do so in browser extensions (e.g. Chrome, Firefox, Edge, etc.) and in userscripts. I've also wanted to be able to pass data to such code, so I wrote a function called executeInPage(), which will take a function defined in the current context, convert it to text, insert it into the page context and execute it while passing any arguments you have for it (of most types). If interested, you can find executeInPage() in my answer to Calling webpage JavaScript methods from browser extension and my answer to How to use cloneInto in a Firefox web extension?
The following is my original answer based on the original version of the question, which did not show when the content script was being executed, or explain that the code being added to the page was in a separate file, not in the actual content script.
You state in your question that you "can handle the loading time of Content Script to be at the start/end of the DOM", but you don't make clear why you are unable to resolve your issue by executing your content script at document_start.
You can have your script injected prior to the page you are injecting into being built by specifying document_start for the run_at property in your manifest.json content_scripts entry, or for the runAt option passed to chrome.tabs.executeScript(). If you do this, then your script will start running when document.head and document.body are both null. You can then control what gets added to the page.
For chrome.tabs.executeScript() exactly when your script runs depends on when you execute chrome.tabs.executeScript() in relation to the process of loading the page. Due to the asynchronous nature of the processing (your background script is usually running in a different process), it is difficult to get your script consistently injected when document.head and document.body are both null. The best I've accomplished is to have the script injected sometimes when that is the case, and sometimes after the page is populated, but prior to any other resources being fetched. This timing will work for most things, but if you really need to have your script run prior to the page existing, then you should use a manifest.json content_scripts entry.
With your content script running prior to the existence of the head and body, you can control what gets inserted first. Thus, you can insert your <script> prior to anything else on the page. This should make your script execute prior to any other script in the page context.

Can you write JS (inline or external) that can interact with functions in a Chrome content script?

I'm writing a script loader Chrome extension that will augment the console to allow users to load in scripts from external urls. My core function works something like this:
__('jquery', 'underscore.js', 'backbone.js', function() {
$('body').append($('<p>').html('I can use jQuery now!'));
});
What I'm wondering is basically what order Chrome content scripts get loaded into the document and if they can be accessed by external scripts? Would it be possible, for example, to call the same code above from the document itself, rather than from the console, or will the script be executed prior to the insertion and execution of my Chrome extension's content script?

IE extension - Injecting Javascript file

I am developing an IE extension which works on sites opened in Internet Explorer. It is designed to work the same way as a chrome extension. I am trying to implement the Background function of chrome extension using c++ and the content script by injecting JS into the current web page. The content script, I am trying to load via IHTMLWindow2 execScript on Document load event. Now that I need to inject JS files directly I tried the following.
Had the JS file under a folder inside the Project destination and tried to inject using physical path.
std::wstring filePath(_T("d:/xx/xxx/x/x/Content/myFile.js"));
scriptText = scriptText+ filePath + endScript;
VARIANT vrt = {0};
HRESULT hrexec = ifWnd->execScript(SysAllocString(scriptText.c_str()),L"javascript", &vrt);
The scriptText has some javascript code to create script element with type and src attributes. The filePath holds the physical path towards the js file.[Also tried relative path but it was a no go]
The above was not working correctly in IE9 due to mixed content issue, upon which I researched to figure out that IE9 expects the js file to be retrieved from a server rather than local physical path. The console throws me the below exception.
SEC7111: HTTPS security is compromised by file:<filepath>
SCRIPT16388: Operation aborted
I am pretty much not sure is there any round about for injecting Javascript to the current DOM from the physical path. Please help me on this.
Also let me know is there any other possibility of injecting the JS file from the current working directory into the DOM.
You don't have to inject a <SCRIPT> tag in the DOM.
If your js file contains:
var strHello = "Hello";
function SayHello() { alert( strHello ); }
you may just read the file into memory, construct a BSTR string with it, and pass that string to IHTMLWindow2::execScript.
Later, another call to execScript with the string SayHello(); will popup the alert box. The code you injected is still here.

Appending a block of javascript to the DOM fails in production - but not in dev

I have an ASP.NET website. Very huge!
The latest addition I've made, everything is JavaScript/AJAX enabled.
I send HTML and JavaScript code back from the server to the client, and the client will inject the HTML into a DIV - and inject the JavaScript into the DOM using this:
$('<script type="text/javascript">' + script + '</sc' + 'ript>').appendTo(document);
or this:
var js = document.createElement("script");
js.setAttribute("type", "text/javascript");
js.text = script;
document.appendChild(js);
On my own dev machine, the injected javascript is accessible and I'm able to execute injected JavaScript functions.
When I deploy to my test environment, )we have an internal domain name such as www.testenv.com) I get JavaScript errors.
I've tried to isolate the problem into a small page, where I inject alert("sfdfdf"); at the bottom of the page, and that works fine.
Is there any policy settings that prohibits this?
Don't appendChild to 'document'; <script> can't go there and you should get a HIERARCHY_REQUEST_ERR DOMException according to the DOM Level 1 Core spec. The only element that can go in the Document object's child list is the single root documentElement, <html>.
Instead append the script to an element inside the document, such as the <body>.
Dynamically creating elements should work fine, the script will be executed upon insertion into the DOM. To answer your question specifically, there are no direct policy settings that prohibit script injection, however, if you're using ajax calls within the dynamically inserted script you could run into Cross-Domain restrictions.
If you could post the error, and maybe the source of the 'script' element you're inserting it would help to debug the problem :)

Categories

Resources