Skip to content

Commit c669354

Browse files
committed
Merge branch 'master' of https://git.torproject.org/https-everywhere into better-obs-whitelisting
2 parents 2878602 + 39146f3 commit c669354

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1120
-468
lines changed

chromium/background.js

Lines changed: 78 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
var USER_RULE_KEY = 'userRules';
12
// Records which tabId's are active in the HTTPS Switch Planner (see
23
// devtools-panel.js).
34
var switchPlannerEnabledFor = {};
@@ -7,9 +8,26 @@ var switchPlannerEnabledFor = {};
78
// rw / nrw stand for "rewritten" versus "not rewritten"
89
var switchPlannerInfo = {};
910

11+
var getStoredUserRules = function() {
12+
var oldUserRuleString = localStorage.getItem(USER_RULE_KEY);
13+
var oldUserRules = [];
14+
if (oldUserRuleString) {
15+
oldUserRules = JSON.parse(oldUserRuleString);
16+
}
17+
return oldUserRules;
18+
};
1019
var all_rules = new RuleSets();
1120
var wr = chrome.webRequest;
21+
var loadStoredUserRules = function() {
22+
var rules = getStoredUserRules();
23+
var i;
24+
for (i = 0; i < rules.length; ++i) {
25+
all_rules.addUserRule(rules[i]);
26+
}
27+
log('INFO', 'loaded ' + i + ' stored user rules');
28+
};
1229

30+
loadStoredUserRules();
1331
/*
1432
for (var v in localStorage) {
1533
log(DBUG, "localStorage["+v+"]: "+localStorage[v]);
@@ -22,20 +40,23 @@ for (r in rs) {
2240
}
2341
*/
2442

