зеркало из https://github.com/mozilla/gecko-dev.git
Bug 932179 - Part 1: Expose security information in the WebConsoleActor. r=past
This commit is contained in:
Родитель
853e2fb5f6
Коммит
b53cf7a775
|
@ -1755,6 +1755,20 @@ NetworkEventActor.prototype =
|
|||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* The "getSecurityInfo" packet type handler.
|
||||
*
|
||||
* @return object
|
||||
* The response packet - connection security information.
|
||||
*/
|
||||
onGetSecurityInfo: function NEA_onGetSecurityInfo()
|
||||
{
|
||||
return {
|
||||
from: this.actorID,
|
||||
securityInfo: this._securityInfo,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* The "getResponseHeaders" packet type handler.
|
||||
*
|
||||
|
@ -1910,6 +1924,26 @@ NetworkEventActor.prototype =
|
|||
this.conn.send(packet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add connection security information.
|
||||
*
|
||||
* @param object info
|
||||
* The object containing security information.
|
||||
*/
|
||||
addSecurityInfo: function NEA_addSecurityInfo(info)
|
||||
{
|
||||
this._securityInfo = info;
|
||||
|
||||
let packet = {
|
||||
from: this.actorID,
|
||||
type: "networkEventUpdate",
|
||||
updateType: "securityInfo",
|
||||
state: info.state,
|
||||
};
|
||||
|
||||
this.conn.send(packet);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add network response headers.
|
||||
*
|
||||
|
@ -2034,4 +2068,5 @@ NetworkEventActor.prototype.requestTypes =
|
|||
"getResponseCookies": NetworkEventActor.prototype.onGetResponseCookies,
|
||||
"getResponseContent": NetworkEventActor.prototype.onGetResponseContent,
|
||||
"getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
|
||||
"getSecurityInfo": NetworkEventActor.prototype.onGetSecurityInfo,
|
||||
};
|
||||
|
|
|
@ -366,6 +366,23 @@ WebConsoleClient.prototype = {
|
|||
this._client.request(packet, aOnResponse);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the security information for the given NetworkEventActor.
|
||||
*
|
||||
* @param string aActor
|
||||
* The NetworkEventActor ID.
|
||||
* @param function aOnResponse
|
||||
* The function invoked when the response is received.
|
||||
*/
|
||||
getSecurityInfo: function WCC_getSecurityInfo(aActor, aOnResponse)
|
||||
{
|
||||
let packet = {
|
||||
to: aActor,
|
||||
type: "getSecurityInfo",
|
||||
};
|
||||
this._client.request(packet, aOnResponse);
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a HTTP request with the given data.
|
||||
*
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
|
||||
const {components, Cc, Ci, Cu} = require("chrome");
|
||||
loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
|
||||
loader.lazyImporter(this, "DevToolsUtils", "resource://gre/modules/devtools/DevToolsUtils.jsm");
|
||||
|
||||
/**
|
||||
* Helper object for networking stuff.
|
||||
|
@ -480,6 +481,207 @@ let NetworkHelper = {
|
|||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes a securityInfo object of nsIRequest, the nsIRequest itself and
|
||||
* extracts security information from them.
|
||||
*
|
||||
* @param object securityInfo
|
||||
* The securityInfo object of a request. If null channel is assumed
|
||||
* to be insecure.
|
||||
* @param nsIRequest request
|
||||
* The nsIRequest object for the request used to dig more information
|
||||
* about this request.
|
||||
*
|
||||
* @return object
|
||||
* Returns an object containing following members:
|
||||
* - state: The security of the connection used to fetch this
|
||||
* request. Has one of following string values:
|
||||
* * "insecure": the connection was not secure (only http)
|
||||
* * "broken": secure connection failed (e.g. expired cert)
|
||||
* * "secure": the connection was properly secured.
|
||||
* If state == broken:
|
||||
* - errorMessage: full error message from nsITransportSecurityInfo.
|
||||
* If state == secure:
|
||||
* - protocolVersion: one of SSLv3, TLSv1, TLSv1.1, TLSv1.2.
|
||||
* - cipherSuite: the cipher suite used in this connection.
|
||||
* - cert: information about certificate used in this connection.
|
||||
* See parseCertificateInfo for the contents.
|
||||
* - hsts: true if host uses Strict Transport Security, false otherwise
|
||||
* - hpkp: true if host uses Public Key Pinning, false otherwise
|
||||
*/
|
||||
parseSecurityInfo: function NH_parseSecurityInfo(securityInfo, request) {
|
||||
const info = {
|
||||
state: "insecure",
|
||||
};
|
||||
|
||||
// The request did not contain any security info.
|
||||
if (!securityInfo) {
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Different scenarios to consider here and how they are handled:
|
||||
* - request is HTTP, the connection is not secure
|
||||
* => securityInfo is null
|
||||
* => state === "insecure"
|
||||
*
|
||||
* - request is HTTPS, the connection is secure
|
||||
* => .securityState has STATE_IS_SECURE flag
|
||||
* => state === "secure"
|
||||
*
|
||||
* - request is HTTPS, the connection has security issues
|
||||
* => .securityState has STATE_IS_INSECURE flag
|
||||
* => .errorCode is an NSS error code.
|
||||
* => state === "broken"
|
||||
*
|
||||
* - request is HTTPS, the connection was terminated before the security
|
||||
* could be validated
|
||||
* => .securityState has STATE_IS_INSECURE flag
|
||||
* => .errorCode is NOT an NSS error code.
|
||||
* => .errorMessage is not available.
|
||||
* => state === "insecure"
|
||||
*
|
||||
* - request is HTTPS but it uses a weak cipher or old protocol, see
|
||||
* http://hg.mozilla.org/mozilla-central/annotate/def6ed9d1c1a/
|
||||
* security/manager/ssl/src/nsNSSCallbacks.cpp#l1233
|
||||
* - request is mixed content (which makes no sense whatsoever)
|
||||
* => .securityState has STATE_IS_BROKEN flag
|
||||
* => .errorCode is NOT an NSS error code
|
||||
* => .errorMessage is not available
|
||||
* => state === "insecure"
|
||||
*/
|
||||
|
||||
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
|
||||
securityInfo.QueryInterface(Ci.nsISSLStatusProvider);
|
||||
|
||||
const wpl = Ci.nsIWebProgressListener;
|
||||
const NSSErrorsService = Cc['@mozilla.org/nss_errors_service;1']
|
||||
.getService(Ci.nsINSSErrorsService);
|
||||
const SSLStatus = securityInfo.SSLStatus;
|
||||
|
||||
if (securityInfo.securityState & wpl.STATE_IS_SECURE) {
|
||||
// The connection is secure.
|
||||
info.state = "secure";
|
||||
|
||||
// Cipher suite.
|
||||
info.cipherSuite = SSLStatus.cipherName;
|
||||
|
||||
// Protocol version.
|
||||
info.protocolVersion = this.formatSecurityProtocol(SSLStatus.protocolVersion);
|
||||
|
||||
// Certificate.
|
||||
info.cert = this.parseCertificateInfo(SSLStatus.serverCert);
|
||||
|
||||
// HSTS and HPKP if available.
|
||||
if (request.URI) {
|
||||
const sss = Cc["@mozilla.org/ssservice;1"]
|
||||
.getService(Ci.nsISiteSecurityService);
|
||||
|
||||
request.QueryInterface(Ci.nsIPrivateBrowsingChannel);
|
||||
|
||||
// SiteSecurityService uses different storage if the channel is
|
||||
// private. Thus we must give isSecureHost correct flags or we
|
||||
// might get incorrect results.
|
||||
let flags = (request.isChannelPrivate) ?
|
||||
Ci.nsISocketProvider.NO_PERMANENT_STORAGE : 0;
|
||||
|
||||
let host = request.URI.host;
|
||||
|
||||
info.hsts = sss.isSecureHost(sss.HEADER_HSTS, host, flags);
|
||||
info.hpkp = sss.isSecureHost(sss.HEADER_HPKP, host, flags);
|
||||
} else {
|
||||
DevToolsUtils.reportException("NetworkHelper.parseSecurityInfo",
|
||||
"Could not get HSTS/HPKP status as request.URI not available.");
|
||||
info.hsts = false;
|
||||
info.hpkp = false;
|
||||
}
|
||||
|
||||
} else if (NSSErrorsService.isNSSErrorCode(securityInfo.errorCode)) {
|
||||
// The connection failed.
|
||||
info.state = "broken";
|
||||
info.errorMessage = securityInfo.errorMessage;
|
||||
} else {
|
||||
// Connection has securityInfo, it is not secure and there's no problems
|
||||
// to report. Mark the request as insecure.
|
||||
return info;
|
||||
}
|
||||
|
||||
return info;
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an nsIX509Cert and returns an object with certificate information.
|
||||
*
|
||||
* @param nsIX509Cert cert
|
||||
* The certificate to extract the information from.
|
||||
* @return object
|
||||
* An object with following format:
|
||||
* {
|
||||
* subject: { commonName, organization, organizationalUnit },
|
||||
* issuer: { commonName, organization, organizationUnit },
|
||||
* validity: { start, end },
|
||||
* fingerprint: { sha1, sha256 }
|
||||
* }
|
||||
*/
|
||||
parseCertificateInfo: function NH_parseCertifificateInfo(cert) {
|
||||
let info = {};
|
||||
if (cert) {
|
||||
info.subject = {
|
||||
commonName: cert.commonName,
|
||||
organization: cert.organization,
|
||||
organizationalUnit: cert.organizationalUnit,
|
||||
};
|
||||
|
||||
info.issuer = {
|
||||
commonName: cert.issuerCommonName,
|
||||
organization: cert.issuerOrganization,
|
||||
organizationUnit: cert.issuerOrganizationUnit,
|
||||
};
|
||||
|
||||
info.validity = {
|
||||
start: cert.validity.notBeforeLocalDay,
|
||||
end: cert.validity.notAfterLocalDay,
|
||||
};
|
||||
|
||||
info.fingerprint = {
|
||||
sha1: cert.sha1Fingerprint,
|
||||
sha256: cert.sha256Fingerprint,
|
||||
};
|
||||
} else {
|
||||
DevToolsUtils.reportException("NetworkHelper.parseCertificateInfo",
|
||||
"Secure connection established without certificate.");
|
||||
}
|
||||
|
||||
return info;
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes protocolVersion of SSLStatus object and returns human readable
|
||||
* description.
|
||||
*
|
||||
* @param Number version
|
||||
* One of nsISSLStatus version constants.
|
||||
* @return string
|
||||
* One of SSLv3, TLSv1, TLSv1.1, TLSv1.2 if @param version is valid,
|
||||
* Unknown otherwise.
|
||||
*/
|
||||
formatSecurityProtocol: function NH_formatSecurityProtocol(version) {
|
||||
switch (version) {
|
||||
case Ci.nsISSLStatus.SSL_VERSION_3:
|
||||
return "SSLv3";
|
||||
case Ci.nsISSLStatus.TLS_VERSION_1:
|
||||
return "TLSv1";
|
||||
case Ci.nsISSLStatus.TLS_VERSION_1_1:
|
||||
return "TLSv1.1";
|
||||
case Ci.nsISSLStatus.TLS_VERSION_1_2:
|
||||
return "TLSv1.2";
|
||||
default:
|
||||
DevToolsUtils.reportException("NetworkHelper.formatSecurityProtocol",
|
||||
"protocolVersion " + version + " is unknown.");
|
||||
return "Unknown";
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
for (let prop of Object.getOwnPropertyNames(NetworkHelper)) {
|
||||
|
|
|
@ -205,11 +205,26 @@ NetworkResponseListener.prototype = {
|
|||
onStartRequest: function NRL_onStartRequest(aRequest)
|
||||
{
|
||||
this.request = aRequest;
|
||||
this._getSecurityInfo();
|
||||
this._findOpenResponse();
|
||||
// Asynchronously wait for the data coming from the request.
|
||||
this.setAsyncListener(this.sink.inputStream, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse security state of this request and report it to the client.
|
||||
*/
|
||||
_getSecurityInfo: DevToolsUtils.makeInfallible(function NRL_getSecurityInfo() {
|
||||
// Take the security information from the original nsIHTTPChannel instead of
|
||||
// the nsIRequest received in onStartRequest. If response to this request
|
||||
// was a redirect from http to https, the request object seems to contain
|
||||
// security info for the https request after redirect.
|
||||
let secinfo = this.httpActivity.channel.securityInfo;
|
||||
let info = NetworkHelper.parseSecurityInfo(secinfo, this.request);
|
||||
|
||||
this.httpActivity.owner.addSecurityInfo(info);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Handle the onStopRequest by closing the sink output stream.
|
||||
*
|
||||
|
@ -1224,8 +1239,8 @@ NetworkEventActorProxy.prototype = {
|
|||
(function() {
|
||||
// Listeners for new network event data coming from the NetworkMonitor.
|
||||
let methods = ["addRequestHeaders", "addRequestCookies", "addRequestPostData",
|
||||
"addResponseStart", "addResponseHeaders", "addResponseCookies",
|
||||
"addResponseContent", "addEventTimings"];
|
||||
"addResponseStart", "addSecurityInfo", "addResponseHeaders",
|
||||
"addResponseCookies", "addResponseContent", "addEventTimings"];
|
||||
let factory = NetworkEventActorProxy.methodFactory;
|
||||
for (let method of methods) {
|
||||
NetworkEventActorProxy.prototype[method] = factory(method);
|
||||
|
|
|
@ -17,6 +17,8 @@ support-files =
|
|||
[test_network_get.html]
|
||||
[test_network_longstring.html]
|
||||
[test_network_post.html]
|
||||
[test_network_security-hpkp.html]
|
||||
[test_network_security-hsts.html]
|
||||
[test_nsiconsolemessage.html]
|
||||
[test_object_actor.html]
|
||||
[test_object_actor_native_getters.html]
|
||||
|
|
|
@ -91,6 +91,11 @@ function onNetworkEventUpdate(aState, aType, aPacket)
|
|||
},
|
||||
};
|
||||
break;
|
||||
case "securityInfo":
|
||||
expectedPacket = {
|
||||
state: "insecure",
|
||||
};
|
||||
break;
|
||||
case "responseCookies":
|
||||
expectedPacket = {
|
||||
cookies: 0,
|
||||
|
|
|
@ -114,6 +114,11 @@ function onNetworkEventUpdate(aState, aType, aPacket)
|
|||
},
|
||||
};
|
||||
break;
|
||||
case "securityInfo":
|
||||
expectedPacket = {
|
||||
state: "insecure",
|
||||
};
|
||||
break;
|
||||
case "responseCookies":
|
||||
expectedPacket = {
|
||||
cookies: 0,
|
||||
|
|
|
@ -107,6 +107,11 @@ function onNetworkEventUpdate(aState, aType, aPacket)
|
|||
},
|
||||
};
|
||||
break;
|
||||
case "securityInfo":
|
||||
expectedPacket = {
|
||||
state: "insecure",
|
||||
};
|
||||
break;
|
||||
case "responseCookies":
|
||||
expectedPacket = {
|
||||
cookies: 0,
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Test for the network actor (HPKP detection)</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="common.js"></script>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<p>Test for the network actor (HPKP detection)</p>
|
||||
|
||||
<iframe src="https://example.com/chrome/toolkit/devtools/webconsole/test/network_requests_iframe.html"></iframe>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.8">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let gCurrentTestCase = -1;
|
||||
const HPKP_PREF = "security.cert_pinning.process_headers_from_non_builtin_roots";
|
||||
const TEST_CASES = [
|
||||
{
|
||||
desc: "no Public Key Pinning",
|
||||
url: "https://example.com",
|
||||
usesPinning: false,
|
||||
},
|
||||
{
|
||||
desc: "static Public Key Pinning",
|
||||
url: "https://include-subdomains.pinning.example.com/",
|
||||
usesPinning: true,
|
||||
},
|
||||
{
|
||||
desc: "dynamic Public Key Pinning with this request",
|
||||
url: "https://include-subdomains.pinning-dynamic.example.com/" +
|
||||
"browser/browser/base/content/test/general/pinning_headers.sjs",
|
||||
usesPinning: true,
|
||||
},
|
||||
{
|
||||
desc: "dynamic Public Key Pinning with previous request",
|
||||
url: "https://include-subdomains.pinning-dynamic.example.com/",
|
||||
usesPinning: true,
|
||||
}
|
||||
];
|
||||
|
||||
function startTest()
|
||||
{
|
||||
// Need to enable this pref or pinning headers are rejected due test
|
||||
// certificate.
|
||||
Services.prefs.setBoolPref(HPKP_PREF, true);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref(HPKP_PREF, false);
|
||||
|
||||
// Reset pinning state.
|
||||
let gSSService = Cc["@mozilla.org/ssservice;1"]
|
||||
.getService(Ci.nsISiteSecurityService);
|
||||
|
||||
let gIOService = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
for (let {url} of TEST_CASES) {
|
||||
let uri = gIOService.newURI(url, null, null);
|
||||
gSSService.removeState(Ci.nsISiteSecurityService.HEADER_HPKP, uri, 0);
|
||||
}
|
||||
});
|
||||
|
||||
info("Test detection of Public Key Pinning.");
|
||||
removeEventListener("load", startTest);
|
||||
attachConsole(["NetworkActivity"], onAttach, true);
|
||||
}
|
||||
|
||||
function onAttach(aState, aResponse)
|
||||
{
|
||||
onNetworkEventUpdate = onNetworkEventUpdate.bind(null, aState);
|
||||
aState.dbgClient.addListener("networkEventUpdate", onNetworkEventUpdate);
|
||||
|
||||
runNextCase(aState);
|
||||
}
|
||||
|
||||
function runNextCase(aState) {
|
||||
gCurrentTestCase++;
|
||||
if (gCurrentTestCase === TEST_CASES.length) {
|
||||
info("Tests ran. Cleaning up.");
|
||||
closeDebugger(aState, SimpleTest.finish);
|
||||
return;
|
||||
}
|
||||
|
||||
let { desc, url } = TEST_CASES[gCurrentTestCase];
|
||||
info("Testing site with " + desc);
|
||||
|
||||
let iframe = document.querySelector("iframe").contentWindow;
|
||||
iframe.wrappedJSObject.makeXhrCallback("GET", url);
|
||||
}
|
||||
|
||||
function onNetworkEventUpdate(aState, aType, aPacket)
|
||||
{
|
||||
function onSecurityInfo(packet) {
|
||||
let data = TEST_CASES[gCurrentTestCase];
|
||||
is(packet.securityInfo.hpkp, data.usesPinning,
|
||||
"Public Key Pinning detected correctly.");
|
||||
|
||||
runNextCase(aState);
|
||||
}
|
||||
|
||||
if (aPacket.updateType === "securityInfo") {
|
||||
aState.client.getSecurityInfo(aPacket.from, onSecurityInfo);
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener("load", startTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,100 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Test for the network actor (HSTS detection)</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="common.js"></script>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<p>Test for the network actor (HSTS detection)</p>
|
||||
|
||||
<iframe src="https://example.com/chrome/toolkit/devtools/webconsole/test/network_requests_iframe.html"></iframe>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.8">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let gCurrentTestCase = -1;
|
||||
const TEST_CASES = [
|
||||
{
|
||||
desc: "no HSTS",
|
||||
url: "https://example.com",
|
||||
usesHSTS: false,
|
||||
},
|
||||
{
|
||||
desc: "HSTS from this response",
|
||||
url: "https://example.com/"+
|
||||
"browser/browser/base/content/test/general/browser_star_hsts.sjs",
|
||||
usesHSTS: true,
|
||||
},
|
||||
{
|
||||
desc: "stored HSTS from previous response",
|
||||
url: "https://example.com/",
|
||||
usesHSTS: true,
|
||||
}
|
||||
];
|
||||
|
||||
function startTest()
|
||||
{
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
// Reset HSTS state.
|
||||
let gSSService = Cc["@mozilla.org/ssservice;1"]
|
||||
.getService(Ci.nsISiteSecurityService);
|
||||
|
||||
let gIOService = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
|
||||
let uri = gIOService.newURI(TEST_CASES[0].url, null, null);
|
||||
gSSService.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0);
|
||||
});
|
||||
|
||||
info("Test detection of HTTP Strict Transport Security.");
|
||||
removeEventListener("load", startTest);
|
||||
attachConsole(["NetworkActivity"], onAttach, true);
|
||||
}
|
||||
|
||||
function onAttach(aState, aResponse)
|
||||
{
|
||||
onNetworkEventUpdate = onNetworkEventUpdate.bind(null, aState);
|
||||
aState.dbgClient.addListener("networkEventUpdate", onNetworkEventUpdate);
|
||||
|
||||
runNextCase(aState);
|
||||
}
|
||||
|
||||
function runNextCase(aState) {
|
||||
gCurrentTestCase++;
|
||||
if (gCurrentTestCase === TEST_CASES.length) {
|
||||
info("Tests ran. Cleaning up.");
|
||||
closeDebugger(aState, SimpleTest.finish);
|
||||
return;
|
||||
}
|
||||
|
||||
let { desc, url } = TEST_CASES[gCurrentTestCase];
|
||||
info("Testing site with " + desc);
|
||||
|
||||
let iframe = document.querySelector("iframe").contentWindow;
|
||||
iframe.wrappedJSObject.makeXhrCallback("GET", url);
|
||||
}
|
||||
|
||||
function onNetworkEventUpdate(aState, aType, aPacket)
|
||||
{
|
||||
function onSecurityInfo(packet) {
|
||||
let data = TEST_CASES[gCurrentTestCase];
|
||||
is(packet.securityInfo.hsts, data.usesHSTS,
|
||||
"Strict Transport Security detected correctly.");
|
||||
|
||||
runNextCase(aState);
|
||||
}
|
||||
|
||||
if (aPacket.updateType === "securityInfo") {
|
||||
aState.client.getSecurityInfo(aPacket.from, onSecurityInfo);
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener("load", startTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,68 @@
|
|||
/* 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";
|
||||
|
||||
// Tests that NetworkHelper.parseCertificateInfo parses certificate information
|
||||
// correctly.
|
||||
|
||||
const { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
|
||||
Object.defineProperty(this, "NetworkHelper", {
|
||||
get: function() {
|
||||
return devtools.require("devtools/toolkit/webconsole/network-helper");
|
||||
},
|
||||
configurable: true,
|
||||
writeable: false,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const DUMMY_CERT = {
|
||||
commonName: "cn",
|
||||
organization: "o",
|
||||
organizationalUnit: "ou",
|
||||
issuerCommonName: "issuerCN",
|
||||
issuerOrganization: "issuerO",
|
||||
issuerOrganizationUnit: "issuerOU",
|
||||
sha256Fingerprint: "qwertyuiopoiuytrewq",
|
||||
sha1Fingerprint: "qwertyuiop",
|
||||
validity: {
|
||||
notBeforeLocalDay: "yesterday",
|
||||
notAfterLocalDay: "tomorrow",
|
||||
}
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
do_print("Testing NetworkHelper.parseCertificateInfo.");
|
||||
|
||||
let result = NetworkHelper.parseCertificateInfo(DUMMY_CERT);
|
||||
|
||||
// Subject
|
||||
equal(result.subject.commonName, DUMMY_CERT.commonName,
|
||||
"Common name is correct.");
|
||||
equal(result.subject.organization, DUMMY_CERT.organization,
|
||||
"Organization is correct.");
|
||||
equal(result.subject.organizationalUnit, DUMMY_CERT.organizationalUnit,
|
||||
"Organizational unit is correct.");
|
||||
|
||||
// Issuer
|
||||
equal(result.issuer.commonName, DUMMY_CERT.issuerCommonName,
|
||||
"Common name of the issuer is correct.");
|
||||
equal(result.issuer.organization, DUMMY_CERT.issuerOrganization,
|
||||
"Organization of the issuer is correct.");
|
||||
equal(result.issuer.organizationalUnit, DUMMY_CERT.issuerOrganizationalUnit,
|
||||
"Organizational unit of the issuer is correct.");
|
||||
|
||||
// Validity
|
||||
equal(result.validity.start, DUMMY_CERT.validity.notBeforeLocalDay,
|
||||
"Start of the validity period is correct.");
|
||||
equal(result.validity.end, DUMMY_CERT.validity.notAfterLocalDay,
|
||||
"End of the validity period is correct.");
|
||||
|
||||
// Fingerprints
|
||||
equal(result.fingerprint.sha1, DUMMY_CERT.sha1Fingerprint,
|
||||
"Certificate SHA1 fingerprint is correct.");
|
||||
equal(result.fingerprint.sha256, DUMMY_CERT.sha256Fingerprint,
|
||||
"Certificate SHA256 fingerprint is correct.");
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* 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 NetworkHelper.parseSecurityInfo returns correctly formatted object.
|
||||
|
||||
const { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
Object.defineProperty(this, "NetworkHelper", {
|
||||
get: function() {
|
||||
return devtools.require("devtools/toolkit/webconsole/network-helper");
|
||||
},
|
||||
configurable: true,
|
||||
writeable: false,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const wpl = Ci.nsIWebProgressListener;
|
||||
const MockCertificate = {
|
||||
commonName: "cn",
|
||||
organization: "o",
|
||||
organizationalUnit: "ou",
|
||||
issuerCommonName: "issuerCN",
|
||||
issuerOrganization: "issuerO",
|
||||
issuerOrganizationUnit: "issuerOU",
|
||||
sha256Fingerprint: "qwertyuiopoiuytrewq",
|
||||
sha1Fingerprint: "qwertyuiop",
|
||||
validity: {
|
||||
notBeforeLocalDay: "yesterday",
|
||||
notAfterLocalDay: "tomorrow",
|
||||
}
|
||||
};
|
||||
|
||||
const MockSecurityInfo = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITransportSecurityInfo,
|
||||
Ci.nsISSLStatusProvider]),
|
||||
securityState: wpl.STATE_IS_SECURE,
|
||||
errorCode: 0,
|
||||
SSLStatus: {
|
||||
cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
protocolVersion: 3, // TLS_VERSION_1_2
|
||||
serverCert: MockCertificate,
|
||||
}
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
|
||||
|
||||
equal(result.state, "secure", "State is correct.");
|
||||
|
||||
equal(result.cipherSuite, MockSecurityInfo.cipherSuite,
|
||||
"Cipher suite is correct.");
|
||||
|
||||
equal(result.protocolVersion, "TLSv1.2", "Protocol version is correct.");
|
||||
|
||||
deepEqual(result.cert, NetworkHelper.parseCertificateInfo(MockCertificate),
|
||||
"Certificate information is correct.");
|
||||
|
||||
equal(result.hpkp, false, "HPKP is false when URI is not available.");
|
||||
equal(result.hsts, false, "HSTS is false when URI is not available.");
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* 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";
|
||||
|
||||
// Tests that NetworkHelper.formatSecurityProtocol returns correct
|
||||
// protocol version strings.
|
||||
|
||||
const { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
|
||||
Object.defineProperty(this, "NetworkHelper", {
|
||||
get: function() {
|
||||
return devtools.require("devtools/toolkit/webconsole/network-helper");
|
||||
},
|
||||
configurable: true,
|
||||
writeable: false,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const TEST_CASES = [
|
||||
{
|
||||
description: "SSL_VERSION_3",
|
||||
input: 0,
|
||||
expected: "SSLv3"
|
||||
}, {
|
||||
description: "TLS_VERSION_1",
|
||||
input: 1,
|
||||
expected: "TLSv1"
|
||||
}, {
|
||||
description: "TLS_VERSION_1.1",
|
||||
input: 2,
|
||||
expected: "TLSv1.1"
|
||||
}, {
|
||||
description: "TLS_VERSION_1.2",
|
||||
input: 3,
|
||||
expected: "TLSv1.2"
|
||||
}, {
|
||||
description: "invalid version",
|
||||
input: -1,
|
||||
expected: "Unknown"
|
||||
},
|
||||
];
|
||||
|
||||
function run_test() {
|
||||
do_print("Testing NetworkHelper.formatSecurityProtocol.");
|
||||
|
||||
for (let {description, input, expected} of TEST_CASES) {
|
||||
do_print("Testing " + description);
|
||||
|
||||
equal(NetworkHelper.formatSecurityProtocol(input), expected,
|
||||
"Got the expected protocol string.");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/* 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";
|
||||
|
||||
// Tests that security info parser gives correct general security state for
|
||||
// different cases.
|
||||
|
||||
const { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
Object.defineProperty(this, "NetworkHelper", {
|
||||
get: function() {
|
||||
return devtools.require("devtools/toolkit/webconsole/network-helper");
|
||||
},
|
||||
configurable: true,
|
||||
writeable: false,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const wpl = Ci.nsIWebProgressListener;
|
||||
const MockSecurityInfo = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITransportSecurityInfo,
|
||||
Ci.nsISSLStatusProvider]),
|
||||
securityState: wpl.STATE_IS_BROKEN,
|
||||
errorCode: 0,
|
||||
SSLStatus: {
|
||||
protocolVersion: 3, // nsISSLStatus.TLS_VERSION_1_2
|
||||
cipherSuite: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
}
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
test_nullSecurityInfo();
|
||||
test_insecureSecurityInfoWithNSSError();
|
||||
test_insecureSecurityInfoWithoutNSSError();
|
||||
test_brokenSecurityInfo();
|
||||
test_secureSecurityInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that undefined security information is returns "insecure".
|
||||
*/
|
||||
function test_nullSecurityInfo() {
|
||||
let result = NetworkHelper.parseSecurityInfo(null, {});
|
||||
equal(result.state, "insecure",
|
||||
"state == 'insecure' when securityInfo was undefined");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that STATE_IS_INSECURE with NSSError returns "broken"
|
||||
*/
|
||||
function test_insecureSecurityInfoWithNSSError() {
|
||||
MockSecurityInfo.securityState = wpl.STATE_IS_INSECURE;
|
||||
|
||||
// Taken from security/manager/ssl/tests/unit/head_psm.js.
|
||||
MockSecurityInfo.errorCode = -8180;
|
||||
|
||||
let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
|
||||
equal(result.state, "broken",
|
||||
"state == 'broken' if securityState contains STATE_IS_INSECURE flag AND " +
|
||||
"errorCode is NSS error.");
|
||||
|
||||
MockSecurityInfo.errorCode = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that STATE_IS_INSECURE without NSSError returns "insecure"
|
||||
*/
|
||||
function test_insecureSecurityInfoWithoutNSSError() {
|
||||
MockSecurityInfo.securityState = wpl.STATE_IS_INSECURE;
|
||||
|
||||
let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
|
||||
equal(result.state, "insecure",
|
||||
"state == 'insecure' if securityState contains STATE_IS_INSECURE flag BUT " +
|
||||
"errorCode is not NSS error.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that STATE_IS_SECURE returns "secure"
|
||||
*/
|
||||
function test_secureSecurityInfo() {
|
||||
MockSecurityInfo.securityState = wpl.STATE_IS_SECURE;
|
||||
|
||||
let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
|
||||
equal(result.state, "secure",
|
||||
"state == 'secure' if securityState contains STATE_IS_SECURE flag");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that STATE_IS_BROKEN returns "insecure"
|
||||
*/
|
||||
function test_brokenSecurityInfo() {
|
||||
MockSecurityInfo.securityState = wpl.STATE_IS_BROKEN;
|
||||
|
||||
let result = NetworkHelper.parseSecurityInfo(MockSecurityInfo, {});
|
||||
equal(result.state, "insecure",
|
||||
"state == 'insecure' if securityState contains STATE_IS_BROKEN flag");
|
||||
}
|
|
@ -6,3 +6,7 @@ support-files =
|
|||
|
||||
[test_js_property_provider.js]
|
||||
[test_network_helper.js]
|
||||
[test_security-info-certificate.js]
|
||||
[test_security-info-parser.js]
|
||||
[test_security-info-protocol-version.js]
|
||||
[test_security-info-state.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче