diff options
Diffstat (limited to 'devtools/shared/network-observer/test/xpcshell')
10 files changed, 664 insertions, 0 deletions
diff --git a/devtools/shared/network-observer/test/xpcshell/head.js b/devtools/shared/network-observer/test/xpcshell/head.js new file mode 100644 index 0000000000..93b66e4632 --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/head.js @@ -0,0 +1,9 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + NetworkHelper: + "resource://devtools/shared/network-observer/NetworkHelper.sys.mjs", +}); diff --git a/devtools/shared/network-observer/test/xpcshell/test_network_helper.js b/devtools/shared/network-observer/test/xpcshell/test_network_helper.js new file mode 100644 index 0000000000..ff514ab98e --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/test_network_helper.js @@ -0,0 +1,90 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function run_test() { + test_isTextMimeType(); + test_parseCookieHeader(); +} + +function test_isTextMimeType() { + Assert.equal(NetworkHelper.isTextMimeType("text/plain"), true); + Assert.equal(NetworkHelper.isTextMimeType("application/javascript"), true); + Assert.equal(NetworkHelper.isTextMimeType("application/json"), true); + Assert.equal(NetworkHelper.isTextMimeType("text/css"), true); + Assert.equal(NetworkHelper.isTextMimeType("text/html"), true); + Assert.equal(NetworkHelper.isTextMimeType("image/svg+xml"), true); + Assert.equal(NetworkHelper.isTextMimeType("application/xml"), true); + + // Test custom JSON subtype + Assert.equal( + NetworkHelper.isTextMimeType("application/vnd.tent.posts-feed.v0+json"), + true + ); + Assert.equal( + NetworkHelper.isTextMimeType("application/vnd.tent.posts-feed.v0-json"), + true + ); + // Test custom XML subtype + Assert.equal( + NetworkHelper.isTextMimeType("application/vnd.tent.posts-feed.v0+xml"), + true + ); + Assert.equal( + NetworkHelper.isTextMimeType("application/vnd.tent.posts-feed.v0-xml"), + false + ); + // Test case-insensitive + Assert.equal( + NetworkHelper.isTextMimeType("application/vnd.BIG-CORP+json"), + true + ); + // Test non-text type + Assert.equal(NetworkHelper.isTextMimeType("image/png"), false); + // Test invalid types + Assert.equal(NetworkHelper.isTextMimeType("application/foo-+json"), false); + Assert.equal(NetworkHelper.isTextMimeType("application/-foo+json"), false); + Assert.equal( + NetworkHelper.isTextMimeType("application/foo--bar+json"), + false + ); + + // Test we do not cause internal errors with unoptimized regex. Bug 961097 + Assert.equal( + NetworkHelper.isTextMimeType("application/vnd.google.safebrowsing-chunk"), + false + ); +} + +function test_parseCookieHeader() { + let result = NetworkHelper.parseSetCookieHeaders(["Test=1; SameSite=Strict"]); + Assert.deepEqual(result, [{ name: "Test", value: "1", samesite: "Strict" }]); + + result = NetworkHelper.parseSetCookieHeaders(["Test=1; SameSite=strict"]); + Assert.deepEqual(result, [{ name: "Test", value: "1", samesite: "Strict" }]); + + result = NetworkHelper.parseSetCookieHeaders(["Test=1; SameSite=STRICT"]); + Assert.deepEqual(result, [{ name: "Test", value: "1", samesite: "Strict" }]); + + result = NetworkHelper.parseSetCookieHeaders(["Test=1; SameSite=None"]); + Assert.deepEqual(result, [{ name: "Test", value: "1", samesite: "None" }]); + + result = NetworkHelper.parseSetCookieHeaders(["Test=1; SameSite=NONE"]); + Assert.deepEqual(result, [{ name: "Test", value: "1", samesite: "None" }]); + + result = NetworkHelper.parseSetCookieHeaders(["Test=1; SameSite=lax"]); + Assert.deepEqual(result, [{ name: "Test", value: "1", samesite: "Lax" }]); + + result = NetworkHelper.parseSetCookieHeaders(["Test=1; SameSite=Lax"]); + Assert.deepEqual(result, [{ name: "Test", value: "1", samesite: "Lax" }]); + + result = NetworkHelper.parseSetCookieHeaders([ + "Test=1; SameSite=Lax", + "Foo=2; SameSite=None", + ]); + Assert.deepEqual(result, [ + { name: "Test", value: "1", samesite: "Lax" }, + { name: "Foo", value: "2", samesite: "None" }, + ]); +} diff --git a/devtools/shared/network-observer/test/xpcshell/test_security-info-certificate.js b/devtools/shared/network-observer/test/xpcshell/test_security-info-certificate.js new file mode 100644 index 0000000000..00f482b8ca --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/test_security-info-certificate.js @@ -0,0 +1,84 @@ +/* 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 DUMMY_CERT = { + getBase64DERString() { + // This is the base64-encoded contents of the "DigiCert ECC Secure Server CA" + // intermediate certificate as issued by "DigiCert Global Root CA". It was + // chosen as a test certificate because it has an issuer common name, + // organization, and organizational unit that are somewhat distinct from + // its subject common name and organization name. + return "MIIDrDCCApSgAwIBAgIQCssoukZe5TkIdnRw883GEjANBgkqhkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0IEVDQyBTZWN1cmUgU2VydmVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ghC6nfYJN6gLGSkE85AnCNyqQIKDjc/ITa4jVMU9tWRlUvzlgKNcR7E2Munn17voOZ/WpIRllNv68DLP679Wz9HJOeaBy6Wvqgvu1cYr3GkvXg6HuhbPGtkESvMNCuMo4IBITCCAR0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUo53mH/naOU/AbuiRy5Wl2jHiCp8wHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEMBQADggEBAMeKoENL7HTJxavVHzA1Nm6YVntIrAVjrnuaVyRXzG/63qttnMe2uuzO58pzZNvfBDcKAEmzP58mrZGMIOgfiA4q+2Y3yDDo0sIkp0VILeoBUEoxlBPfjV/aKrtJPGHzecicZpIalir0ezZYoyxBEHQa0+1IttK7igZFcTMQMHp6mCHdJLnsnLWSB62DxsRq+HfmNb4TDydkskO/g+l3VtsIh5RHFPVfKK+jaEyDj2D3loB5hWp2Jp2VDCADjT7ueihlZGak2YPqmXTNbk19HOuNssWvFhtOyPNV6og4ETQdEa8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc="; + }, +}; + +add_task(async function run_test() { + info("Testing NetworkHelper.parseCertificateInfo."); + + const result = await NetworkHelper.parseCertificateInfo( + DUMMY_CERT, + new Map() + ); + + // Subject + equal( + result.subject.commonName, + "DigiCert ECC Secure Server CA", + "Common name is correct." + ); + equal( + result.subject.organization, + "DigiCert Inc", + "Organization is correct." + ); + equal( + result.subject.organizationUnit, + undefined, + "Organizational unit is correct." + ); + + // Issuer + equal( + result.issuer.commonName, + "DigiCert Global Root CA", + "Common name of the issuer is correct." + ); + equal( + result.issuer.organization, + "DigiCert Inc", + "Organization of the issuer is correct." + ); + equal( + result.issuer.organizationUnit, + "www.digicert.com", + "Organizational unit of the issuer is correct." + ); + + // Validity + equal( + result.validity.start, + "Fri, 08 Mar 2013 12:00:00 GMT", + "Start of the validity period is correct." + ); + equal( + result.validity.end, + "Wed, 08 Mar 2023 12:00:00 GMT", + "End of the validity period is correct." + ); + + // Fingerprints + equal( + result.fingerprint.sha1, + "56:EE:7C:27:06:83:16:2D:83:BA:EA:CC:79:0E:22:47:1A:DA:AB:E8", + "Certificate SHA1 fingerprint is correct." + ); + equal( + result.fingerprint.sha256, + "45:84:46:BA:75:D9:32:E9:14:F2:3C:2B:57:B7:D1:92:ED:DB:C2:18:1D:95:8E:11:81:AD:52:51:74:7A:1E:E8", + "Certificate SHA256 fingerprint is correct." + ); +}); diff --git a/devtools/shared/network-observer/test/xpcshell/test_security-info-parser.js b/devtools/shared/network-observer/test/xpcshell/test_security-info-parser.js new file mode 100644 index 0000000000..9515851a8b --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/test_security-info-parser.js @@ -0,0 +1,54 @@ +/* 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 wpl = Ci.nsIWebProgressListener; +const MockCertificate = { + getBase64DERString() { + // This is the same test certificate as in + // test_security-info-certificate.js for consistency. + return "MIIDrDCCApSgAwIBAgIQCssoukZe5TkIdnRw883GEjANBgkqhkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0IEVDQyBTZWN1cmUgU2VydmVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ghC6nfYJN6gLGSkE85AnCNyqQIKDjc/ITa4jVMU9tWRlUvzlgKNcR7E2Munn17voOZ/WpIRllNv68DLP679Wz9HJOeaBy6Wvqgvu1cYr3GkvXg6HuhbPGtkESvMNCuMo4IBITCCAR0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUo53mH/naOU/AbuiRy5Wl2jHiCp8wHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEMBQADggEBAMeKoENL7HTJxavVHzA1Nm6YVntIrAVjrnuaVyRXzG/63qttnMe2uuzO58pzZNvfBDcKAEmzP58mrZGMIOgfiA4q+2Y3yDDo0sIkp0VILeoBUEoxlBPfjV/aKrtJPGHzecicZpIalir0ezZYoyxBEHQa0+1IttK7igZFcTMQMHp6mCHdJLnsnLWSB62DxsRq+HfmNb4TDydkskO/g+l3VtsIh5RHFPVfKK+jaEyDj2D3loB5hWp2Jp2VDCADjT7ueihlZGak2YPqmXTNbk19HOuNssWvFhtOyPNV6og4ETQdEa8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc="; + }, +}; + +// This *cannot* be used as an nsITransportSecurityInfo (since that interface is +// builtinclass) but the methods being tested aren't defined by XPCOM and aren't +// calling QueryInterface, so this usage is fine. +const MockSecurityInfo = { + securityState: wpl.STATE_IS_SECURE, + errorCode: 0, + cipherName: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", + // TLS_VERSION_1_2 + protocolVersion: 3, + serverCert: MockCertificate, +}; + +add_task(async function run_test() { + const result = await NetworkHelper.parseSecurityInfo( + MockSecurityInfo, + {}, + {}, + new Map() + ); + + equal(result.state, "secure", "State is correct."); + + equal( + result.cipherSuite, + MockSecurityInfo.cipherName, + "Cipher suite is correct." + ); + + equal(result.protocolVersion, "TLSv1.2", "Protocol version is correct."); + + deepEqual( + result.cert, + await NetworkHelper.parseCertificateInfo(MockCertificate, new Map()), + "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."); +}); diff --git a/devtools/shared/network-observer/test/xpcshell/test_security-info-protocol-version.js b/devtools/shared/network-observer/test/xpcshell/test_security-info-protocol-version.js new file mode 100644 index 0000000000..a81f7ce73c --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/test_security-info-protocol-version.js @@ -0,0 +1,48 @@ +/* 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 TEST_CASES = [ + { + 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: "TLS_VERSION_1.3", + input: 4, + expected: "TLSv1.3", + }, + { + description: "invalid version", + input: -1, + expected: "Unknown", + }, +]; + +function run_test() { + info("Testing NetworkHelper.formatSecurityProtocol."); + + for (const { description, input, expected } of TEST_CASES) { + info("Testing " + description); + + equal( + NetworkHelper.formatSecurityProtocol(input), + expected, + "Got the expected protocol string." + ); + } +} diff --git a/devtools/shared/network-observer/test/xpcshell/test_security-info-state.js b/devtools/shared/network-observer/test/xpcshell/test_security-info-state.js new file mode 100644 index 0000000000..be622b2019 --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/test_security-info-state.js @@ -0,0 +1,122 @@ +/* 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 wpl = Ci.nsIWebProgressListener; + +// This *cannot* be used as an nsITransportSecurityInfo (since that interface is +// builtinclass) but the methods being tested aren't defined by XPCOM and aren't +// calling QueryInterface, so this usage is fine. +const MockSecurityInfo = { + securityState: wpl.STATE_IS_BROKEN, + errorCode: 0, + // nsISSLStatus.TLS_VERSION_1_2 + protocolVersion: 3, + cipherName: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", +}; + +add_task(async function run_test() { + await test_nullSecurityInfo(); + await test_insecureSecurityInfoWithNSSError(); + await test_insecureSecurityInfoWithoutNSSError(); + await test_brokenSecurityInfo(); + await test_secureSecurityInfo(); +}); + +/** + * Test that undefined security information is returns "insecure". + */ +async function test_nullSecurityInfo() { + const result = await NetworkHelper.parseSecurityInfo(null, {}, {}, new Map()); + equal( + result.state, + "insecure", + "state == 'insecure' when securityInfo was undefined" + ); +} + +/** + * Test that STATE_IS_INSECURE with NSSError returns "broken" + */ +async function test_insecureSecurityInfoWithNSSError() { + MockSecurityInfo.securityState = wpl.STATE_IS_INSECURE; + + // Taken from security/manager/ssl/tests/unit/head_psm.js. + MockSecurityInfo.errorCode = -8180; + + const result = await NetworkHelper.parseSecurityInfo( + MockSecurityInfo, + {}, + {}, + new Map() + ); + 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" + */ +async function test_insecureSecurityInfoWithoutNSSError() { + MockSecurityInfo.securityState = wpl.STATE_IS_INSECURE; + + const result = await NetworkHelper.parseSecurityInfo( + MockSecurityInfo, + {}, + {}, + new Map() + ); + 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" + */ +async function test_secureSecurityInfo() { + MockSecurityInfo.securityState = wpl.STATE_IS_SECURE; + + const result = await NetworkHelper.parseSecurityInfo( + MockSecurityInfo, + {}, + {}, + new Map() + ); + equal( + result.state, + "secure", + "state == 'secure' if securityState contains STATE_IS_SECURE flag" + ); +} + +/** + * Test that STATE_IS_BROKEN returns "weak" + */ +async function test_brokenSecurityInfo() { + MockSecurityInfo.securityState = wpl.STATE_IS_BROKEN; + + const result = await NetworkHelper.parseSecurityInfo( + MockSecurityInfo, + {}, + {}, + new Map() + ); + equal( + result.state, + "weak", + "state == 'weak' if securityState contains STATE_IS_BROKEN flag" + ); +} diff --git a/devtools/shared/network-observer/test/xpcshell/test_security-info-static-hpkp.js b/devtools/shared/network-observer/test/xpcshell/test_security-info-static-hpkp.js new file mode 100644 index 0000000000..f1aa883b93 --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/test_security-info-static-hpkp.js @@ -0,0 +1,41 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that NetworkHelper.parseSecurityInfo correctly detects static hpkp pins + +const wpl = Ci.nsIWebProgressListener; + +// This *cannot* be used as an nsITransportSecurityInfo (since that interface is +// builtinclass) but the methods being tested aren't defined by XPCOM and aren't +// calling QueryInterface, so this usage is fine. +const MockSecurityInfo = { + securityState: wpl.STATE_IS_SECURE, + errorCode: 0, + cipherName: "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", + // TLS_VERSION_1_2 + protocolVersion: 3, + serverCert: { + getBase64DERString() { + // This is the same test certificate as in + // test_security-info-certificate.js for consistency. + return "MIIDrDCCApSgAwIBAgIQCssoukZe5TkIdnRw883GEjANBgkqhkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0IEVDQyBTZWN1cmUgU2VydmVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ghC6nfYJN6gLGSkE85AnCNyqQIKDjc/ITa4jVMU9tWRlUvzlgKNcR7E2Munn17voOZ/WpIRllNv68DLP679Wz9HJOeaBy6Wvqgvu1cYr3GkvXg6HuhbPGtkESvMNCuMo4IBITCCAR0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUo53mH/naOU/AbuiRy5Wl2jHiCp8wHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEMBQADggEBAMeKoENL7HTJxavVHzA1Nm6YVntIrAVjrnuaVyRXzG/63qttnMe2uuzO58pzZNvfBDcKAEmzP58mrZGMIOgfiA4q+2Y3yDDo0sIkp0VILeoBUEoxlBPfjV/aKrtJPGHzecicZpIalir0ezZYoyxBEHQa0+1IttK7igZFcTMQMHp6mCHdJLnsnLWSB62DxsRq+HfmNb4TDydkskO/g+l3VtsIh5RHFPVfKK+jaEyDj2D3loB5hWp2Jp2VDCADjT7ueihlZGak2YPqmXTNbk19HOuNssWvFhtOyPNV6og4ETQdEa8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc="; + }, + }, +}; + +const MockHttpInfo = { + hostname: "include-subdomains.pinning.example.com", + private: false, +}; + +add_task(async function run_test() { + Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1); + const result = await NetworkHelper.parseSecurityInfo( + MockSecurityInfo, + {}, + MockHttpInfo, + new Map() + ); + equal(result.hpkp, true, "Static HPKP detected."); +}); diff --git a/devtools/shared/network-observer/test/xpcshell/test_security-info-weakness-reasons.js b/devtools/shared/network-observer/test/xpcshell/test_security-info-weakness-reasons.js new file mode 100644 index 0000000000..71d7675284 --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/test_security-info-weakness-reasons.js @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Tests that NetworkHelper.getReasonsForWeakness returns correct reasons for +// weak requests. + +const wpl = Ci.nsIWebProgressListener; +const TEST_CASES = [ + { + description: "weak cipher", + input: wpl.STATE_IS_BROKEN | wpl.STATE_USES_WEAK_CRYPTO, + expected: ["cipher"], + }, + { + description: "only STATE_IS_BROKEN flag", + input: wpl.STATE_IS_BROKEN, + expected: [], + }, + { + description: "only STATE_IS_SECURE flag", + input: wpl.STATE_IS_SECURE, + expected: [], + }, +]; + +function run_test() { + info("Testing NetworkHelper.getReasonsForWeakness."); + + for (const { description, input, expected } of TEST_CASES) { + info("Testing " + description); + + deepEqual( + NetworkHelper.getReasonsForWeakness(input), + expected, + "Got the expected reasons for weakness." + ); + } +} diff --git a/devtools/shared/network-observer/test/xpcshell/test_throttle.js b/devtools/shared/network-observer/test/xpcshell/test_throttle.js new file mode 100644 index 0000000000..5f8ef589fb --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/test_throttle.js @@ -0,0 +1,162 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* eslint-disable mozilla/use-chromeutils-generateqi */ + +const { NetworkThrottleManager } = ChromeUtils.importESModule( + "resource://devtools/shared/network-observer/NetworkThrottleManager.sys.mjs" +); +const nsIScriptableInputStream = Ci.nsIScriptableInputStream; + +function TestStreamListener() { + this.state = "initial"; +} +TestStreamListener.prototype = { + onStartRequest() { + this.setState("start"); + }, + + onStopRequest() { + this.setState("stop"); + }, + + onDataAvailable(request, inputStream, offset, count) { + const sin = Cc["@mozilla.org/scriptableinputstream;1"].createInstance( + nsIScriptableInputStream + ); + sin.init(inputStream); + this.data = sin.read(count); + this.setState("data"); + }, + + setState(state) { + this.state = state; + if (this._deferred) { + this._deferred.resolve(state); + this._deferred = null; + } + }, + + onStateChanged() { + if (!this._deferred) { + let resolve, reject; + const promise = new Promise(function (res, rej) { + resolve = res; + reject = rej; + }); + this._deferred = { resolve, reject, promise }; + } + return this._deferred.promise; + }, +}; + +function TestChannel() { + this.state = "initial"; + this.testListener = new TestStreamListener(); + this._throttleQueue = null; +} +TestChannel.prototype = { + QueryInterface() { + return this; + }, + + get throttleQueue() { + return this._throttleQueue; + }, + + set throttleQueue(q) { + this._throttleQueue = q; + this.state = "throttled"; + }, + + setNewListener(listener) { + this.listener = listener; + this.state = "listener"; + return this.testListener; + }, +}; + +add_task(async function () { + const throttler = new NetworkThrottleManager({ + latencyMean: 1, + latencyMax: 1, + downloadBPSMean: 500, + downloadBPSMax: 500, + uploadBPSMean: 500, + uploadBPSMax: 500, + }); + + const uploadChannel = new TestChannel(); + throttler.manageUpload(uploadChannel); + equal( + uploadChannel.state, + "throttled", + "NetworkThrottleManager set throttleQueue" + ); + + const downloadChannel = new TestChannel(); + const testListener = downloadChannel.testListener; + + const listener = throttler.manage(downloadChannel); + equal( + downloadChannel.state, + "listener", + "NetworkThrottleManager called setNewListener" + ); + + equal(testListener.state, "initial", "test listener in initial state"); + + // This method must be passed through immediately. + listener.onStartRequest(null); + equal(testListener.state, "start", "test listener started"); + + const TEST_INPUT = "hi bob"; + + const testStream = Cc["@mozilla.org/storagestream;1"].createInstance( + Ci.nsIStorageStream + ); + testStream.init(512, 512); + const out = testStream.getOutputStream(0); + out.write(TEST_INPUT, TEST_INPUT.length); + out.close(); + const testInputStream = testStream.newInputStream(0); + + const activityDistributor = Cc[ + "@mozilla.org/network/http-activity-distributor;1" + ].getService(Ci.nsIHttpActivityDistributor); + let activitySeen = false; + listener.addActivityCallback( + () => { + activitySeen = true; + }, + null, + null, + null, + activityDistributor.ACTIVITY_SUBTYPE_RESPONSE_COMPLETE, + null, + TEST_INPUT.length, + null + ); + + // onDataAvailable is required to immediately read the data. + listener.onDataAvailable(null, testInputStream, 0, 6); + equal(testInputStream.available(), 0, "no more data should be available"); + equal( + testListener.state, + "start", + "test listener should not have received data" + ); + equal(activitySeen, false, "activity not distributed yet"); + + let newState = await testListener.onStateChanged(); + equal(newState, "data", "test listener received data"); + equal(testListener.data, TEST_INPUT, "test listener received all the data"); + equal(activitySeen, true, "activity has been distributed"); + + const onChange = testListener.onStateChanged(); + listener.onStopRequest(null, null); + newState = await onChange; + equal(newState, "stop", "onStateChanged reported"); +}); diff --git a/devtools/shared/network-observer/test/xpcshell/xpcshell.ini b/devtools/shared/network-observer/test/xpcshell/xpcshell.ini new file mode 100644 index 0000000000..3f5c1b674a --- /dev/null +++ b/devtools/shared/network-observer/test/xpcshell/xpcshell.ini @@ -0,0 +1,15 @@ +[DEFAULT] +tags = devtools +head = head.js +firefox-appdir = browser +skip-if = toolkit == 'android' +support-files = + +[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] +[test_security-info-static-hpkp.js] +[test_security-info-weakness-reasons.js] +[test_throttle.js] |