Skip to content
This repository has been archived by the owner on Nov 6, 2023. It is now read-only.

Commit

Permalink
Merge branch 'master' of https://git.torproject.org/https-everywhere
Browse files Browse the repository at this point in the history
…into better-obs-whitelisting
  • Loading branch information
pde committed Apr 25, 2014
2 parents 2878602 + 39146f3 commit c669354
Show file tree
Hide file tree
Showing 61 changed files with 1,120 additions and 468 deletions.
171 changes: 78 additions & 93 deletions chromium/background.js
@@ -1,3 +1,4 @@
var USER_RULE_KEY = 'userRules';
// Records which tabId's are active in the HTTPS Switch Planner (see
// devtools-panel.js).
var switchPlannerEnabledFor = {};
Expand All @@ -7,9 +8,26 @@ var switchPlannerEnabledFor = {};
// rw / nrw stand for "rewritten" versus "not rewritten"
var switchPlannerInfo = {};

var getStoredUserRules = function() {
var oldUserRuleString = localStorage.getItem(USER_RULE_KEY);
var oldUserRules = [];
if (oldUserRuleString) {
oldUserRules = JSON.parse(oldUserRuleString);
}
return oldUserRules;
};
var all_rules = new RuleSets();
var wr = chrome.webRequest;
var loadStoredUserRules = function() {
var rules = getStoredUserRules();
var i;
for (i = 0; i < rules.length; ++i) {
all_rules.addUserRule(rules[i]);
}
log('INFO', 'loaded ' + i + ' stored user rules');
};

loadStoredUserRules();
/*
for (var v in localStorage) {
log(DBUG, "localStorage["+v+"]: "+localStorage[v]);
Expand All @@ -22,20 +40,23 @@ for (r in rs) {
}
*/

