...
 
Commits (2)
CORS Everywhere
===============
- [Website][1]
- Author: [spenibus][1]
- Repositories
- [GitHub][2]
......@@ -14,6 +14,11 @@ CORS Everywhere
This is a firefox addon that allows the user to enable [CORS][6] everywhere by altering http responses.
Note
----
This addon is now a WebExtension.
Usage
-----
......@@ -30,37 +35,9 @@ Intended for developers. Use at your own risk.
Preferences
-----------
Branch: `extensions.spenibus_corsEverywhere.`
- `enabledAtStartup` (boolean) Enables this addon on startup
Technical note
--------------
This addon uses a JavaScript code module, which basically makes it run like a global, single instance service. In contrast, the standard method of instantiating addons is to load them via an "overlay" which is tied to an individual window. This is important because this addon registers http events in the observer service.
Allow me to quote the documentation, from [Intercepting Page Loads][7]:
>HTTP notifications are fired for all HTTP requests originating from Firefox.
>They are window-independent, so it is better to keep your observer code in non-chrome objects (your XPCOM service or jsm module). Otherwise you have to make sure to avoid duplicated work if you have 2 or more windows open.
As such, this addon runs its core logic in a global context and only uses
overlays for the UI. This avoids 2 issues:
- Redundant observers.
Each window being unaware of the status of other windows, the addon could be
enabled within each of them, each with its own observer, all doing the exact
same thing at the same time. Not critical but still a better deal regarding
cpu and memory.
- Running silently.
Available in about:addons.
This is much more critical. Since each window is only aware of itself,
enabling the addon in one of them would let the user believe that it is only
running in that one window while actually affecting all http requests
regardless of the window in which they run.
- `enabledAtStartup` : Enables this addon on startup
FAQ
......
let spenibus_corsEverywhere = {
/***************************************************************************
props
***/
enabled : false
,transactions : {} // contains requests/responses
/***************************************************************************
init
***/
,init : function() {
// toggle activation on button click
browser.browserAction.onClicked.addListener(function(){
spenibus_corsEverywhere.toggle();
});
// enabled at startup
let prefs = browser.storage.sync.get('enabledAtStartup');
prefs.then((res) => {
if(res.enabledAtStartup) {
spenibus_corsEverywhere.toggle(true);
}
});
// update button
spenibus_corsEverywhere.updateButton();
}
/***************************************************************************
toggle
***/
,toggle : function(state) {
// set state by input
if(typeof state === 'boolean') {
spenibus_corsEverywhere.enabled = state;
}
// set state by toggle
else {
spenibus_corsEverywhere.enabled = !spenibus_corsEverywhere.enabled;
}
// update button
spenibus_corsEverywhere.updateButton();
// clear transactions
spenibus_corsEverywhere.transactions = {};
// add observer, observe http responses
if(spenibus_corsEverywhere.enabled) {
browser.webRequest.onBeforeSendHeaders.addListener(
spenibus_corsEverywhere.requestHandler
,{urls: ["<all_urls>"]}
,["blocking" ,"requestHeaders"]
);
browser.webRequest.onHeadersReceived.addListener(
spenibus_corsEverywhere.responseHandler
,{urls: ["<all_urls>"]}
,["blocking" ,"responseHeaders"]
);
}
// remove observer
else {
browser.webRequest.onBeforeSendHeaders.removeListener(
spenibus_corsEverywhere.requestHandler
);
browser.webRequest.onHeadersReceived.removeListener(
spenibus_corsEverywhere.responseHandler
);
}
}
/***************************************************************************
updateButton
***/
,updateButton : function() {
let buttonStatus = spenibus_corsEverywhere.enabled ? 'on' : 'off';
browser.browserAction.setIcon({path:{48:'media/button-48-'+buttonStatus+'.png'}});
}
/***************************************************************************
requestHandler
***/
,requestHandler : function(request) {
// prepare transaction, store transaction request
let transaction = {
request : request
,requestHeaders : {}
,response : {}
,responseHeaders : {}
};
// shorthand access to request headers
for(let header of request.requestHeaders) {
transaction.requestHeaders[header.name.toLowerCase()] = header;
}
// store transaction
spenibus_corsEverywhere.transactions[request.requestId] = transaction;
}
/***************************************************************************
responseHandler
***/
,responseHandler : function(response) {
// get transaction
let transaction = spenibus_corsEverywhere.transactions[response.requestId];
// store transaction response
transaction.response = response;
// shorthand access to response headers
for(let header of response.responseHeaders) {
transaction.responseHeaders[header.name.toLowerCase()] = header;
}
// create response headers if necessary
for(let name of [
'access-control-allow-origin'
,'access-control-allow-methods'
,'access-control-allow-headers'
,'access-control-allow-credentials'
]) {
// header exists, skip
if(transaction.responseHeaders[name]) {
continue;
}
// create header
let header = {
name : name
,value : "null"
};
// update response
transaction.response.responseHeaders.push(header)
// update shorthand
transaction.responseHeaders[name] = header;
}
// set "access-control-allow-origin", prioritize "origin" else "*"
transaction.responseHeaders['access-control-allow-origin'].value =
transaction.requestHeaders['origin']
&& transaction.requestHeaders['origin'].value !== null
? transaction.requestHeaders['origin'].value
: '*';
// set "access-control-allow-methods"
if(
transaction.requestHeaders['access-control-request-method']
&& transaction.requestHeaders['access-control-request-method'].value !== null
) {
transaction.responseHeaders['access-control-allow-methods'].value =
transaction.requestHeaders['access-control-request-method'].value
}
// set "access-control-allow-headers"
if(
transaction.requestHeaders['access-control-request-headers']
&& transaction.requestHeaders['access-control-request-headers'].value !== null
) {
transaction.responseHeaders['access-control-allow-headers'].value =
transaction.requestHeaders['access-control-request-headers'].value
}
// set "access-control-allow-credentials"
transaction.responseHeaders['access-control-allow-credentials'].value = "true";
// delete transaction
delete spenibus_corsEverywhere.transactions[response.requestId];
// apply modifications
return {
responseHeaders: transaction.response.responseHeaders
,statusCode : 777
};
}
};
/*******************************************************************************
run
***/
spenibus_corsEverywhere.init();
\ No newline at end of file
2015-09-10 12:45 +0000
- updated readme
- updated test page
2015-09-10 09:51 +0000
- restored em:maxVersion, set to 44.0
2015-09-10 09:46 +0000
- removed em:maxVersion
2015-08-10 19:35 +0000
- added cors-everywhere-test.html
2015-08-10 18:53 +0000
- always set "Access-Control-Allow-Credentials" to "true"
2015-08-10 16:57 +0000
- readme: added description and behavior of button
2015-07-07 19:57 +0000
- fixed version
2015-07-07 19:48 +0000
- made response headers more permissive
added Access-Control-Request-Method
added Access-Control-Request-Headers
prompted by: https://github.com/thomaspurchas/cors-everywhere-firefox-addon/commit/6e1d0554121f2115211cfbef85ef186bbfd48245
2015-05-21 22:43 +0000
- updated readme with technical note
2015-05-09 13:06 +0000
- updated readme
2015-04-08 08:48 +0000
- added pref: enabledAtStartup
2015-03-18 23:40 +0000
- fixed wrong date in version and changelog
2015-03-18 21:35 +0000
- updated readme
- fixed changelog entries order
2015-03-13 22:28 +0000
- updated readme
2015-01-17 01:37 +0000
- fixed issue with mismatching headers
2015-01-10 03:34 +0000
- initial release
\ No newline at end of file
content cors-everywhere-spenibus content/
resource cors-everywhere-spenibus content/
style chrome://global/content/customizeToolbar.xul chrome://cors-everywhere-spenibus/content/style.css
overlay chrome://browser/content/browser.xul chrome://cors-everywhere-spenibus/content/overlay.xul
\ No newline at end of file
/*******************************************************************************
Docs
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
https://developer.mozilla.org/en/docs/Debugging_JavaScript#Console.log_in_Browser_Console
*******************************************************************************/
/*********************************************** export javacript code module */
var EXPORTED_SYMBOLS = ["spenibus_corsEverywhere"];
// import console
const { console } = Components.utils.import("resource://gre/modules/devtools/Console.jsm", {});
/******************************************************************************/
var spenibus_corsEverywhere = {
/***************************************************************************/
enabled : false,
observerService : null,
/******************************************************************** init */
init : function() {
// observer service
spenibus_corsEverywhere.observerService =
Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
// prefs service
spenibus_corsEverywhere.prefs =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("extensions.spenibus_corsEverywhere.");
// enabled at startup
// mozilla, you're just addicted to exceptions
try {
if(spenibus_corsEverywhere.prefs.getBoolPref('enabledAtStartup')) {
spenibus_corsEverywhere.toggle(true);
}
} catch(e) {}
},
/****************************************************************** toggle */
toggle : function(state) {
// set state by input
if(typeof state === 'boolean') {
spenibus_corsEverywhere.enabled = state;
}
// set state by toggle
else {
spenibus_corsEverywhere.enabled = !spenibus_corsEverywhere.enabled;
}
// notification topic
var topic = spenibus_corsEverywhere.enabled
? 'spenibus_corsEverywhere_enabled'
: 'spenibus_corsEverywhere_disabled';
// notify
spenibus_corsEverywhere.observerService.notifyObservers(
null,
topic,
null
);
// add observer, observe http responses
if(spenibus_corsEverywhere.enabled) {
spenibus_corsEverywhere.observerService.addObserver(
spenibus_corsEverywhere.observerHandler,
'http-on-examine-response',
false
);
}
// remove observer
else {
spenibus_corsEverywhere.observerService.removeObserver(
spenibus_corsEverywhere.observerHandler,
'http-on-examine-response'
);
}
},
/******************************************************** observer handler */
observerHandler : { observe : function(subject, topic, data) {
// request headers storage
var reqHeaders = {
'Origin' : null,
'Access-Control-Request-Method' : null,
'Access-Control-Request-Headers' : null,
};
/*
// response headers storage
var respHeaders = {
'Access-Control-Allow-Origin' : null,
'Access-Control-Expose-Headers' : null,
'Access-Control-Max-Age' : null,
'Access-Control-Allow-Credentials' : null,
'Access-Control-Allow-Methods' : null,
'Access-Control-Allow-Headers' : null,
};
*/
// http interface
var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);
if(httpChannel === null) {
return;
}
// get request headers
for(var header in reqHeaders) {
// was throwing an exception necessary if header is not set, mozilla ?
try {
reqHeaders[header] = httpChannel.getRequestHeader(header);
} catch(e) {}
}
/*
// get response headers
for(var header in respHeaders) {
// was throwing an exception necessary if header is not set, mozilla ?
try {
respHeaders[header] = httpChannel.getResponseHeader(header);
} catch(e) {}
}
*/
// set "Access-Control-Allow-Origin"
// prioritize "Origin" else "*"
httpChannel.setResponseHeader(
'Access-Control-Allow-Origin',
reqHeaders['Origin'] !== null
? reqHeaders['Origin']
: '*',
false
);
// set "Access-Control-Allow-Methods"
if(reqHeaders['Access-Control-Request-Method'] !== null) {
httpChannel.setResponseHeader(
'Access-Control-Allow-Methods',
reqHeaders['Access-Control-Request-Method'],
false
);
}
// set "Access-Control-Allow-Headers"
if(reqHeaders['Access-Control-Request-Headers'] !== null) {
httpChannel.setResponseHeader(
'Access-Control-Allow-Headers',
reqHeaders['Access-Control-Request-Headers'],
false
);
}
// set "Access-Control-Allow-Credentials"
httpChannel.setResponseHeader(
'Access-Control-Allow-Credentials',
"true",
false
);
}},
};
\ No newline at end of file
#spenibus_cors_everywhere_button_toggle {
background-color:rgba(255,0,0,0.25);
}
#spenibus_cors_everywhere_button_toggle[data-enabled="true"] {
background-color:rgba(0,255,0,0.25);
}
\ No newline at end of file
/********************************************************* import code module */
Components.utils.import("resource://cors-everywhere-spenibus/module.js");
/*********************************************************** init code module */
window.addEventListener("load", spenibus_corsEverywhere.init, false);
window.addEventListener("unload", function() {
window.removeEventListener("load", spenibus_corsEverywhere.init, false);
}, false);
/******************************************* prepare button of current window */
window.addEventListener('load', function(){
// button id
var id = 'spenibus_cors_everywhere_button_toggle';
// get button, try document first
var btn = document.getElementById(id);
// try toolbar palette if document yielded nothing
if(btn == null) {
btn = gNavToolbox.palette.querySelector('#'+id);
}
// no button found, abort
if(btn == null) {
return;
}
// add command to button: toggle
btn.addEventListener("command", spenibus_corsEverywhere.toggle, false);
// observer service
var obs = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
// observe: enable
obs.addObserver(
{observe:function(subject, topic, data){
btn.setAttribute('data-enabled', true);
}},
'spenibus_corsEverywhere_enabled',
false
);
// observe: disable
obs.addObserver(
{observe:function(subject, topic, data){
btn.setAttribute('data-enabled', false);
}},
'spenibus_corsEverywhere_disabled',
false
);
// check if enabled for proper init
if(spenibus_corsEverywhere.enabled) {
btn.setAttribute('data-enabled', true);
}
}, false);
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
<?xml-stylesheet type="text/css" href="chrome://cors-everywhere-spenibus/content/overlay.css"?>
<!DOCTYPE overlay>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript" src="chrome://cors-everywhere-spenibus/content/overlay.js" />
<!-- Firefox -->
<toolbarpalette id="BrowserToolbarPalette">
<!-- button: toggle -->
<toolbarbutton
id="spenibus_cors_everywhere_button_toggle"
tooltiptext="cors everywhere (green: enabled)"
class="toolbarbutton-1 chromeclass-toolbar-additional">
<label value="CorsE"/>
</toolbarbutton>
</toolbarpalette>
</overlay>
\ No newline at end of file
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest"
em:name="cors everywhere"
em:description="enable cors everywhere"
em:creator="spenibus"
em:id="cors-everywhere@spenibus"
em:version="20160122-2027"
em:homepageURL="http://spenibus.net">
<em:targetApplication><!-- Firefox -->
<Description em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
em:minVersion="1.4"
em:maxVersion="46.0" />
</em:targetApplication>
</Description>
</RDF>
\ No newline at end of file
{
"manifest_version" : 2
,"name" : "CORS Everywhere"
,"version" : "20170716-1733"
,"author" : "spenibus"
,"description" : "Bypass CORS restrictions by altering http responses."
,"permissions" : [
"webRequest"
,"webRequestBlocking"
,"storage"
,"<all_urls>"
]
,"background" : {
"scripts" : ["background.js"]
}
,"browser_action" : {
"default_title" : "CorsE"
,"default_icon" : {
"48" : "media/button-48.png"
}
}
,"options_ui" : {
"page" : "options.html"
,"browser_style" : true
}
,"applications": {
"gecko": {
"id": "cors-everywhere@spenibus"
}
}
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<form>
<div>
<label>
<input type="checkbox" id="enabledAtStartup">
Enabled at startup
</label>
</div>
<div>
<button type="submit">Save</button>
</div>
</form>
<script src="options.js"></script>
</body>
</html>
\ No newline at end of file
function saveOptions(e) {
browser.storage.sync.set({
enabledAtStartup : document.querySelector('#enabledAtStartup').checked
});
e.preventDefault();
}
function restoreOptions() {
let prefs = browser.storage.sync.get('enabledAtStartup');
prefs.then((res) => {
document.querySelector('#enabledAtStartup').checked = res.enabledAtStartup || false;
});
}
document.addEventListener('DOMContentLoaded', restoreOptions);
document.querySelector('form').addEventListener('submit', saveOptions);
\ No newline at end of file