25-
// Add the HTTPS Everywhere icon to the URL address bar.
26-
// TODO: Switch from pageAction to browserAction?
27-
function displayPageAction(tabId) {
28-
if (tabId !== -1) {
29-
chrome.tabs.get(tabId, function(tab) {
30-
if(typeof(tab) === "undefined") {
31-
log(DBUG, "Not a real tab. Skipping showing pageAction.");
32-
}
33-
else {
34-
chrome.pageAction.show(tabId);
35-
}
36-
});
43+
44+
var addNewRule = function(params, cb) {
45+
if (all_rules.addUserRule(params)) {
46+
// If we successfully added the user rule, save it in local
47+
// storage so it's automatically applied when the extension is
48+
// reloaded.
49+
var oldUserRules = getStoredUserRules();
50+
// TODO: there's a race condition here, if this code is ever executed from multiple
51+
// client windows in different event loops.
52+
oldUserRules.push(params);
53+
// TODO: can we exceed the max size for storage?
54+
localStorage.setItem(USER_RULE_KEY, JSON.stringify(oldUserRules));
55+
cb(true);
56+
} else {
57+
cb(false);
3758
}
38-
}
59+
};
3960

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

118-
if (details.requestId in redirectCounter) {
119-
redirectCounter[details.requestId] += 1;
120-
log(DBUG, "Got redirect id "+details.requestId+
121-
": "+redirectCounter[details.requestId]);
122-
123-
if (redirectCounter[details.requestId] > 9) {
124-
log(NOTE, "Redirect counter hit for "+canonical_url);
125-
urlBlacklist[canonical_url] = true;
126-
var hostname = uri.hostname();
127-
domainBlacklist[hostname] = true;
128-
log(WARN, "Domain blacklisted " + hostname);
129-
return;
130-
}
131-
} else {
132-
redirectCounter[details.requestId] = 0;
139+
if (redirectCounter[details.requestId] >= 8) {
140+
log(NOTE, "Redirect counter hit for " + canonical_url);
141+
urlBlacklist[canonical_url] = true;
142+
var hostname = uri.hostname();
143+
domainBlacklist[hostname] = true;
144+
log(WARN, "Domain blacklisted " + hostname);
145+
return null;
133146
}
134147

135148
var newuristr = null;
@@ -321,103 +334,75 @@ function switchPlannerDetailsHtmlSection(tab_id, rewritten) {
321334
function onCookieChanged(changeInfo) {
322335
if (!changeInfo.removed && !changeInfo.cookie.secure) {
323336
if (all_rules.shouldSecureCookie(changeInfo.cookie, false)) {
324-
var cookie = {name:changeInfo.cookie.name,value:changeInfo.cookie.value,
325-
domain:changeInfo.cookie.domain,path:changeInfo.cookie.path,
337+
var cookie = {name:changeInfo.cookie.name,
338+
value:changeInfo.cookie.value,
339+
path:changeInfo.cookie.path,
326340
httpOnly:changeInfo.cookie.httpOnly,
327341
expirationDate:changeInfo.cookie.expirationDate,
328-
storeId:changeInfo.cookie.storeId};
329-
cookie.secure = true;
330-
// FIXME: What is with this url noise? are we just supposed to lie?
331-
if (cookie.domain[0] == ".") {
332-
cookie.url = "https://www"+cookie.domain+cookie.path;
342+
storeId:changeInfo.cookie.storeId,
343+
secure: true};
344+
345+
// Host-only cookies don't set the domain field.
346+
if (!changeInfo.cookie.hostOnly) {
347+
cookie.domain = changeInfo.cookie.domain;
348+
}
349+
350+
// The cookie API is magical -- we must recreate the URL from the domain and path.
351+
if (changeInfo.cookie.domain[0] == ".") {
352+
cookie.url = "https://www" + changeInfo.cookie.domain + cookie.path;
333353
} else {
334-
cookie.url = "https://"+cookie.domain+cookie.path;
354+
cookie.url = "https://" + changeInfo.cookie.domain + cookie.path;
335355
}
336356
// We get repeated events for some cookies because sites change their
337357
// value repeatedly and remove the "secure" flag.
338358
log(DBUG,
339-
"Securing cookie "+cookie.name+" for "+cookie.domain+", was secure="+changeInfo.cookie.secure);
359+
"Securing cookie " + cookie.name + " for " + changeInfo.cookie.domain + ", was secure=" + changeInfo.cookie.secure);
340360
chrome.cookies.set(cookie);
341361
}
342362
}
343363
}
344364

345-
// This event is needed due to the potential race between cookie permissions
346-
// update and cookie transmission (because the cookie API is non-blocking).
347-
// Without this function, an aggressive attacker could race to steal a not-yet-secured
348-
// cookie if they controlled & could redirect the user to a non-SSL subdomain.
349-
// WARNING: This is a very hot function.
350-
function onBeforeSendHeaders(details) {
351-
// TODO: Verify this with wireshark
352-
for (var h in details.requestHeaders) {
353-
if (details.requestHeaders[h].name == "Cookie") {
354-
// Per RFC 6265, Chrome sends only ONE cookie header, period.
355-
var uri = new URI(details.url);
356-
var host = uri.hostname();
357-
358-
var newCookies = [];
359-
var cookies = details.requestHeaders[h].value.split(";");
360-
361-
for (var c in cookies) {
362-
// Create a fake "nsICookie2"-ish object to pass in to our rule API:
363-
var fake = {domain:host, name:cookies[c].split("=")[0]};
364-
// XXX I have no idea whether the knownHttp parameter should be true
365-
// or false here. We're supposedly inside a race condition or
366-
// something, right?
367-
var ruleset = all_rules.shouldSecureCookie(fake, false);
368-
if (ruleset) {
369-
activeRulesets.addRulesetToTab(details.tabId, ruleset);
370-
log(INFO, "Woah, we lost the race on updating a cookie: "+details.requestHeaders[h].value);
365+
function onBeforeRedirect(details) {
366+
// Catch HTTPs -> HTTP redirect loops, ignoring about:blank, HTTPS 302s, etc.
367+
if (details.redirectUrl.substring(0, 7) === "http://") {
368+
if (details.requestId in redirectCounter) {
369+
redirectCounter[details.requestId] += 1;
370+
log(DBUG, "Got redirect id "+details.requestId+
371+
": "+redirectCounter[details.requestId]);
371372
} else {
372-
newCookies.push(cookies[c]);
373+
redirectCounter[details.requestId] = 1;
373374
}
374-
}
375-
details.requestHeaders[h].value = newCookies.join(";");
376-
log(DBUG, "Got new cookie header: "+details.requestHeaders[h].value);
377-
378-
// We've seen the one cookie header, so let's get out of here!
379-
break;
380375
}
381-
}
382-
383-
return {requestHeaders:details.requestHeaders};
384-
}
385-
386-
function onResponseStarted(details) {
387-
388-
// redirect counter workaround
389-
// TODO: Remove this code if they ever give us a real counter
390-
if (details.requestId in redirectCounter) {
391-
delete redirectCounter[details.requestId];
392-
}
393376
}
394377

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

397-
// This watches cookies sent via HTTP.
398-
// We do *not* watch HTTPS cookies -- they're already being sent over HTTPS -- yay!
399-
wr.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ["http://*/*"]},
400-
["requestHeaders", "blocking"]);
401-
402-
wr.onResponseStarted.addListener(onResponseStarted,
403-
{urls: ["https://*/*", "http://*/*"]});
380+
// Try to catch redirect loops on URLs we've redirected to HTTPS.
381+
wr.onBeforeRedirect.addListener(onBeforeRedirect, {urls: ["https://*/*"]});
404382

405383

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

413393
// Pre-rendered tabs / instant experiments sometimes skip onUpdated.
414394
// See http://crbug.com/109557
415395
chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) {
416-
displayPageAction(addedTabId);
396+
chrome.tabs.get(addedTabId, function(tab) {
397+
if(typeof(tab) === "undefined") {
398+
log(DBUG, "Not a real tab. Skipping showing pageAction.");
399+
} else {
400+
chrome.pageAction.show(addedTabId);
401+
}
402+
});
417403
});
418404

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

423408
function disableSwitchPlannerFor(tabId) {

chromium/manifest.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"16": "icon16.png",
2222
"48": "icon48.png"
2323
},
24+
"incognito": "split",
2425
"manifest_version": 2,
2526
"minimum_chrome_version": "18",
2627
"name": "__MSG_about_ext_name__",
@@ -37,5 +38,5 @@
3738
"<all_urls>"
3839
],
3940
"update_url": "https://www.eff.org/files/https-everywhere-chrome-updates.xml",
40-
"version": "2014.1.17"
41-
}
41+
"version": "2014.4.16"
42+
}