// Add the HTTPS Everywhere icon to the URL address bar.
// TODO: Switch from pageAction to browserAction?
function displayPageAction(tabId) {
if (tabId !== -1) {
chrome.tabs.get(tabId, function(tab) {
if(typeof(tab) === "undefined") {
log(DBUG, "Not a real tab. Skipping showing pageAction.");
}
else {
chrome.pageAction.show(tabId);
}
});

var addNewRule = function(params, cb) {
if (all_rules.addUserRule(params)) {
// If we successfully added the user rule, save it in local
// storage so it's automatically applied when the extension is
// reloaded.
var oldUserRules = getStoredUserRules();
// TODO: there's a race condition here, if this code is ever executed from multiple
// client windows in different event loops.
oldUserRules.push(params);
// TODO: can we exceed the max size for storage?
localStorage.setItem(USER_RULE_KEY, JSON.stringify(oldUserRules));
cb(true);
} else {
cb(false);
}
}
};

function AppliedRulesets() {
this.active_tab_rules = {};
Expand Down Expand Up @@ -115,21 +136,13 @@ function onBeforeRequest(details) {
// If no rulesets could apply, let's get out of here!
if (rs.length === 0) { return; }

if (details.requestId in redirectCounter) {
redirectCounter[details.requestId] += 1;
log(DBUG, "Got redirect id "+details.requestId+
": "+redirectCounter[details.requestId]);

if (redirectCounter[details.requestId] > 9) {
log(NOTE, "Redirect counter hit for "+canonical_url);
urlBlacklist[canonical_url] = true;
var hostname = uri.hostname();
domainBlacklist[hostname] = true;
log(WARN, "Domain blacklisted " + hostname);
return;
}
} else {
redirectCounter[details.requestId] = 0;
if (redirectCounter[details.requestId] >= 8) {
log(NOTE, "Redirect counter hit for " + canonical_url);
urlBlacklist[canonical_url] = true;
var hostname = uri.hostname();
domainBlacklist[hostname] = true;
log(WARN, "Domain blacklisted " + hostname);
return null;
}

var newuristr = null;
Expand Down Expand Up @@ -321,103 +334,75 @@ function switchPlannerDetailsHtmlSection(tab_id, rewritten) {
function onCookieChanged(changeInfo) {
if (!changeInfo.removed && !changeInfo.cookie.secure) {
if (all_rules.shouldSecureCookie(changeInfo.cookie, false)) {
var cookie = {name:changeInfo.cookie.name,value:changeInfo.cookie.value,
domain:changeInfo.cookie.domain,path:changeInfo.cookie.path,
var cookie = {name:changeInfo.cookie.name,
value:changeInfo.cookie.value,
path:changeInfo.cookie.path,
httpOnly:changeInfo.cookie.httpOnly,
expirationDate:changeInfo.cookie.expirationDate,
storeId:changeInfo.cookie.storeId};
cookie.secure = true;
// FIXME: What is with this url noise? are we just supposed to lie?
if (cookie.domain[0] == ".") {
cookie.url = "https://www"+cookie.domain+cookie.path;
storeId:changeInfo.cookie.storeId,
secure: true};

// Host-only cookies don't set the domain field.
if (!changeInfo.cookie.hostOnly) {
cookie.domain = changeInfo.cookie.domain;
}

// The cookie API is magical -- we must recreate the URL from the domain and path.
if (changeInfo.cookie.domain[0] == ".") {
cookie.url = "https://www" + changeInfo.cookie.domain + cookie.path;
} else {
cookie.url = "https://"+cookie.domain+cookie.path;
cookie.url = "https://" + changeInfo.cookie.domain + cookie.path;
}
// We get repeated events for some cookies because sites change their
// value repeatedly and remove the "secure" flag.
log(DBUG,
"Securing cookie "+cookie.name+" for "+cookie.domain+", was secure="+changeInfo.cookie.secure);
"Securing cookie " + cookie.name + " for " + changeInfo.cookie.domain + ", was secure=" + changeInfo.cookie.secure);
chrome.cookies.set(cookie);
}
}
}

// This event is needed due to the potential race between cookie permissions
// update and cookie transmission (because the cookie API is non-blocking).
// Without this function, an aggressive attacker could race to steal a not-yet-secured
// cookie if they controlled & could redirect the user to a non-SSL subdomain.
// WARNING: This is a very hot function.
function onBeforeSendHeaders(details) {
// TODO: Verify this with wireshark
for (var h in details.requestHeaders) {
if (details.requestHeaders[h].name == "Cookie") {
// Per RFC 6265, Chrome sends only ONE cookie header, period.
var uri = new URI(details.url);
var host = uri.hostname();

var newCookies = [];
var cookies = details.requestHeaders[h].value.split(";");

for (var c in cookies) {
// Create a fake "nsICookie2"-ish object to pass in to our rule API:
var fake = {domain:host, name:cookies[c].split("=")[0]};
// XXX I have no idea whether the knownHttp parameter should be true
// or false here. We're supposedly inside a race condition or
// something, right?
var ruleset = all_rules.shouldSecureCookie(fake, false);
if (ruleset) {
activeRulesets.addRulesetToTab(details.tabId, ruleset);
log(INFO, "Woah, we lost the race on updating a cookie: "+details.requestHeaders[h].value);
function onBeforeRedirect(details) {
// Catch HTTPs -> HTTP redirect loops, ignoring about:blank, HTTPS 302s, etc.
if (details.redirectUrl.substring(0, 7) === "http://") {
if (details.requestId in redirectCounter) {
redirectCounter[details.requestId] += 1;
log(DBUG, "Got redirect id "+details.requestId+
": "+redirectCounter[details.requestId]);
} else {
newCookies.push(cookies[c]);
redirectCounter[details.requestId] = 1;
}
}
details.requestHeaders[h].value = newCookies.join(";");
log(DBUG, "Got new cookie header: "+details.requestHeaders[h].value);

// We've seen the one cookie header, so let's get out of here!
break;
}
}

return {requestHeaders:details.requestHeaders};
}

function onResponseStarted(details) {

// redirect counter workaround
// TODO: Remove this code if they ever give us a real counter
if (details.requestId in redirectCounter) {
delete redirectCounter[details.requestId];
}
}

wr.onBeforeRequest.addListener(onBeforeRequest, {urls: ["https://*/*", "http://*/*"]}, ["blocking"]);

// This watches cookies sent via HTTP.
// We do *not* watch HTTPS cookies -- they're already being sent over HTTPS -- yay!
wr.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ["http://*/*"]},
["requestHeaders", "blocking"]);

wr.onResponseStarted.addListener(onResponseStarted,
{urls: ["https://*/*", "http://*/*"]});
// Try to catch redirect loops on URLs we've redirected to HTTPS.
wr.onBeforeRedirect.addListener(onBeforeRedirect, {urls: ["https://*/*"]});


// Add the small HTTPS Everywhere icon in the address bar.
// Note: We can't use any other hook (onCreated, onActivated, etc.) because Chrome resets the
// pageActions on URL change. We should strongly consider switching from pageAction to browserAction.
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
displayPageAction(tabId);
if (changeInfo.status === "loading") {
chrome.pageAction.show(tabId);
}
});

// Pre-rendered tabs / instant experiments sometimes skip onUpdated.
// See http://crbug.com/109557
chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) {
displayPageAction(addedTabId);
chrome.tabs.get(addedTabId, function(tab) {
if(typeof(tab) === "undefined") {
log(DBUG, "Not a real tab. Skipping showing pageAction.");
} else {
chrome.pageAction.show(addedTabId);
}
});
});

// Listen for cookies set/updated and secure them if applicable. This function is async/nonblocking,
// so we also use onBeforeSendHeaders to prevent a small window where cookies could be stolen.
// Listen for cookies set/updated and secure them if applicable. This function is async/nonblocking.
chrome.cookies.onChanged.addListener(onCookieChanged);

function disableSwitchPlannerFor(tabId) {
Expand Down
5 changes: 3 additions & 2 deletions chromium/manifest.json
Expand Up @@ -21,6 +21,7 @@
"16": "icon16.png",
"48": "icon48.png"
},
"incognito": "split",
"manifest_version": 2,
"minimum_chrome_version": "18",
"name": "__MSG_about_ext_name__",
Expand All @@ -37,5 +38,5 @@
"<all_urls>"
],
"update_url": "https://www.eff.org/files/https-everywhere-chrome-updates.xml",
"version": "2014.1.17"
}
"version": "2014.4.16"
}
27 changes: 26 additions & 1 deletion chromium/popup.html
Expand Up @@ -5,13 +5,38 @@
<link href="chrome-resources/css/chrome_shared.css" rel="stylesheet" type="text/css"/>

