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

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
MB committed Jan 11, 2014
2 parents 72a5de8 + 6c8cd5d commit c873a48
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 1 deletion.
202 changes: 202 additions & 0 deletions chromium/background.js
@@ -1,3 +1,11 @@
// Records which tabId's are active in the HTTPS Switch Planner (see
// devtools-panel.js).
var switchPlannerEnabledFor = {};
// Detailed information recorded when the HTTPS Switch Planner is active.
// Structure is:
// switchPlannerInfo[tabId]["rw"/"nrw"][resource_host][active_content][url];
// rw / nrw stand for "rewritten" versus "not rewritten"
var switchPlannerInfo = {};

var all_rules = new RuleSets();
var wr = chrome.webRequest;
Expand Down Expand Up @@ -141,6 +149,16 @@ function onBeforeRequest(details) {
newuristr = finaluri.toString();
}

// In Switch Planner Mode, record any non-rewriteable
// HTTP URIs by parent hostname, along with the resource type.
if (switchPlannerEnabledFor[details.tabId] && uri.protocol() !== "https") {
writeToSwitchPlanner(details.type,
details.tabId,
canonical_host,
details.url,
newuristr);
}

if (newuristr) {
log(DBUG, "Redirecting from "+details.url+" to "+newuristr);
return {redirectUrl: newuristr};
Expand All @@ -149,6 +167,157 @@ function onBeforeRequest(details) {
}
}


// Map of which values for the `type' enum denote active vs passive content.
// https://developer.chrome.com/extensions/webRequest.html#event-onBeforeRequest
var activeTypes = { stylesheet: 1, script: 1, object: 1, other: 1};
// We consider sub_frame to be passive even though it can contain JS or Flash.
// This is because code running the sub_frame cannot access the main frame's
// content, by same-origin policy. This is true even if the sub_frame is on the
// same domain but different protocol - i.e. HTTP while the parent is HTTPS -
// because same-origin policy includes the protocol. This also mimics Chrome's
// UI treatment of insecure subframes.
var passiveTypes = { main_frame: 1, sub_frame: 1, image: 1, xmlhttprequest: 1};

// Record a non-HTTPS URL loaded by a given hostname in the Switch Planner, for
// use in determining which resources need to be ported to HTTPS.
function writeToSwitchPlanner(type, tab_id, resource_host, resource_url, rewritten_url) {
var rw = "rw";
if (rewritten_url == null)
rw = "nrw";

var active_content = 0;
if (activeTypes[type]) {
active_content = 1;
} else if (passiveTypes[type]) {
active_content = 0;
} else {
log(WARN, "Unknown type from onBeforeRequest details: `" + type + "', assuming active");
active_content = 1;
}

if (!switchPlannerInfo[tab_id]) {
switchPlannerInfo[tab_id] = {};
switchPlannerInfo[tab_id]["rw"] = {};
switchPlannerInfo[tab_id]["nrw"] = {};
}
if (!switchPlannerInfo[tab_id][rw][resource_host])
switchPlannerInfo[tab_id][rw][resource_host] = {};
if (!switchPlannerInfo[tab_id][rw][resource_host][active_content])
switchPlannerInfo[tab_id][rw][resource_host][active_content] = {};

switchPlannerInfo[tab_id][rw][resource_host][active_content][resource_url] = 1;
}

// Return the number of properties in an object. For associative maps, this is
// their size.
function objSize(obj) {
if (typeof obj == 'undefined') return 0;
var size = 0, key;
for (key in obj) {
if (obj.hasOwnProperty(key)) size++;
}
return size;
}

// Make an array of asset hosts by score so we can sort them,
// presenting the most important ones first.
function sortSwitchPlanner(tab_id, rewritten) {
var asset_host_list = [];
if (typeof switchPlannerInfo[tab_id] === 'undefined' ||
typeof switchPlannerInfo[tab_id][rewritten] === 'undefined') {
return [];
}
var tabInfo = switchPlannerInfo[tab_id][rewritten];
for (var asset_host in tabInfo) {
var ah = tabInfo[asset_host];
var activeCount = objSize(ah[1]);
var passiveCount = objSize(ah[0]);
var score = activeCount * 100 + passiveCount;
asset_host_list.push([score, activeCount, passiveCount, asset_host]);
}
asset_host_list.sort(function(a,b){return a[0]-b[0]});
return asset_host_list;
}

