Bug 932179 - Part 2: UI to present security info in NetMonitor. r=vporof

This commit is contained in:
Sami Jaktholm 2015-01-06 02:59:00 -05:00
Родитель b53cf7a775
Коммит 16e39dfd4d
18 изменённых файлов: 1024 добавлений и 13 удалений

Просмотреть файл

@ -8,6 +8,7 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
const NET_STRINGS_URI = "chrome://browser/locale/devtools/netmonitor.properties";
const PKI_STRINGS_URI = "chrome://pippki/locale/pippki.properties";
const LISTENERS = [ "NetworkActivity" ];
const NET_PREFS = { "NetworkMonitor.saveRequestAndResponseBodies": true };
@ -34,6 +35,10 @@ const EVENTS = {
UPDATING_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdating:RequestPostData",
RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData",
// When security information begins and finishes receiving.
UPDATING_SECURITY_INFO: "NetMonitor::NetworkEventUpdating:SecurityInfo",
RECEIVED_SECURITY_INFO: "NetMonitor::NetworkEventUpdated:SecurityInfo",
// When response headers begin and finish receiving.
UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders",
RECEIVED_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdated:ResponseHeaders",
@ -136,6 +141,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "DevToolsUtils",
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
XPCOMUtils.defineLazyServiceGetter(this, "DOMParser",
"@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
Object.defineProperty(this, "NetworkHelper", {
get: function() {
return require("devtools/toolkit/webconsole/network-helper");
@ -580,6 +588,13 @@ NetworkEventsHandler.prototype = {
this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
break;
case "securityInfo":
NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
securityState: aPacket.state,
});
this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
break;
case "responseHeaders":
this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
@ -655,6 +670,20 @@ NetworkEventsHandler.prototype = {
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, aResponse.from);
},
/**
* Handles additional information received for a "securityInfo" packet.
*
* @param object aResponse
* The message received from the server.
*/
_onSecurityInfo: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
securityInfo: aResponse.securityInfo
});
window.emit(EVENTS.RECEIVED_SECURITY_INFO, aResponse.from);
},
/**
* Handles additional information received for a "responseHeaders" packet.
*
@ -749,6 +778,7 @@ NetworkEventsHandler.prototype = {
* Localization convenience methods.
*/
let L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
let PKI_L10N = new ViewHelpers.L10N(PKI_STRINGS_URI);
/**
* Shortcuts for accessing various network monitor preferences.

Просмотреть файл

@ -336,6 +336,7 @@ function RequestsMenuView() {
this._byFile = this._byFile.bind(this);
this._byDomain = this._byDomain.bind(this);
this._byType = this._byType.bind(this);
this._onSecurityIconClick = this._onSecurityIconClick.bind(this);
}
RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
@ -1072,6 +1073,17 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
tooltip.defaultPosition = REQUESTS_TOOLTIP_POSITION;
},
/**
* Attaches security icon click listener for the given request menu item.
*
* @param object item
* The network request item to attach the listener to.
*/
attachSecurityIconClickListener: function ({ target }) {
let icon = $(".requests-security-state-icon", target);
icon.addEventListener("click", this._onSecurityIconClick);
},
/**
* Schedules adding additional information to a network request.
*
@ -1150,6 +1162,13 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
requestItem.attachment.requestPostData = value;
requestItem.attachment.requestHeadersFromUploadStream = currentStore;
break;
case "securityState":
requestItem.attachment.securityState = value;
this.updateMenuView(requestItem, key, value);
break;
case "securityInfo":
requestItem.attachment.securityInfo = value;
break;
case "responseHeaders":
requestItem.attachment.responseHeaders = value;
break;
@ -1307,6 +1326,15 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
domain.setAttribute("tooltiptext", hostPort);
break;
}
case "securityState": {
let tooltip = L10N.getStr("netmonitor.security.state." + aValue);
let icon = $(".requests-security-state-icon", target);
icon.classList.add("security-state-" + aValue);
icon.setAttribute("tooltiptext", tooltip);
this.attachSecurityIconClickListener(aItem);
break;
}
case "status": {
let node = $(".requests-menu-status", target);
let codeNode = $(".requests-menu-status-code", target);
@ -1610,6 +1638,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
// in this container, so it's necessary to refresh the Tooltip instances.
this.refreshTooltip(firstItem);
this.refreshTooltip(secondItem);
// Reattach click listener to the security icons
this.attachSecurityIconClickListener(firstItem);
this.attachSecurityIconClickListener(secondItem);
},
/**
@ -1644,6 +1677,18 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
}
},
/**
* A handler that opens the security tab in the details view if secure or
* broken security indicator is clicked.
*/
_onSecurityIconClick: function(e) {
let state = this.selectedItem.attachment.securityState;
if (state === "broken" || state === "secure") {
// Choose the security tab.
NetMonitorView.NetworkDetails.widget.selectedIndex = 5;
}
},
/**
* The resize listener for this container's window.
*/
@ -2083,9 +2128,20 @@ NetworkDetailsView.prototype = {
$("#preview-tab").hidden = !isHtml;
$("#preview-tabpanel").hidden = !isHtml;
// Show the "Security" tab only for requests that
// 1) are https (state != insecure)
// 2) come from a target that provides security information.
let hasSecurityInfo = aData.securityState &&
aData.securityState !== "insecure";
$("#security-tab").hidden = !hasSecurityInfo;
// Switch to the "Headers" tabpanel if the "Preview" previously selected
// and this is not an HTML response.
if (!isHtml && this.widget.selectedIndex == 5) {
// and this is not an HTML response or "Security" was selected but this
// request has no security information.
if (!isHtml && this.widget.selectedPanel === $("#preview-tabpanel") ||
!hasSecurityInfo && this.widget.selectedPanel === $("#security-tabpanel")) {
this.widget.selectedIndex = 0;
}
@ -2152,7 +2208,10 @@ NetworkDetailsView.prototype = {
case 4: // "Timings"
yield view._setTimingsInformation(src.eventTimings);
break;
case 5: // "Preview"
case 5: // "Security"
yield view._setSecurityInfo(src.securityInfo, src.url);
break;
case 6: // "Preview"
yield view._setHtmlPreview(src.responseContent);
break;
}
@ -2657,6 +2716,98 @@ NetworkDetailsView.prototype = {
window.emit(EVENTS.RESPONSE_HTML_PREVIEW_DISPLAYED);
}),
/**
* Sets the security information shown in this view.
*
* @param object securityInfo
* The data received from server
* @param string url
* The URL of this request
* @return object
* A promise that is resolved when the security info is rendered.
*/
_setSecurityInfo: Task.async(function* (securityInfo, url) {
if (!securityInfo) {
// We don't have security info. This could mean one of two things:
// 1) This connection is not secure and this tab is not visible and thus
// we shouldn't be here.
// 2) We have already received securityState and the tab is visible BUT
// the rest of the information is still on its way. Once it arrives
// this method is called again.
return;
}
/**
* A helper that sets label text to specified value.
*
* @param string selector
* A selector for the label.
* @param string value
* The value label should have. If this evaluates to false a
* placeholder string <Not Available> is used instead.
*/
function setLabel(selector, value) {
let label = $(selector);
if (!value) {
label.value = L10N.getStr("netmonitor.security.notAvailable");
label.setAttribute("tooltiptext", label.value);
} else {
label.value = value;
label.setAttribute("tooltiptext", value);
}
}
let errorbox = $("#security-error");
let infobox = $("#security-information");
if (securityInfo.state === "secure") {
infobox.hidden = false;
errorbox.hidden = true;
let enabledLabel = L10N.getStr("netmonitor.security.enabled");
let disabledLabel = L10N.getStr("netmonitor.security.disabled");
// Connection parameters
setLabel("#security-protocol-version-value", securityInfo.protocolVersion);
setLabel("#security-ciphersuite-value", securityInfo.cipherSuite);
// Host header
let domain = NetMonitorView.RequestsMenu._getUriHostPort(url);
let hostHeader = L10N.getFormatStr("netmonitor.security.hostHeader", domain);
setLabel("#security-info-host-header", hostHeader);
// Parameters related to the domain
setLabel("#security-http-strict-transport-security-value",
securityInfo.hsts ? enabledLabel : disabledLabel);
setLabel("#security-public-key-pinning-value",
securityInfo.hpkp ? enabledLabel : disabledLabel);
// Certificate parameters
let cert = securityInfo.cert;
setLabel("#security-cert-subject-cn", cert.subject.commonName);
setLabel("#security-cert-subject-o", cert.subject.organization);
setLabel("#security-cert-subject-ou", cert.subject.organizationalUnit);
setLabel("#security-cert-issuer-cn", cert.issuer.commonName);
setLabel("#security-cert-issuer-o", cert.issuer.organization);
setLabel("#security-cert-issuer-ou", cert.issuer.organizationalUnit);
setLabel("#security-cert-validity-begins", cert.validity.start);
setLabel("#security-cert-validity-expires", cert.validity.end);
setLabel("#security-cert-sha1-fingerprint", cert.fingerprint.sha1);
setLabel("#security-cert-sha256-fingerprint", cert.fingerprint.sha256);
} else {
infobox.hidden = true;
errorbox.hidden = false;
// Strip any HTML from the message.
let plain = DOMParser.parseFromString(securityInfo.errorMessage, "text/html");
$("#security-error-message").textContent = plain.body.textContent;
}
}),
_dataSrc: null,
_headers: null,
_cookies: null,

Просмотреть файл

@ -11,6 +11,8 @@
<!DOCTYPE window [
<!ENTITY % netmonitorDTD SYSTEM "chrome://browser/locale/devtools/netmonitor.dtd">
%netmonitorDTD;
<!ENTITY % certManagerDTD SYSTEM "chrome://pippki/locale/certManager.dtd">
%certManagerDTD;
]>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
@ -82,10 +84,10 @@
</button>
</hbox>
<hbox id="requests-menu-domain-header-box"
class="requests-menu-header requests-menu-domain"
class="requests-menu-header requests-menu-security-and-domain"
align="center">
<button id="requests-menu-domain-button"
class="requests-menu-header-button requests-menu-domain"
class="requests-menu-header-button requests-menu-security-and-domain"
data-key="domain"
label="&netmonitorUI.toolbar.domain;"
flex="1">
@ -180,8 +182,13 @@
crop="end"
flex="1"/>
</hbox>
<label class="plain requests-menu-subitem requests-menu-domain"
crop="end"/>
<hbox class="requests-menu-subitem requests-menu-security-and-domain"
align="center">
<image class="requests-security-state-icon" />
<label class="plain requests-menu-domain"
crop="end"
flex="1"/>
</hbox>
<label class="plain requests-menu-subitem requests-menu-type"
crop="end"/>
<label class="plain requests-menu-subitem requests-menu-transferred"
@ -278,6 +285,8 @@
label="&netmonitorUI.tab.response;"/>
<tab id="timings-tab"
label="&netmonitorUI.tab.timings;"/>
<tab id="security-tab"
label="&netmonitorUI.tab.security;"/>
<tab id="preview-tab"
label="&netmonitorUI.tab.preview;"/>
</tabs>
@ -472,6 +481,194 @@
</hbox>
</vbox>
</tabpanel>
<tabpanel id="security-tabpanel"
class="tabpanel-content">
<vbox id="security-error"
class="tabpanel-summary-container"
flex="1">
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.security.error;"/>
<description id="security-error-message" flex="1"/>
</vbox>
<vbox id="security-information"
flex="1">
<vbox id="security-info-connection"
class="tabpanel-summary-container">
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.security.connection;"/>
<vbox class="security-info-section">
<hbox id="security-protocol-version"
class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.security.protocolVersion;"/>
<label id="security-protocol-version-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
<hbox id="security-ciphersuite"
class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.security.cipherSuite;"/>
<label id="security-ciphersuite-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
</vbox>
</vbox>
<vbox id="security-info-domain"
class="tabpanel-summary-container">
<label class="plain tabpanel-summary-label"
id="security-info-host-header"/>
<vbox class="security-info-section">
<hbox id="security-http-strict-transport-security"
class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.security.hsts;"/>
<label id="security-http-strict-transport-security-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
<hbox id="security-public-key-pinning"
class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.security.hpkp;"/>
<label id="security-public-key-pinning-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
</vbox>
</vbox>
<vbox id="security-info-certificate"
class="tabpanel-summary-container">
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.security.certificate;"/>
<vbox class="security-info-section">
<vbox class="tabpanel-summary-container">
<label class="plain tabpanel-summary-label"
value="&certmgr.subjectinfo.label;" flex="1"/>
</vbox>
<vbox class="security-info-section">
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.certdetail.cn;:"/>
<label id="security-cert-subject-cn"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.certdetail.o;:"/>
<label id="security-cert-subject-o"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.certdetail.ou;:"/>
<label id="security-cert-subject-ou"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
</vbox>
<vbox class="tabpanel-summary-container">
<label class="plain tabpanel-summary-label"
value="&certmgr.issuerinfo.label;" flex="1"/>
</vbox>
<vbox class="security-info-section">
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.certdetail.cn;:"/>
<label id="security-cert-issuer-cn"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.certdetail.o;:"/>
<label id="security-cert-issuer-o"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.certdetail.ou;:"/>
<label id="security-cert-issuer-ou"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
</vbox>
<vbox class="tabpanel-summary-container">
<label class="plain tabpanel-summary-label"
value="&certmgr.periodofvalidity.label;" flex="1"/>
</vbox>
<vbox class="security-info-section">
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.begins;:"/>
<label id="security-cert-validity-begins"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.expires;:"/>
<label id="security-cert-validity-expires"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
</vbox>
<vbox class="tabpanel-summary-container">
<label class="plain tabpanel-summary-label"
value="&certmgr.fingerprints.label;" flex="1"/>
</vbox>
<vbox class="security-info-section">
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.certdetail.sha256fingerprint;:"/>
<label id="security-cert-sha256-fingerprint"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
<hbox class="tabpanel-summary-container"
align="center">
<label class="plain tabpanel-summary-label"
value="&certmgr.certdetail.sha1fingerprint;:"/>
<label id="security-cert-sha1-fingerprint"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
</vbox>
</vbox>
</vbox>
</vbox>
</tabpanel>
<tabpanel id="preview-tabpanel"
class="tabpanel-content">
<html:iframe id="response-preview"

Просмотреть файл

@ -26,6 +26,7 @@ support-files =
html_copy-as-curl.html
html_curl-utils.html
sjs_content-type-test-server.sjs
sjs_https-redirect-test-server.sjs
sjs_simple-test-server.sjs
sjs_sorting-test-server.sjs
sjs_status-codes-test-server.sjs
@ -83,6 +84,13 @@ skip-if = e10s # Bug 1091603
[browser_net_req-resp-bodies.js]
[browser_net_resend.js]
skip-if = e10s # Bug 1091612
[browser_net_security-details.js]
[browser_net_security-error.js]
[browser_net_security-icon-click.js]
[browser_net_security-redirect.js]
[browser_net_security-state.js]
[browser_net_security-tab-deselect.js]
[browser_net_security-tab-visibility.js]
[browser_net_simple-init.js]
[browser_net_simple-request-data.js]
[browser_net_simple-request-details.js]

Просмотреть файл

@ -26,10 +26,10 @@ function test() {
"The preview tabpanel should be hidden for non html responses.");
RequestsMenu.selectedIndex = 4;
NetMonitorView.toggleDetailsPane({ visible: true, animated: false }, 5);
NetMonitorView.toggleDetailsPane({ visible: true, animated: false }, 6);
is($("#event-details-pane").selectedIndex, 5,
"The fifth tab in the details pane should be selected.");
is($("#event-details-pane").selectedIndex, 6,
"The sixth tab in the details pane should be selected.");
is($("#preview-tab").hidden, false,
"The preview tab should be visible now.");
is($("#preview-tabpanel").hidden, false,

Просмотреть файл

@ -0,0 +1,90 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that Security details tab contains the expected data.
*/
add_task(function* () {
let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
let { $, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
info("Performing a secure request.");
debuggee.performRequests(1, "https://example.com");
yield waitForNetworkEvents(monitor, 1);
info("Selecting the request.");
RequestsMenu.selectedIndex = 0;
info("Waiting for details pane to be updated.");
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
info("Selecting security tab.");
NetworkDetails.widget.selectedIndex = 5;
info("Waiting for security tab to be updated.");
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
let errorbox = $("#security-error");
let infobox = $("#security-information");
is(errorbox.hidden, true, "Error box is hidden.");
is(infobox.hidden, false, "Information box visible.");
// Connection
checkLabel("#security-protocol-version-value", "TLSv1");
checkLabel("#security-ciphersuite-value", "TLS_RSA_WITH_AES_128_CBC_SHA");
// Host
checkLabel("#security-info-host-header", "Host example.com:");
checkLabel("#security-http-strict-transport-security-value", "Disabled");
checkLabel("#security-public-key-pinning-value", "Disabled");
// Cert
checkLabel("#security-cert-subject-cn", "example.com");
checkLabel("#security-cert-subject-o", "<Not Available>");
checkLabel("#security-cert-subject-ou", "<Not Available>");
checkLabel("#security-cert-issuer-cn", "Temporary Certificate Authority");
checkLabel("#security-cert-issuer-o", "Mozilla Testing");
checkLabel("#security-cert-issuer-ou", "<Not Available>");
// Locale sensitive and varies between timezones. Cant't compare equality or
// the test fails depending on which part of the world the test is executed.
checkLabelNotEmpty("#security-cert-validity-begins");
checkLabelNotEmpty("#security-cert-validity-expires");
checkLabelNotEmpty("#security-cert-sha1-fingerprint");
checkLabelNotEmpty("#security-cert-sha256-fingerprint");
yield teardown(monitor);
/**
* A helper that compares value attribute of a label with given selector to the
* expected value.
*/
function checkLabel(selector, expected) {
info("Checking label " + selector);
let element = $(selector);
ok(element, "Selector matched an element.");
is(element.value, expected, "Label has the expected value.");
}
/**
* A helper that checks the label with given selector is not an empty string.
*/
function checkLabelNotEmpty(selector) {
info("Checking that label " + selector + " is non-empty.");
let element = $(selector);
ok(element, "Selector matched an element.");
isnot(element.value, "", "Label was not empty.");
}
});

Просмотреть файл

@ -0,0 +1,67 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that Security details tab shows an error message with broken connections.
*/
add_task(function* () {
let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
let { $, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
info("Requesting a resource that has a certificate problem.");
debuggee.performRequests(1, "https://nocert.example.com");
yield waitForSecurityBrokenNetworkEvent();
info("Selecting the request.");
RequestsMenu.selectedIndex = 0;
info("Waiting for details pane to be updated.");
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
info("Selecting security tab.");
NetworkDetails.widget.selectedIndex = 5;
info("Waiting for security tab to be updated.");
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
let errorbox = $("#security-error");
let errormsg = $("#security-error-message");
let infobox = $("#security-information");
is(errorbox.hidden, false, "Error box is visble.");
is(infobox.hidden, true, "Information box is hidden.");
isnot(errormsg.textContent, "", "Error message is not empty.");
yield teardown(monitor);
/**
* Returns a promise that's resolved once a request with security issues is
* completed.
*/
function waitForSecurityBrokenNetworkEvent() {
let awaitedEvents = [
"UPDATING_REQUEST_HEADERS",
"RECEIVED_REQUEST_HEADERS",
"UPDATING_REQUEST_COOKIES",
"RECEIVED_REQUEST_COOKIES",
"STARTED_RECEIVING_RESPONSE",
"UPDATING_RESPONSE_CONTENT",
"RECEIVED_RESPONSE_CONTENT",
"UPDATING_EVENT_TIMINGS",
"RECEIVED_EVENT_TIMINGS",
];
let promises = awaitedEvents.map((event) => {
return monitor.panelWin.once(EVENTS[event]);
});
return Promise.all(promises);
}
});

Просмотреть файл

@ -0,0 +1,53 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that clicking on the security indicator opens the security details tab.
*/
add_task(function* () {
let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
let { $, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
info("Requesting a resource over HTTPS.");
debuggee.performRequests(1, "https://example.com/request_2");
yield waitForNetworkEvents(monitor, 1);
debuggee.performRequests(1, "https://example.com/request_1");
yield waitForNetworkEvents(monitor, 1);
is(RequestsMenu.itemCount, 2, "Two events event logged.");
yield clickAndTestSecurityIcon();
info("Selecting headers panel again.");
NetworkDetails.widget.selectedIndex = 0;
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
info("Sorting the items by filename.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-file-button"));
info("Testing that security icon can be clicked after the items were sorted.");
yield clickAndTestSecurityIcon();
yield teardown(monitor);
function* clickAndTestSecurityIcon() {
let item = RequestsMenu.items[0];
let icon = $(".requests-security-state-icon", item.target);
info("Clicking security icon of the first request and waiting for the " +
"panel to update.");
icon.click();
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
is(NetworkDetails.widget.selectedPanel, $("#security-tabpanel"),
"Security tab is selected.");
}
});

Просмотреть файл

@ -0,0 +1,35 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test a http -> https redirect shows secure icon only for redirected https
* request.
*/
add_task(function* () {
let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
let { $, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
debuggee.performRequests(1, HTTPS_REDIRECT_SJS);
yield waitForNetworkEvents(monitor, 2);
is(RequestsMenu.itemCount, 2, "There were two requests due to redirect.");
let initial = RequestsMenu.items[0];
let redirect = RequestsMenu.items[1];
let initialSecurityIcon = $(".requests-security-state-icon", initial.target);
let redirectSecurityIcon = $(".requests-security-state-icon", redirect.target);
ok(initialSecurityIcon.classList.contains("security-state-insecure"),
"Initial request was marked insecure.");
ok(redirectSecurityIcon.classList.contains("security-state-secure"),
"Redirected request was marked secure.");
yield teardown(monitor);
});

Просмотреть файл

@ -0,0 +1,99 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that correct security state indicator appears depending on the security
* state.
*/
add_task(function* () {
const EXPECTED_SECURITY_STATES = {
"test1.example.com": "security-state-insecure",
"example.com": "security-state-secure",
"nocert.example.com": "security-state-broken",
};
let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
let { $, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
yield performRequests();
for (let item of RequestsMenu.items) {
let domain = $(".requests-menu-domain", item.target).value;
info("Found a request to " + domain);
ok(domain in EXPECTED_SECURITY_STATES, "Domain " + domain + " was expected.");
let classes = $(".requests-security-state-icon", item.target).classList;
let expectedClass = EXPECTED_SECURITY_STATES[domain];
info("Classes of security state icon are: " + classes);
info("Security state icon is expected to contain class: " + expectedClass);
ok(classes.contains(expectedClass), "Icon contained the correct class name.");
}
yield teardown(monitor);
/**
* A helper that performs requests to
* - https://nocert.example.com (broken)
* - https://example.com (secure)
* - http://test1.example.com (insecure)
* and waits until NetworkMonitor has handled all packets sent by the server.
*/
function* performRequests() {
// waitForNetworkEvents does not work for requests with security errors as
// those only emit 9/13 events of a successful request.
let done = waitForSecurityBrokenNetworkEvent();
info("Requesting a resource that has a certificate problem.");
debuggee.performRequests(1, "https://nocert.example.com");
// Wait for the request to complete before firing another request. Otherwise
// the request with security issues interfere with waitForNetworkEvents.
info("Waiting for request to complete.");
yield done;
// Next perform a request over HTTP. If done the other way around the latter
// occasionally hangs waiting for event timings that don't seem to appear...
done = waitForNetworkEvents(monitor, 1);
info("Requesting a resource over HTTP.");
debuggee.performRequests(1, "http://test1.example.com");
yield done;
done = waitForNetworkEvents(monitor, 1);
info("Requesting a resource over HTTPS.");
debuggee.performRequests(1, "https://example.com");
yield done;
is(RequestsMenu.itemCount, 3, "Three events logged.");
}
/**
* Returns a promise that's resolved once a request with security issues is
* completed.
*/
function waitForSecurityBrokenNetworkEvent() {
let awaitedEvents = [
"UPDATING_REQUEST_HEADERS",
"RECEIVED_REQUEST_HEADERS",
"UPDATING_REQUEST_COOKIES",
"RECEIVED_REQUEST_COOKIES",
"STARTED_RECEIVING_RESPONSE",
"UPDATING_RESPONSE_CONTENT",
"RECEIVED_RESPONSE_CONTENT",
"UPDATING_EVENT_TIMINGS",
"RECEIVED_EVENT_TIMINGS",
];
let promises = awaitedEvents.map((event) => {
return monitor.panelWin.once(EVENTS[event]);
});
return Promise.all(promises);
}
});

Просмотреть файл

@ -0,0 +1,38 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that security details tab is no longer selected if an insecure request
* is selected.
*/
add_task(function* () {
let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
let { $, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu, NetworkDetails } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
info("Performing requests.");
debuggee.performRequests(1, "https://example.com");
debuggee.performRequests(1, "http://example.com");
yield waitForNetworkEvents(monitor, 2);
info("Selecting secure request.");
RequestsMenu.selectedIndex = 0;
info("Selecting security tab.");
NetworkDetails.widget.selectedIndex = 5;
info("Selecting insecure request.");
RequestsMenu.selectedIndex = 1;
info("Waiting for security tab to be updated.");
yield monitor.panelWin.once(EVENTS.NETWORKDETAILSVIEW_POPULATED);
is(NetworkDetails.widget.selectedIndex, 0,
"Selected tab was reset when selected security tab was hidden.");
yield teardown(monitor);
});

Просмотреть файл

@ -0,0 +1,111 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that security details tab is visible only when it should.
*/
add_task(function* () {
const TEST_DATA = [
{
desc: "http request",
uri: "http://example.com",
visibleOnNewEvent: false,
visibleOnSecurityInfo: false,
visibleOnceComplete: false,
}, {
desc: "working https request",
uri: "https://example.com",
visibleOnNewEvent: false,
visibleOnSecurityInfo: true,
visibleOnceComplete: true,
}, {
desc: "broken https request",
uri: "https://nocert.example.com",
isBroken: true,
visibleOnNewEvent: false,
visibleOnSecurityInfo: true,
visibleOnceComplete: true,
}
];
let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
let { $, EVENTS, NetMonitorView } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
for (let testcase of TEST_DATA) {
info("Testing Security tab visibility for " + testcase.desc);
let onNewItem = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
let onSecurityInfo = monitor.panelWin.once(EVENTS.RECEIVED_SECURITY_INFO);
let onComplete = testcase.isBroken ?
waitForSecurityBrokenNetworkEvent() :
waitForNetworkEvents(monitor, 1);
let tab = $("#security-tab");
info("Performing a request to " + testcase.uri);
debuggee.performRequests(1, testcase.uri);
info("Waiting for new network event.");
yield onNewItem;
info("Selecting the request.");
RequestsMenu.selectedIndex = 0;
is(RequestsMenu.selectedItem.attachment.securityState, undefined,
"Security state has not yet arrived.");
is(tab.hidden, !testcase.visibleOnNewEvent,
"Security tab is " +
(testcase.visibleOnNewEvent ? "visible" : "hidden") +
" after new request was added to the menu.");
info("Waiting for security information to arrive.");
yield onSecurityInfo;
ok(RequestsMenu.selectedItem.attachment.securityState,
"Security state arrived.");
is(tab.hidden, !testcase.visibleOnSecurityInfo,
"Security tab is " +
(testcase.visibleOnSecurityInfo ? "visible" : "hidden") +
" after security information arrived.");
info("Waiting for request to complete.");
yield onComplete;
is(tab.hidden, !testcase.visibleOnceComplete,
"Security tab is " +
(testcase.visibleOnceComplete ? "visible" : "hidden") +
" after request has been completed.");
info("Clearing requests.");
RequestsMenu.clear();
}
yield teardown(monitor);
/**
* Returns a promise that's resolved once a request with security issues is
* completed.
*/
function waitForSecurityBrokenNetworkEvent() {
let awaitedEvents = [
"UPDATING_REQUEST_HEADERS",
"RECEIVED_REQUEST_HEADERS",
"UPDATING_REQUEST_COOKIES",
"RECEIVED_REQUEST_COOKIES",
"STARTED_RECEIVING_RESPONSE",
"UPDATING_RESPONSE_CONTENT",
"RECEIVED_RESPONSE_CONTENT",
"UPDATING_EVENT_TIMINGS",
"RECEIVED_EVENT_TIMINGS",
];
let promises = awaitedEvents.map((event) => {
return monitor.panelWin.once(EVENTS[event]);
});
return Promise.all(promises);
}
});

Просмотреть файл

@ -43,6 +43,7 @@ const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";
const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs";
const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs";
const SORTING_SJS = EXAMPLE_URL + "sjs_sorting-test-server.sjs";
const HTTPS_REDIRECT_SJS = EXAMPLE_URL + "sjs_https-redirect-test-server.sjs";
const TEST_IMAGE = EXAMPLE_URL + "test-image.png";
const TEST_IMAGE_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";

Просмотреть файл

@ -0,0 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function handleRequest(request, response) {
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
if (request.scheme === "http") {
response.setStatusLine(request.httpVersion, 302, "Found");
response.setHeader("Location", "https://" + request.host + request.path);
} else {
response.setStatusLine(request.httpVersion, 200, "OK");
response.write("Page was accessed over HTTPS!");
}
}

Просмотреть файл

@ -80,6 +80,10 @@
- in the network details pane identifying the preview tab. -->
<!ENTITY netmonitorUI.tab.preview "Preview">
<!-- LOCALIZATION NOTE (netmonitorUI.tab.security): This is the label displayed
- in the network details pane identifying the security tab. -->
<!ENTITY netmonitorUI.tab.security "Security">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterAll): This is the label displayed
- in the network details footer for the "All" filtering button. -->
<!ENTITY netmonitorUI.footer.filterAll "All">
@ -198,6 +202,35 @@
- in a "receive" state. -->
<!ENTITY netmonitorUI.timings.receive "Receiving:">
<!-- LOCALIZATION NOTE (netmonitorUI.security.error): This is the label displayed
- in the security tab if a security error prevented the connection. -->
<!ENTITY netmonitorUI.security.error "An error occured:">
<!-- LOCALIZATION NOTE (netmonitorUI.security.protocolVersion): This is the label displayed
- in the security tab describing TLS/SSL protocol version. -->
<!ENTITY netmonitorUI.security.protocolVersion "Protocol version:">
<!-- LOCALIZATION NOTE (netmonitorUI.security.cipherSuite): This is the label displayed
- in the security tab describing the cipher suite used to secure this connection. -->
<!ENTITY netmonitorUI.security.cipherSuite "Cipher suite:">
<!-- LOCALIZATION NOTE (netmonitorUI.security.hsts): This is the label displayed
- in the security tab describing the usage of HTTP Strict Transport Security. -->
<!ENTITY netmonitorUI.security.hsts "HTTP Strict Transport Security:">
<!-- LOCALIZATION NOTE (netmonitorUI.security.hpkp): This is the label displayed
- in the security tab describing the usage of Public Key Pinning. -->
<!ENTITY netmonitorUI.security.hpkp "Public Key Pinning:">
<!-- LOCALIZATION NOTE (netmonitorUI.security.connection): This is the label displayed
- in the security tab describing the section containing information related to
- the secure connection. -->
<!ENTITY netmonitorUI.security.connection "Connection:">
<!-- LOCALIZATION NOTE (netmonitorUI.security.certificate): This is the label displayed
- in the security tab describing the server certificate section. -->
<!ENTITY netmonitorUI.security.certificate "Certificate:">
<!-- LOCALIZATION NOTE (netmonitorUI.context.perfTools): This is the label displayed
- on the context menu that shows the performance analysis tools -->
<!ENTITY netmonitorUI.context.perfTools "Start Performance Analysis…">

Просмотреть файл

@ -29,6 +29,46 @@ netmonitor.accesskey=N
# displayed inside the developer tools window.
netmonitor.tooltip=Network Monitor
# LOCALIZATION NOTE (netmonitor.security.state.secure)
# This string is used as an tooltip for request that was performed over secure
# channel i.e. the connection was encrypted.
netmonitor.security.state.secure=The connection used to fetch this resource was secure.
# LOCALIZATION NOTE (netmonitor.security.state.insecure)
# This string is used as an tooltip for request that was performed over insecure
# channel i.e. the connection was not encrypted.
netmonitor.security.state.insecure=The connection used to fetch this resource was not encrypted.
# LOCALIZATION NOTE (netmonitor.security.state.broken)
# This string is used as an tooltip for request that failed due to security
# issues.
netmonitor.security.state.broken=A security error prevented the resource from being loaded.
# LOCALIZATION NOTE (netmonitor.security.enabled):
# This string is used to indicate that a specific security feature is used by
# a connection in the security details tab.
# For example: "HTTP Strict Transport Security: Enabled"
netmonitor.security.enabled=Enabled
# LOCALIZATION NOTE (netmonitor.security.disabled):
# This string is used to indicate that a specific security feature is not used by
# a connection in the security details tab.
# For example: "HTTP Strict Transport Security: Disabled"
netmonitor.security.disabled=Disabled
# LOCALIZATION NOTE (netmonitor.security.hostHeader):
# This string is used as a header for section containing security information
# related to the remote host. %S is replaced with the domain name of the remote
# host. For example: Host example.com
netmonitor.security.hostHeader=Host %S:
# LOCALIZATION NOTE (netmonitor.security.notAvailable):
# This string is used to indicate that a certain piece of information is not
# available to be displayd. For example a certificate that has no organization
# defined:
# Organization: <Not Available>
netmonitor.security.notAvailable=<Not Available>
# LOCALIZATION NOTE (collapseDetailsPane): This is the tooltip for the button
# that collapses the network details pane in the UI.
collapseDetailsPane=Hide request details

Просмотреть файл

@ -16,7 +16,7 @@
width: 9em;
}
.requests-menu-domain {
.requests-menu-security-and-domain {
width: 16vw;
}

Просмотреть файл

@ -151,11 +151,38 @@
text-align: start;
}
.requests-menu-domain {
.requests-menu-security-and-domain {
width: 14vw;
min-width: 10em;
}
.requests-security-state-icon {
-moz-margin-end: 4px;
-moz-image-region:rect(0px, 16px, 16px, 0px);
}
.requests-security-state-icon:hover {
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.requests-security-state-icon:active {
-moz-image-region: rect(0px, 48px, 16px, 32px);
}
.security-state-insecure {
list-style-image: url(chrome://browser/skin/identity-icons-generic.png);
}
.security-state-secure {
cursor: pointer;
list-style-image: url(chrome://browser/skin/identity-icons-https.png);
}
.security-state-broken {
cursor: pointer;
list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-active.png);
}
.requests-menu-type {
text-align: center;
width: 4em;
@ -538,6 +565,19 @@ label.requests-menu-status-code {
transition: transform 0.2s ease-out;
}
/* Security tabpanel */
.security-info-section {
-moz-padding-start: 1em;
}
#security-tabpanel {
overflow: auto;
}
#security-error-message {
white-space: pre-wrap;
}
/* Custom request form */
#custom-pane {
@ -783,7 +823,7 @@ label.requests-menu-status-code {
width: 30vw;
}
.requests-menu-domain {
.requests-menu-security-and-domain {
width: 30vw;
}