Modifying Javascript
Javascript can be parsed with acorn
or esprima.
The result is an AST in the ESTree (also known as Mozilla Parser API) format.
You can then make modifications to the AST. estraverse and other estools projects may be helpful.
Some guidelines about making automatic modifications to third-party Javascript:
- You do not want thrown Errors in your code, propagating up the stack and affecting other code. In addition, you will almost always want to be notified about such Errors. Therefore, you should surround all injected code with a try-catch block.
- If you declare variables in your injected code, you do not want them interfering with other code (as can occur through
var hoisting). Therefore, you should wrap all injected code in an IIFE.
- Try to avoid modifying anything or causing any side-effects in your injected code. This will make it more difficult for your modifications to be detected.
Once you are done, transform the AST back into Javascript code with escodegen.
Designing the proxy
Since apparently you cannot modify the Javascript at runtime, you must modify it before runtime – before it has been loaded. Therefore, you will need a man-in-the-middle HTTP proxy.
As you will be using Javascript libraries, such as acorn and escodegen, the proxy must also be written in Javascript. More specifically, Node.js
The browser will request the full URL from the proxy. For example, if there is a request to http://www.google.com, Chrome will send something like this to the proxy:
GET http://www.google.com HTTP/1.1
Host: www.google.com
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
When the proxy receives a request, it should make the same request to the origin server, proxying along most of the headers that it received. It should remove the Host, Remote-Addr, Http-Client-IP, and Proxy-Connection headers sent by the browser, as those are intended for the proxy. It should also send Accept-Encoding: identity to the origin server, to disable HTTP compression, which is annoying to deal with.
After the proxy has downloaded the original resource, it may modify it in any way, before sending it back to the browser.
Configuring Chrome to use the proxy
For obvious reasons, you do not want to permanently mess with system proxy settings or browser proxy settings. You do not want all of your network traffic funneled through your own handwritten, probably hackish, proxy.
The Chrome Extension API chrome.proxy will thus be helpful to you, for the following reasons:
- The proxy will only affect Chrome, and not other applications.
- You can use the bypass list to ensure only certain traffic goes through the proxy.
- You can enable or disable the proxy at any time.
To use this API, you must create a custom Chrome extension.
A better way: creating a dedicated app
The current solution includes three components:
- Chrome, the browser.
- The proxy, a Node.js application that modifies Javascript.
- The proxy configurator, a Chrome extension that tells Chrome to use the proxy.
If you anticipate that all of this will become very complex, you can create a dedicated app for this, using NW.js. NW.js is very versatile, and you can essentially use it as programmable Chrome.
Benefits of this approach:
- NW.js disables the cross-origin policy for trusted code. Thus, you can create a cross-origin
iframe (don't forget to add the nwdisable and nwfaketop attributes), and programmatically interact with it through jQuery.
- NW.js supports all Node.js APIs. You can make the proxy an integrated part of your app. You will no longer need to worry about manually launching and killing it, as you would need to do if the proxy was an external Node.js application.
- NW.js supports all Chrome Extension APIs, including
chrome.proxy. You can make the proxy configurator an integrated part of your app.