// Format the switch planner output for presentation to a user.
function switchPlannerSmallHtmlSection(tab_id, rewritten) {
var asset_host_list = sortSwitchPlanner(tab_id, rewritten);
if (asset_host_list.length == 0) {
return "<b>none</b>";
}

var output = "";
for (var i = asset_host_list.length - 1; i >= 0; i--) {
var host = asset_host_list[i][3];
var activeCount = asset_host_list[i][1];
var passiveCount = asset_host_list[i][2];

output += "<b>" + host + "</b>: ";
if (activeCount > 0) {
output += activeCount + " active";
if (passiveCount > 0)
output += ", ";
}
if (passiveCount > 0) {
output += passiveCount + " passive";
}
output += "<br/>";
}
return output;
}

function switchPlannerRenderSections(tab_id, f) {
return "Unrewritten HTTP resources loaded from this tab (enable HTTPS on " +
"these domains and add them to HTTPS Everywhere):<br/>" +
f(tab_id, "nrw") +
"<br/>Resources rewritten successfully from this tab (update these " +
"in your source code):<br/>" +
f(tab_id, "rw");
}

function switchPlannerSmallHtml(tab_id) {
return switchPlannerRenderSections(tab_id, switchPlannerSmallHtmlSection);
}

function linksFromKeys(map) {
if (typeof map == 'undefined') return "";
var output = "";
for (var key in map) {
if (map.hasOwnProperty(key)) {
output += "<a href='" + key + "'>" + key + "</a><br/>";
}
}
return output;
}

function switchPlannerDetailsHtml(tab_id) {
return switchPlannerRenderSections(tab_id, switchPlannerDetailsHtmlSection);
}

function switchPlannerDetailsHtmlSection(tab_id, rewritten) {
var asset_host_list = sortSwitchPlanner(tab_id, rewritten);
var output = "";

for (var i = asset_host_list.length - 1; i >= 0; i--) {
var host = asset_host_list[i][3];
var activeCount = asset_host_list[i][1];
var passiveCount = asset_host_list[i][2];

output += "<b>" + host + "</b>: ";
if (activeCount > 0) {
output += activeCount + " active<br/>";
output += linksFromKeys(switchPlannerInfo[tab_id][rewritten][host][1]);
}
if (passiveCount > 0) {
output += "<br/>" + passiveCount + " passive<br/>";
output += linksFromKeys(switchPlannerInfo[tab_id][rewritten][host][0]);
}
output += "<br/>";
}
return output;
}