chromium/popup.html

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,38 @@
55
<link href="chrome-resources/css/chrome_shared.css" rel="stylesheet" type="text/css"/>
66

77
<link href="popup.css" rel="stylesheet" type="text/css"/>
8+
<script src="jquery.min.js"></script>
9+
<script src="URI.js"></script>
810
<script src="popup.js"></script>
11+
912
</head>
1013

1114
<body>
1215
<header>
1316
<h1 i18n="about_ext_name"></h1>
1417
</header>
18+
<section>
19+
<a href="#" id="add-rule-link">Add this site</a>
20+
<div id="add-new-rule-div" style="display:none">
21+
<h3> Add a new rule</h3>
22+
<p>Always use https for this host.
23+
<label for="new-rule-host">Host:</label> <br><input size="50" id="new-rule-host" type="text" disabled><br>
24+
25+
<div id="new-rule-regular-text">
26+
<p><a href="#" id="new-rule-show-advanced-link">Show advanced</a>
27+
</div>
28+
<div id="new-rule-advanced" style="display:none;">
29+
<p><a href="#" id="new-rule-hide-advanced-link">Hide advanced</a>
30+
<p>
31+
<label for="new-rule-name">Rule name</label><br><input size="50" id="new-rule-name" type="text"><br>
32+
<label for="new-rule-regex">Matching regex</label> <br><input size="50" id="new-rule-regex" type="text"><br>
33+
<label for="new-rule-redirect">Redirect to</label> <br><input size="50" id="new-rule-redirect" type="text"><br>
34+
</div>
35+
36+
<button id="add-new-rule-button">Add new Rule</button><br>
37+
<button id="cancel-new-rule">Cancel</button>
38+
</div>
39+
</section>
1540
<section id="StableRules" class="rules">
1641
<h3 i18n="chrome_stable_rules"></h3>
1742
<p class="description" i18n="chrome_stable_rules_description"></p>
@@ -22,7 +47,7 @@ <h3 i18n="chrome_experimental_rules"></h3>
2247
</section>
2348
<footer>
2449
<a id="whatIsThis" href="https://www.eff.org/https-everywhere" target="_blank" tabindex="-1" i18n="chrome_what_is_this">blah</a>
25-
<!-- <a href="" title="HTTPS Everywhere Options">Options</a> --->
50+
<!-- <a href="" title="HTTPS Everywhere Options">Options</a> -->
2651
</footer>
2752

2853
</body>

chromium/popup.js

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ var stableRules = null;
33
var unstableRules = null;
44
var hostReg = /.*\/\/[^$/]*\//;
55

6+
function e(id) {
7+
return document.getElementById(id);
8+
}
9+
610
function toggleRuleLine(checkbox, ruleset) {
711
ruleset.active = checkbox.checked;
812

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

8387
// auto-translate all elements with i18n attributes
84-
var e = document.querySelectorAll("[i18n]");
85-
for (var i=0; i < e.length; i++) {
86-
e[i].innerHTML = chrome.i18n.getMessage(e[i].getAttribute("i18n"));
88+
var elem = document.querySelectorAll("[i18n]");
89+
for (var i=0; i < elem.length; i++) {
90+
elem[i].innerHTML = chrome.i18n.getMessage(elem[i].getAttribute("i18n"));
8791
}
8892

8993
// other translations
90-
document.getElementById("whatIsThis").setAttribute("title", chrome.i18n.getMessage("chrome_what_is_this_title"));
94+
e("whatIsThis").setAttribute("title", chrome.i18n.getMessage("chrome_what_is_this_title"));
95+
e("add-rule-link").addEventListener("click", addManualRule);
9196
});
9297

98+
99+
var escapeForRegex = function( value ) {
100+
return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
101+
};
102+
103+
function hide(elem) {
104+
elem.style.display = "none";
105+
}
106+
107+
function show(elem) {
108+
elem.style.display = "block";
109+
}
110+
111+
function addManualRule() {
112+
chrome.tabs.getSelected(null, function(tab) {
113+
hide(e("add-rule-link"));
114+
show(e("add-new-rule-div"));
115+
var newUrl = new URI(tab.url);
116+
newUrl.scheme("https");
117+
e("new-rule-host").value = newUrl.host();
118+
var oldUrl = new URI(tab.url);
119+
oldUrl.scheme("http");
120+
var oldMatcher = "^" + escapeForRegex(oldUrl.scheme() + "://" + oldUrl.host() + "/");
121+
e("new-rule-regex").value = oldMatcher;
122+
var redirectPath = newUrl.scheme() + "://" + newUrl.host() + "/";
123+
e("new-rule-redirect").value = redirectPath;
124+
e("new-rule-name").value = "Manual rule for " + oldUrl.host();
125+
e("add-new-rule-button").addEventListener("click", function() {
126+
var params = {
127+
host : e("new-rule-host").value,
128+
redirectTo : e("new-rule-redirect").value,
129+
urlMatcher : e("new-rule-regex").value
130+
};
131+
backgroundPage.addNewRule(params, function() {
132+
location.reload();
133+
});
134+
});
135+
136+
e("cancel-new-rule").addEventListener("click", function() {
137+
show(e("add-rule-link"));
138+
hide(e("add-new-rule-div"));
139+
});
140+
e("new-rule-show-advanced-link").addEventListener("click", function() {
141+
show(e("new-rule-advanced"));
142+
hide(e("new-rule-regular-text"));
143+
});
144+
e("new-rule-hide-advanced-link").addEventListener("click", function() {
145+
hide(e("new-rule-advanced"));
146+
show(e("new-rule-regular-text"));
147+
});
148+
});
149+
}

0 commit comments

Comments
 (0)