<link href="popup.css" rel="stylesheet" type="text/css"/>
<script src="jquery.min.js"></script>
<script src="URI.js"></script>
<script src="popup.js"></script>

</head>

<body>
<header>
<h1 i18n="about_ext_name"></h1>
</header>
<section>
<a href="#" id="add-rule-link">Add this site</a>
<div id="add-new-rule-div" style="display:none">
<h3> Add a new rule</h3>
<p>Always use https for this host.
<label for="new-rule-host">Host:</label> <br><input size="50" id="new-rule-host" type="text" disabled><br>

<div id="new-rule-regular-text">
<p><a href="#" id="new-rule-show-advanced-link">Show advanced</a>
</div>
<div id="new-rule-advanced" style="display:none;">
<p><a href="#" id="new-rule-hide-advanced-link">Hide advanced</a>
<p>
<label for="new-rule-name">Rule name</label><br><input size="50" id="new-rule-name" type="text"><br>
<label for="new-rule-regex">Matching regex</label> <br><input size="50" id="new-rule-regex" type="text"><br>
<label for="new-rule-redirect">Redirect to</label> <br><input size="50" id="new-rule-redirect" type="text"><br>
</div>

<button id="add-new-rule-button">Add new Rule</button><br>
<button id="cancel-new-rule">Cancel</button>
</div>
</section>
<section id="StableRules" class="rules">
<h3 i18n="chrome_stable_rules"></h3>
<p class="description" i18n="chrome_stable_rules_description"></p>
Expand All @@ -22,7 +47,7 @@ <h3 i18n="chrome_experimental_rules"></h3>
</section>
<footer>
<a id="whatIsThis" href="https://www.eff.org/https-everywhere" target="_blank" tabindex="-1" i18n="chrome_what_is_this">blah</a>
<!-- <a href="" title="HTTPS Everywhere Options">Options</a> --->
<!-- <a href="" title="HTTPS Everywhere Options">Options</a> -->
</footer>

</body>
65 changes: 61 additions & 4 deletions chromium/popup.js
Expand Up @@ -3,6 +3,10 @@ var stableRules = null;
var unstableRules = null;
var hostReg = /.*\/\/[^$/]*\//;

function e(id) {
return document.getElementById(id);
}

function toggleRuleLine(checkbox, ruleset) {
ruleset.active = checkbox.checked;

Expand Down Expand Up @@ -81,12 +85,65 @@ document.addEventListener("DOMContentLoaded", function () {
chrome.tabs.getSelected(null, gotTab);

// auto-translate all elements with i18n attributes
var e = document.querySelectorAll("[i18n]");
for (var i=0; i < e.length; i++) {
e[i].innerHTML = chrome.i18n.getMessage(e[i].getAttribute("i18n"));
var elem = document.querySelectorAll("[i18n]");
for (var i=0; i < elem.length; i++) {
elem[i].innerHTML = chrome.i18n.getMessage(elem[i].getAttribute("i18n"));
}

// other translations
document.getElementById("whatIsThis").setAttribute("title", chrome.i18n.getMessage("chrome_what_is_this_title"));
e("whatIsThis").setAttribute("title", chrome.i18n.getMessage("chrome_what_is_this_title"));
e("add-rule-link").addEventListener("click", addManualRule);
});


var escapeForRegex = function( value ) {
return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
};

function hide(elem) {
elem.style.display = "none";
}

function show(elem) {
elem.style.display = "block";
}

function addManualRule() {
chrome.tabs.getSelected(null, function(tab) {
hide(e("add-rule-link"));
show(e("add-new-rule-div"));
var newUrl = new URI(tab.url);
newUrl.scheme("https");
e("new-rule-host").value = newUrl.host();
var oldUrl = new URI(tab.url);
oldUrl.scheme("http");
var oldMatcher = "^" + escapeForRegex(oldUrl.scheme() + "://" + oldUrl.host() + "/");
e("new-rule-regex").value = oldMatcher;
var redirectPath = newUrl.scheme() + "://" + newUrl.host() + "/";
e("new-rule-redirect").value = redirectPath;
e("new-rule-name").value = "Manual rule for " + oldUrl.host();
e("add-new-rule-button").addEventListener("click", function() {
var params = {
host : e("new-rule-host").value,
redirectTo : e("new-rule-redirect").value,
urlMatcher : e("new-rule-regex").value
};
backgroundPage.addNewRule(params, function() {
location.reload();
});
});

e("cancel-new-rule").addEventListener("click", function() {
show(e("add-rule-link"));
hide(e("add-new-rule-div"));
});
e("new-rule-show-advanced-link").addEventListener("click", function() {
show(e("new-rule-advanced"));
hide(e("new-rule-regular-text"));
});
e("new-rule-hide-advanced-link").addEventListener("click", function() {
hide(e("new-rule-advanced"));
show(e("new-rule-regular-text"));
});
});
}

0 comments on commit c669354

Please sign in to comment.