function onCookieChanged(changeInfo) {
if (!changeInfo.removed && !changeInfo.cookie.secure) {
if (all_rules.shouldSecureCookie(changeInfo.cookie, false)) {
Expand Down Expand Up @@ -250,3 +419,36 @@ chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) {
// 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.
chrome.cookies.onChanged.addListener(onCookieChanged);

function disableSwitchPlannerFor(tabId) {
delete switchPlannerEnabledFor[tabId];
// Clear stored URL info.
delete switchPlannerInfo[tabId];
}

function enableSwitchPlannerFor(tabId) {
switchPlannerEnabledFor[tabId] = true;
}

// Listen for connection from the DevTools panel so we can set up communication.
chrome.runtime.onConnect.addListener(function (port) {
if (port.name == "devtools-page") {
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
var tabId = message.tabId;

var disableOnCloseCallback = function(port) {
log(DBUG, "Devtools window for tab " + tabId + " closed, clearing data.");
disableSwitchPlannerFor(tabId);
};

if (message.type === "enable") {
enableSwitchPlannerFor(tabId);
port.onDisconnect.addListener(disableOnCloseCallback);
} else if (message.type === "disable") {
disableSwitchPlannerFor(tabId);
} else if (message.type === "getSmallHtml") {
sendResponse({html: switchPlannerSmallHtml(tabId)});
}
});
}
});
32 changes: 32 additions & 0 deletions chromium/devtools-panel.html
@@ -0,0 +1,32 @@
<head>
<title></title>
<script src="devtools-panel.js"></script>
</head>
<body>
<div id=SwitchPlanner>
<input type=checkbox id=SwitchPlannerCheckbox>
<label for=SwitchPlannerCheckbox>Enable HTTPS Switch Planner mode (reloads page).</label>
<p>
<div id=SwitchPlannerDescription>
Switch Planner mode helps prepare for your site's switch to HTTPS by generating
a report of external HTTP resources that might not yet be available on HTTPS.
<p>
After enabling, navigate around your site and try to exercise all
functionality in order to get a comprehensive list of external resources.
<p>
For each group of resources listed as "Unrewritten," find out whether they
are available on HTTPS. If so: add a rule to HTTPS Everywhere! If not: try
to make them available over HTTPS or use a different resource or provider.
Otherwise your site will generate
<a id=MixedContentLink href="https://developer.mozilla.org/en-US/docs/Security/MixedContent#Mixed_passive.2Fdisplay_content">Mixed Content</a>
(passive or active) errors when you turn on HTTPS.
<p>
For most accurate results, disable ad blockers before using. Closing this
panel will deactivate Switch Planner mode and clear stored data.
</div>
<div id=SwitchPlannerResults style="display: none;">
<div id=SwitchPlannerDetails></div>
<a id=SwitchPlannerDetailsLink href="javascript:void(0);">details</a>
</div>
</div>
</body>
67 changes: 67 additions & 0 deletions chromium/devtools-panel.js
@@ -0,0 +1,67 @@
function e(id) {
return document.getElementById(id);
}

function sendMessage(type) {
chrome.runtime.sendMessage({
type: type,
tabId: chrome.devtools.inspectedWindow.tabId,
});
}

// Turn on the Switch Planner recording mode, and hide the long description.
function enableSwitchPlanner() {
sendMessage("enable");
e("SwitchPlannerDescription").style.display = "none";
e("SwitchPlannerDetails").style.display = "block";
// Hack: Fetch and display summary information from background page
// once per second.
setInterval(display, 1000);
chrome.devtools.inspectedWindow.reload();
}

// Disable the switch planner and reload, so any state is forgotten and
// the long description is restored.
function disableSwitchPlanner() {
sendMessage("disable");
document.location.reload();
}

// Fetch summary HTML of the planner results from the background page for
// display in the devtools panel.
function display() {
chrome.runtime.sendMessage({
type: "getSmallHtml",
tabId: chrome.devtools.inspectedWindow.tabId,
}, function(response) {
e("SwitchPlannerDetails").innerHTML = response.html;
e("SwitchPlannerResults").style.display = "block";
});
}

window.onload = function() {
// Open a connection to the background page. Right now this is only used
// by the background page so it knows when the devtools pane has closed.
// We don't receive messages from the background page currently, though that
// may be a future improvement. Sending messages to the background page doesn't
// require an existing connection.
chrome.runtime.connect({ name: "devtools-page" });

var checkbox = e("SwitchPlannerCheckbox");
checkbox.addEventListener("change", function() {
if (checkbox.checked) {
enableSwitchPlanner();
} else {
disableSwitchPlanner();
}
});

e("SwitchPlannerDetailsLink").addEventListener("click", function() {
window.open("switch-planner.html?tab=" + chrome.devtools.inspectedWindow.tabId);
});
// Since this is rendered in a devtools console, we have to make clicks on the
// link open a new window.
e("MixedContentLink").addEventListener("click", function(e) {
window.open(e.target.href);
});
};
6 changes: 6 additions & 0 deletions chromium/devtools.html
@@ -0,0 +1,6 @@
<head>
<title></title>
<script src="devtools.js"></script>
</head>
<body>
</body>
6 changes: 6 additions & 0 deletions chromium/devtools.js
@@ -0,0 +1,6 @@
chrome.devtools.panels.create("HTTPS Everywhere",
"icon48.png",
"devtools-panel.html",
function(panel) {
}
);
1 change: 1 addition & 0 deletions chromium/manifest.json
Expand Up @@ -28,6 +28,7 @@
"default_popup": "popup.html",
"default_title": "__MSG_about_ext_name__"
},
"devtools_page": "devtools.html",
"permissions": [
"webRequest",
"webRequestBlocking",
Expand Down
2 changes: 1 addition & 1 deletion chromium/rules.js
Expand Up @@ -187,7 +187,7 @@ RuleSets.prototype = {
// Have we cached this result? If so, return it!
var cached_item = ruleCache.get(host);
if (cached_item !== undefined) {
log(DBUG, "Rulseset cache hit for " + host);
log(DBUG, "Ruleset cache hit for " + host);
return cached_item;
}
log(DBUG, "Ruleset cache miss for " + host);
Expand Down
7 changes: 7 additions & 0 deletions chromium/switch-planner.html
@@ -0,0 +1,7 @@
<head>
<title></title>
<script src="switch-planner.js"></script>
</head>
<body>
<div id=content />
</body>
7 changes: 7 additions & 0 deletions chromium/switch-planner.js
@@ -0,0 +1,7 @@
window.onload = function() {
var backgroundPage = chrome.extension.getBackgroundPage();
var tab = document.location.search.match(/tab=([^&]*)/)[1];
document.getElementById("content").innerHTML =
backgroundPage.switchPlannerDetailsHtml(tab);
};

0 comments on commit c873a48

Please sign in to comment.