diff options
Diffstat (limited to 'security/manager/ssl/tests/unit/test_crlite_filters.js')
-rw-r--r-- | security/manager/ssl/tests/unit/test_crlite_filters.js | 880 |
1 files changed, 880 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/test_crlite_filters.js b/security/manager/ssl/tests/unit/test_crlite_filters.js new file mode 100644 index 0000000000..55fe4d75e3 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_crlite_filters.js @@ -0,0 +1,880 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Tests that CRLite filter downloading works correctly. + +// The file `test_crlite_filters/20201017-0-filter` can be regenerated using +// the rust-create-cascade program from https://github.com/mozilla/crlite. +// +// The input to this program is a list of known serial numbers and a list of +// revoked serial numbers. The lists are presented as directories of files in +// which each file holds serials for one issuer. The file names are +// urlsafe-base64 encoded SHA256 hashes of issuer SPKIs. The file contents are +// ascii hex encoded serial numbers. The program crlite_key.py in this directory +// can generate these values for you. +// +// The test filter was generated as follows: +// +// $ ./crlite_key.py test_crlite_filters/issuer.pem test_crlite_filters/valid.pem +// 8Rw90Ej3Ttt8RRkrg-WYDS9n7IS03bk5bjP_UXPtaY8= +// 00da4f392bfd8bcea8 +// +// $ ./crlite_key.py test_crlite_filters/issuer.pem test_crlite_filters/revoked.pem +// 8Rw90Ej3Ttt8RRkrg-WYDS9n7IS03bk5bjP_UXPtaY8= +// 2d35ca6503fb1ba3 +// +// $ mkdir known revoked +// $ echo "00da4f392bfd8bcea8" > known/8Rw90Ej3Ttt8RRkrg-WYDS9n7IS03bk5bjP_UXPtaY8\= +// $ echo "2d35ca6503fb1ba3" >> known/8Rw90Ej3Ttt8RRkrg-WYDS9n7IS03bk5bjP_UXPtaY8\= +// $ echo "2d35ca6503fb1ba3" > revoked/8Rw90Ej3Ttt8RRkrg-WYDS9n7IS03bk5bjP_UXPtaY8\= +// +// $ rust-create-cascade --known ./known/ --revoked ./revoked/ +// + +"use strict"; +do_get_profile(); // must be called before getting nsIX509CertDB + +const { RemoteSecuritySettings } = ChromeUtils.importESModule( + "resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs" +); +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +const { CRLiteFiltersClient } = RemoteSecuritySettings.init(); + +const CRLITE_FILTERS_ENABLED_PREF = + "security.remote_settings.crlite_filters.enabled"; +const INTERMEDIATES_ENABLED_PREF = + "security.remote_settings.intermediates.enabled"; +const INTERMEDIATES_DL_PER_POLL_PREF = + "security.remote_settings.intermediates.downloads_per_poll"; + +// crlite_enrollment_id.py test_crlite_filters/issuer.pem +const ISSUER_PEM_UID = "UbH9/ZAnjuqf79Xhah1mFOWo6ZvgQCgsdheWfjvVUM8="; +// crlite_enrollment_id.py test_crlite_filters/no-sct-issuer.pem +const NO_SCT_ISSUER_PEM_UID = "Myn7EasO1QikOtNmo/UZdh6snCAw0BOY6wgU8OsUeeY="; + +function getHashCommon(aStr, useBase64) { + let hasher = Cc["@mozilla.org/security/hash;1"].createInstance( + Ci.nsICryptoHash + ); + hasher.init(Ci.nsICryptoHash.SHA256); + let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( + Ci.nsIStringInputStream + ); + stringStream.data = aStr; + hasher.updateFromStream(stringStream, -1); + + return hasher.finish(useBase64); +} + +// Get a hexified SHA-256 hash of the given string. +function getHash(aStr) { + return hexify(getHashCommon(aStr, false)); +} + +// Get the name of the file in the test directory to serve as the attachment +// for the given filter. +function getFilenameForFilter(filter) { + if (filter.type == "full") { + return "20201017-0-filter"; + } + if (filter.id == "0001") { + return "20201017-1-filter.stash"; + } + // The addition of another stash file was written more than a month after + // other parts of this test. As such, the second stash file for October 17th, + // 2020 was not readily available. Since the structure of stash files don't + // depend on each other, though, any two stash files are compatible, and so + // this stash from December 1st is used instead. + return "20201201-3-filter.stash"; +} + +/** + * Simulate a Remote Settings synchronization by filling up the local data with + * fake records. + * + * @param {*} filters List of filters for which we will create records. + * @param {boolean} clear Whether or not to clear the local DB first. Defaults + * to true. + */ +async function syncAndDownload(filters, clear = true) { + const localDB = await CRLiteFiltersClient.client.db; + if (clear) { + await localDB.clear(); + } + + for (let filter of filters) { + const filename = getFilenameForFilter(filter); + const file = do_get_file(`test_crlite_filters/${filename}`); + const fileBytes = readFile(file); + + const record = { + details: { + name: `${filter.timestamp}-${filter.type}`, + }, + attachment: { + hash: getHash(fileBytes), + size: fileBytes.length, + filename, + location: `security-state-workspace/cert-revocations/test_crlite_filters/${filename}`, + mimetype: "application/octet-stream", + }, + incremental: filter.type == "diff", + effectiveTimestamp: new Date(filter.timestamp).getTime(), + parent: filter.type == "diff" ? filter.parent : undefined, + id: filter.id, + coverage: filter.type == "full" ? filter.coverage : undefined, + enrolledIssuers: + filter.type == "full" ? filter.enrolledIssuers : undefined, + }; + + await localDB.create(record); + } + // This promise will wait for the end of downloading. + let promise = TestUtils.topicObserved( + "remote-security-settings:crlite-filters-downloaded" + ); + // Simulate polling for changes, trigger the download of attachments. + Services.obs.notifyObservers(null, "remote-settings:changes-poll-end"); + let results = await promise; + return results[1]; // topicObserved gives back a 2-array +} + +add_task(async function test_crlite_filters_disabled() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, false); + + let result = await syncAndDownload([ + { + timestamp: "2019-01-01T00:00:00Z", + type: "full", + id: "0000", + coverage: [ + { + logID: "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=", + minTimestamp: 0, + maxTimestamp: 9999999999999, + }, + ], + }, + ]); + equal(result, "disabled", "CRLite filter download should not have run"); +}); + +add_task(async function test_crlite_no_filters() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + + let result = await syncAndDownload([]); + equal( + result, + "unavailable", + "CRLite filter download should have run, but nothing was available" + ); +}); + +add_task(async function test_crlite_only_incremental_filters() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + + let result = await syncAndDownload([ + { + timestamp: "2019-01-01T06:00:00Z", + type: "diff", + id: "0001", + parent: "0000", + }, + { + timestamp: "2019-01-01T18:00:00Z", + type: "diff", + id: "0002", + parent: "0001", + }, + { + timestamp: "2019-01-01T12:00:00Z", + type: "diff", + id: "0003", + parent: "0002", + }, + ]); + equal( + result, + "unavailable", + "CRLite filter download should have run, but no full filters were available" + ); +}); + +add_task(async function test_crlite_incremental_filters_with_wrong_parent() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + + let result = await syncAndDownload([ + { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" }, + { + timestamp: "2019-01-01T06:00:00Z", + type: "diff", + id: "0001", + parent: "0000", + }, + { + timestamp: "2019-01-01T12:00:00Z", + type: "diff", + id: "0003", + parent: "0002", + }, + { + timestamp: "2019-01-01T18:00:00Z", + type: "diff", + id: "0004", + parent: "0003", + }, + ]); + let [status, filters] = result.split(";"); + equal(status, "finished", "CRLite filter download should have run"); + let filtersSplit = filters.split(","); + deepEqual( + filtersSplit, + ["2019-01-01T00:00:00Z-full", "2019-01-01T06:00:00Z-diff"], + "Should have downloaded the expected CRLite filters" + ); +}); + +add_task(async function test_crlite_incremental_filter_too_early() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + + let result = await syncAndDownload([ + { timestamp: "2019-01-02T00:00:00Z", type: "full", id: "0000" }, + { + timestamp: "2019-01-01T00:00:00Z", + type: "diff", + id: "0001", + parent: "0000", + }, + ]); + equal( + result, + "finished;2019-01-02T00:00:00Z-full", + "CRLite filter download should have run" + ); +}); + +add_task(async function test_crlite_filters_basic() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + + let result = await syncAndDownload([ + { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" }, + ]); + equal( + result, + "finished;2019-01-01T00:00:00Z-full", + "CRLite filter download should have run" + ); +}); + +add_task(async function test_crlite_filters_not_cached() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + let filters = [ + { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" }, + ]; + let result = await syncAndDownload(filters); + equal( + result, + "finished;2019-01-01T00:00:00Z-full", + "CRLite filter download should have run" + ); + + let records = await CRLiteFiltersClient.client.db.list(); + + // `syncAndDownload` should not cache the attachment, so this download should + // get the attachment from the source. + let attachment = await CRLiteFiltersClient.client.attachments.download( + records[0] + ); + equal(attachment._source, "remote_match"); + await CRLiteFiltersClient.client.attachments.deleteDownloaded(records[0]); +}); + +add_task(async function test_crlite_filters_full_and_incremental() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + + let result = await syncAndDownload([ + // These are deliberately listed out of order. + { + timestamp: "2019-01-01T06:00:00Z", + type: "diff", + id: "0001", + parent: "0000", + }, + { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" }, + { + timestamp: "2019-01-01T18:00:00Z", + type: "diff", + id: "0003", + parent: "0002", + }, + { + timestamp: "2019-01-01T12:00:00Z", + type: "diff", + id: "0002", + parent: "0001", + }, + ]); + let [status, filters] = result.split(";"); + equal(status, "finished", "CRLite filter download should have run"); + let filtersSplit = filters.split(","); + deepEqual( + filtersSplit, + [ + "2019-01-01T00:00:00Z-full", + "2019-01-01T06:00:00Z-diff", + "2019-01-01T12:00:00Z-diff", + "2019-01-01T18:00:00Z-diff", + ], + "Should have downloaded the expected CRLite filters" + ); +}); + +add_task(async function test_crlite_filters_multiple_days() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + + let result = await syncAndDownload([ + // These are deliberately listed out of order. + { + timestamp: "2019-01-02T06:00:00Z", + type: "diff", + id: "0011", + parent: "0010", + }, + { + timestamp: "2019-01-03T12:00:00Z", + type: "diff", + id: "0022", + parent: "0021", + }, + { + timestamp: "2019-01-02T12:00:00Z", + type: "diff", + id: "0012", + parent: "0011", + }, + { + timestamp: "2019-01-03T18:00:00Z", + type: "diff", + id: "0023", + parent: "0022", + }, + { + timestamp: "2019-01-02T18:00:00Z", + type: "diff", + id: "0013", + parent: "0012", + }, + { timestamp: "2019-01-02T00:00:00Z", type: "full", id: "0010" }, + { timestamp: "2019-01-03T00:00:00Z", type: "full", id: "0020" }, + { + timestamp: "2019-01-01T06:00:00Z", + type: "diff", + id: "0001", + parent: "0000", + }, + { + timestamp: "2019-01-01T18:00:00Z", + type: "diff", + id: "0003", + parent: "0002", + }, + { + timestamp: "2019-01-01T12:00:00Z", + type: "diff", + id: "0002", + parent: "0001", + }, + { timestamp: "2019-01-01T00:00:00Z", type: "full", id: "0000" }, + { + timestamp: "2019-01-03T06:00:00Z", + type: "diff", + id: "0021", + parent: "0020", + }, + ]); + let [status, filters] = result.split(";"); + equal(status, "finished", "CRLite filter download should have run"); + let filtersSplit = filters.split(","); + deepEqual( + filtersSplit, + [ + "2019-01-03T00:00:00Z-full", + "2019-01-03T06:00:00Z-diff", + "2019-01-03T12:00:00Z-diff", + "2019-01-03T18:00:00Z-diff", + ], + "Should have downloaded the expected CRLite filters" + ); +}); + +add_task(async function test_crlite_confirm_revocations_mode() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + Services.prefs.setIntPref( + "security.pki.crlite_mode", + CRLiteModeConfirmRevocationsValue + ); + Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true); + + let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( + Ci.nsIX509CertDB + ); + addCertFromFile(certdb, "test_crlite_filters/issuer.pem", ",,"); + addCertFromFile(certdb, "test_crlite_filters/no-sct-issuer.pem", ",,"); + + let result = await syncAndDownload([ + { + timestamp: "2020-10-17T00:00:00Z", + type: "full", + id: "0000", + coverage: [ + { + logID: "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=", + minTimestamp: 0, + maxTimestamp: 9999999999999, + }, + { + logID: "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=", + minTimestamp: 0, + maxTimestamp: 9999999999999, + }, + ], + enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID], + }, + ]); + equal( + result, + "finished;2020-10-17T00:00:00Z-full", + "CRLite filter download should have run" + ); + + // The CRLite result should be enforced for this certificate and + // OCSP should not be consulted. + let validCert = constructCertFromFile("test_crlite_filters/valid.pem"); + await checkCertErrorGenericAtTime( + certdb, + validCert, + PRErrorCodeSuccess, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + undefined, + "vpn.worldofspeed.org", + 0 + ); + + // OCSP should be consulted for this certificate, but OCSP is disabled by + // Ci.nsIX509CertDB.FLAG_LOCAL_ONLY so this will be treated as a soft-failure + // and the CRLite result will be used. + let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem"); + await checkCertErrorGenericAtTime( + certdb, + revokedCert, + SEC_ERROR_REVOKED_CERTIFICATE, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + undefined, + "us-datarecovery.com", + Ci.nsIX509CertDB.FLAG_LOCAL_ONLY + ); + + // Reload the filter w/o coverage and enrollment metadata. + result = await syncAndDownload([ + { + timestamp: "2020-10-17T00:00:00Z", + type: "full", + id: "0000", + coverage: [], + enrolledIssuers: [], + }, + ]); + equal( + result, + "finished;2020-10-17T00:00:00Z-full", + "CRLite filter download should have run" + ); + + // OCSP will be consulted for the revoked certificate, but a soft-failure + // should now result in a Success return. + await checkCertErrorGenericAtTime( + certdb, + revokedCert, + PRErrorCodeSuccess, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + undefined, + "us-datarecovery.com", + Ci.nsIX509CertDB.FLAG_LOCAL_ONLY + ); +}); + +add_task(async function test_crlite_filters_and_check_revocation() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + Services.prefs.setIntPref( + "security.pki.crlite_mode", + CRLiteModeEnforcePrefValue + ); + Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true); + + let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( + Ci.nsIX509CertDB + ); + addCertFromFile(certdb, "test_crlite_filters/issuer.pem", ",,"); + addCertFromFile(certdb, "test_crlite_filters/no-sct-issuer.pem", ",,"); + + let result = await syncAndDownload([ + { + timestamp: "2020-10-17T00:00:00Z", + type: "full", + id: "0000", + coverage: [ + { + logID: "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=", + minTimestamp: 0, + maxTimestamp: 9999999999999, + }, + { + logID: "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=", + minTimestamp: 0, + maxTimestamp: 9999999999999, + }, + ], + enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID], + }, + ]); + equal( + result, + "finished;2020-10-17T00:00:00Z-full", + "CRLite filter download should have run" + ); + + let validCert = constructCertFromFile("test_crlite_filters/valid.pem"); + // NB: by not specifying Ci.nsIX509CertDB.FLAG_LOCAL_ONLY, this tests that + // the implementation does not fall back to OCSP fetching, because if it + // did, the implementation would attempt to connect to a server outside the + // test infrastructure, which would result in a crash in the test + // environment, which would be treated as a test failure. + await checkCertErrorGenericAtTime( + certdb, + validCert, + PRErrorCodeSuccess, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "vpn.worldofspeed.org", + 0 + ); + + let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem"); + await checkCertErrorGenericAtTime( + certdb, + revokedCert, + SEC_ERROR_REVOKED_CERTIFICATE, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "us-datarecovery.com", + 0 + ); + + // Before any stashes are downloaded, this should verify successfully. + let revokedInStashCert = constructCertFromFile( + "test_crlite_filters/revoked-in-stash.pem" + ); + await checkCertErrorGenericAtTime( + certdb, + revokedInStashCert, + PRErrorCodeSuccess, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "stokedmoto.com", + 0 + ); + + result = await syncAndDownload( + [ + { + timestamp: "2020-10-17T03:00:00Z", + type: "diff", + id: "0001", + parent: "0000", + }, + ], + false + ); + equal( + result, + "finished;2020-10-17T03:00:00Z-diff", + "Should have downloaded the expected CRLite filters" + ); + + // After downloading the first stash, this should be revoked. + await checkCertErrorGenericAtTime( + certdb, + revokedInStashCert, + SEC_ERROR_REVOKED_CERTIFICATE, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "stokedmoto.com", + 0 + ); + + // Before downloading the second stash, this should not be revoked. + let revokedInStash2Cert = constructCertFromFile( + "test_crlite_filters/revoked-in-stash-2.pem" + ); + await checkCertErrorGenericAtTime( + certdb, + revokedInStash2Cert, + PRErrorCodeSuccess, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "icsreps.com", + 0 + ); + + result = await syncAndDownload( + [ + { + timestamp: "2020-10-17T06:00:00Z", + type: "diff", + id: "0002", + parent: "0001", + }, + ], + false + ); + equal( + result, + "finished;2020-10-17T06:00:00Z-diff", + "Should have downloaded the expected CRLite filters" + ); + + // After downloading the second stash, this should be revoked. + await checkCertErrorGenericAtTime( + certdb, + revokedInStash2Cert, + SEC_ERROR_REVOKED_CERTIFICATE, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "icsreps.com", + 0 + ); + + // The other certificates should still get the same results as they did before. + await checkCertErrorGenericAtTime( + certdb, + validCert, + PRErrorCodeSuccess, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "vpn.worldofspeed.org", + 0 + ); + + await checkCertErrorGenericAtTime( + certdb, + revokedCert, + SEC_ERROR_REVOKED_CERTIFICATE, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "us-datarecovery.com", + 0 + ); + + await checkCertErrorGenericAtTime( + certdb, + revokedInStashCert, + SEC_ERROR_REVOKED_CERTIFICATE, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "stokedmoto.com", + 0 + ); + + // This certificate has no embedded SCTs, so it is not guaranteed to be in + // CT, so CRLite can't be guaranteed to give the correct answer, so it is + // not consulted, and the implementation falls back to OCSP. Since the real + // OCSP responder can't be reached, this results in a + // SEC_ERROR_OCSP_SERVER_ERROR. + let noSCTCert = constructCertFromFile("test_crlite_filters/no-sct.pem"); + // NB: this will cause an OCSP request to be sent to localhost:80, but + // since an OCSP responder shouldn't be running on that port, this should + // fail safely. + Services.prefs.setCharPref("network.dns.localDomains", "ocsp.digicert.com"); + Services.prefs.setBoolPref("security.OCSP.require", true); + Services.prefs.setIntPref("security.OCSP.enabled", 1); + await checkCertErrorGenericAtTime( + certdb, + noSCTCert, + SEC_ERROR_OCSP_SERVER_ERROR, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "mail233.messagelabs.com", + 0 + ); + Services.prefs.clearUserPref("network.dns.localDomains"); + Services.prefs.clearUserPref("security.OCSP.require"); + Services.prefs.clearUserPref("security.OCSP.enabled"); + + // The revoked certificate example has one SCT from the log with ID "9ly...=" + // at time 1598140096613 and another from the log with ID "XNx...=" at time + // 1598140096917. The filter we construct here fails to cover it by one + // millisecond in each case. The implementation will fall back to OCSP + // fetching. Since this would result in a crash and test failure, the + // Ci.nsIX509CertDB.FLAG_LOCAL_ONLY is used. + result = await syncAndDownload([ + { + timestamp: "2020-10-17T00:00:00Z", + type: "full", + id: "0000", + coverage: [ + { + logID: "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=", + minTimestamp: 0, + maxTimestamp: 1598140096612, + }, + { + logID: "XNxDkv7mq0VEsV6a1FbmEDf71fpH3KFzlLJe5vbHDso=", + minTimestamp: 1598140096917, + maxTimestamp: 9999999999999, + }, + ], + enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID], + }, + ]); + equal( + result, + "finished;2020-10-17T00:00:00Z-full", + "CRLite filter download should have run" + ); + await checkCertErrorGenericAtTime( + certdb, + revokedCert, + PRErrorCodeSuccess, + certificateUsageSSLServer, + new Date("2020-10-20T00:00:00Z").getTime() / 1000, + false, + "us-datarecovery.com", + Ci.nsIX509CertDB.FLAG_LOCAL_ONLY + ); +}); + +add_task(async function test_crlite_filters_avoid_reprocessing_filters() { + Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); + + let result = await syncAndDownload([ + { + timestamp: "2019-01-01T00:00:00Z", + type: "full", + id: "0000", + coverage: [ + { + logID: "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=", + minTimestamp: 0, + maxTimestamp: 9999999999999, + }, + ], + enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID], + }, + { + timestamp: "2019-01-01T06:00:00Z", + type: "diff", + id: "0001", + parent: "0000", + }, + { + timestamp: "2019-01-01T12:00:00Z", + type: "diff", + id: "0002", + parent: "0001", + }, + { + timestamp: "2019-01-01T18:00:00Z", + type: "diff", + id: "0003", + parent: "0002", + }, + ]); + let [status, filters] = result.split(";"); + equal(status, "finished", "CRLite filter download should have run"); + let filtersSplit = filters.split(","); + deepEqual( + filtersSplit, + [ + "2019-01-01T00:00:00Z-full", + "2019-01-01T06:00:00Z-diff", + "2019-01-01T12:00:00Z-diff", + "2019-01-01T18:00:00Z-diff", + ], + "Should have downloaded the expected CRLite filters" + ); + // This simulates another poll without clearing the database first. The + // filter and stashes should not be re-downloaded. + result = await syncAndDownload([], false); + equal(result, "finished;"); + + // If a new stash is added, only it should be downloaded. + result = await syncAndDownload( + [ + { + timestamp: "2019-01-02T00:00:00Z", + type: "diff", + id: "0004", + parent: "0003", + }, + ], + false + ); + equal(result, "finished;2019-01-02T00:00:00Z-diff"); +}); + +let server; + +function run_test() { + server = new HttpServer(); + server.start(-1); + registerCleanupFunction(() => server.stop(() => {})); + + server.registerDirectory( + "/cdn/security-state-workspace/cert-revocations/", + do_get_file(".") + ); + + server.registerPathHandler("/v1/", (request, response) => { + response.write( + JSON.stringify({ + capabilities: { + attachments: { + base_url: `http://localhost:${server.identity.primaryPort}/cdn/`, + }, + }, + }) + ); + response.setHeader("Content-Type", "application/json; charset=UTF-8"); + response.setStatusLine(null, 200, "OK"); + }); + + Services.prefs.setCharPref( + "services.settings.server", + `http://localhost:${server.identity.primaryPort}/v1` + ); + + // Set intermediate preloading to download 0 intermediates at a time. + Services.prefs.setIntPref(INTERMEDIATES_DL_PER_POLL_PREF, 0); + + Services.prefs.setCharPref("browser.policies.loglevel", "debug"); + + run_next_test(); +} |