Bug 932179 - Part 1: Expose security information in the WebConsoleActor. r=past

This commit is contained in:
Sami Jaktholm 2015-01-06 02:58:00 -05:00
Родитель 853e2fb5f6
Коммит b53cf7a775
15 изменённых файлов: 789 добавлений и 2 удалений

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

@ -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]