From 9627f18cebab38cdfe45592d83371ee7bbc62cfa Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 09:21:33 -0500 Subject: Remove kinto client, Firefox kinto storage adapter, blocklist update client and integration with sync, OneCRL and the custom time check for derives system time. --- .../tests/unit/test_blocklist_certificates.js | 224 --------- .../common/tests/unit/test_blocklist_clients.js | 412 ----------------- .../common/tests/unit/test_blocklist_signatures.js | 510 --------------------- .../common/tests/unit/test_blocklist_updater.js | 173 ------- services/common/tests/unit/test_kinto.js | 412 ----------------- services/common/tests/unit/xpcshell.ini | 8 - 6 files changed, 1739 deletions(-) delete mode 100644 services/common/tests/unit/test_blocklist_certificates.js delete mode 100644 services/common/tests/unit/test_blocklist_clients.js delete mode 100644 services/common/tests/unit/test_blocklist_signatures.js delete mode 100644 services/common/tests/unit/test_blocklist_updater.js delete mode 100644 services/common/tests/unit/test_kinto.js (limited to 'services/common/tests/unit') diff --git a/services/common/tests/unit/test_blocklist_certificates.js b/services/common/tests/unit/test_blocklist_certificates.js deleted file mode 100644 index e85970321..000000000 --- a/services/common/tests/unit/test_blocklist_certificates.js +++ /dev/null @@ -1,224 +0,0 @@ -const { Constructor: CC } = Components; - -Cu.import("resource://testing-common/httpd.js"); - -const { OneCRLBlocklistClient } = Cu.import("resource://services-common/blocklist-clients.js"); -const { loadKinto } = Cu.import("resource://services-common/kinto-offline-client.js"); - -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", "setInputStream"); - -let server; - -// set up what we need to make storage adapters -const Kinto = loadKinto(); -const FirefoxAdapter = Kinto.adapters.FirefoxAdapter; -const kintoFilename = "kinto.sqlite"; - -let kintoClient; - -function do_get_kinto_collection(collectionName) { - if (!kintoClient) { - let config = { - // Set the remote to be some server that will cause test failure when - // hit since we should never hit the server directly, only via maybeSync() - remote: "https://firefox.settings.services.mozilla.com/v1/", - // Set up the adapter and bucket as normal - adapter: FirefoxAdapter, - bucket: "blocklists" - }; - kintoClient = new Kinto(config); - } - return kintoClient.collection(collectionName); -} - -// Some simple tests to demonstrate that the logic inside maybeSync works -// correctly and that simple kinto operations are working as expected. There -// are more tests for core Kinto.js (and its storage adapter) in the -// xpcshell tests under /services/common -add_task(function* test_something(){ - const configPath = "/v1/"; - const recordsPath = "/v1/buckets/blocklists/collections/certificates/records"; - - Services.prefs.setCharPref("services.settings.server", - `http://localhost:${server.identity.primaryPort}/v1`); - - // register a handler - function handleResponse (request, response) { - try { - const sample = getSampleResponse(request, server.identity.primaryPort); - if (!sample) { - do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`); - } - - response.setStatusLine(null, sample.status.status, - sample.status.statusText); - // send the headers - for (let headerLine of sample.sampleHeaders) { - let headerElements = headerLine.split(':'); - response.setHeader(headerElements[0], headerElements[1].trimLeft()); - } - response.setHeader("Date", (new Date()).toUTCString()); - - response.write(sample.responseBody); - } catch (e) { - do_print(e); - } - } - server.registerPathHandler(configPath, handleResponse); - server.registerPathHandler(recordsPath, handleResponse); - - // Test an empty db populates - let result = yield OneCRLBlocklistClient.maybeSync(2000, Date.now()); - - // Open the collection, verify it's been populated: - // Our test data has a single record; it should be in the local collection - let collection = do_get_kinto_collection("certificates"); - yield collection.db.open(); - let list = yield collection.list(); - do_check_eq(list.data.length, 1); - yield collection.db.close(); - - // Test the db is updated when we call again with a later lastModified value - result = yield OneCRLBlocklistClient.maybeSync(4000, Date.now()); - - // Open the collection, verify it's been updated: - // Our test data now has two records; both should be in the local collection - collection = do_get_kinto_collection("certificates"); - yield collection.db.open(); - list = yield collection.list(); - do_check_eq(list.data.length, 3); - yield collection.db.close(); - - // Try to maybeSync with the current lastModified value - no connection - // should be attempted. - // Clear the kinto base pref so any connections will cause a test failure - Services.prefs.clearUserPref("services.settings.server"); - yield OneCRLBlocklistClient.maybeSync(4000, Date.now()); - - // Try again with a lastModified value at some point in the past - yield OneCRLBlocklistClient.maybeSync(3000, Date.now()); - - // Check the OneCRL check time pref is modified, even if the collection - // hasn't changed - Services.prefs.setIntPref("services.blocklist.onecrl.checked", 0); - yield OneCRLBlocklistClient.maybeSync(3000, Date.now()); - let newValue = Services.prefs.getIntPref("services.blocklist.onecrl.checked"); - do_check_neq(newValue, 0); - - // Check that a sync completes even when there's bad data in the - // collection. This will throw on fail, so just calling maybeSync is an - // acceptible test. - Services.prefs.setCharPref("services.settings.server", - `http://localhost:${server.identity.primaryPort}/v1`); - yield OneCRLBlocklistClient.maybeSync(5000, Date.now()); -}); - -function run_test() { - // Ensure that signature verification is disabled to prevent interference - // with basic certificate sync tests - Services.prefs.setBoolPref("services.blocklist.signing.enforced", false); - - // Set up an HTTP Server - server = new HttpServer(); - server.start(-1); - - run_next_test(); - - do_register_cleanup(function() { - server.stop(() => { }); - }); -} - -// get a response for a given request from sample data -function getSampleResponse(req, port) { - const responses = { - "OPTIONS": { - "sampleHeaders": [ - "Access-Control-Allow-Headers: Content-Length,Expires,Backoff,Retry-After,Last-Modified,Total-Records,ETag,Pragma,Cache-Control,authorization,content-type,if-none-match,Alert,Next-Page", - "Access-Control-Allow-Methods: GET,HEAD,OPTIONS,POST,DELETE,OPTIONS", - "Access-Control-Allow-Origin: *", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": "null" - }, - "GET:/v1/?": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"settings":{"batch_max_requests":25}, "url":`http://localhost:${port}/v1/`, "documentation":"https://kinto.readthedocs.org/", "version":"1.5.1", "commit":"cbc6f58", "hello":"kinto"}) - }, - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"3000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "issuerName": "MEQxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xHjAcBgNVBAMTFXRoYXd0ZSBFViBTU0wgQ0EgLSBHMw==", - "serialNumber":"CrTHPEE6AZSfI3jysin2bA==", - "id":"78cf8900-fdea-4ce5-f8fb-b78710617718", - "last_modified":3000 - }]}) - }, - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=3000": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"4000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "issuerName":"MFkxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKjAoBgNVBAMTIVN0YWF0IGRlciBOZWRlcmxhbmRlbiBPdmVyaGVpZCBDQQ", - "serialNumber":"ATFpsA==", - "id":"dabafde9-df4a-ddba-2548-748da04cc02c", - "last_modified":4000 - },{ - "subject":"MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5", - "pubKeyHash":"VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=", - "id":"dabafde9-df4a-ddba-2548-748da04cc02d", - "last_modified":4000 - }]}) - }, - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=4000": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"5000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "issuerName":"not a base64 encoded issuer", - "serialNumber":"not a base64 encoded serial", - "id":"dabafde9-df4a-ddba-2548-748da04cc02e", - "last_modified":5000 - },{ - "subject":"not a base64 encoded subject", - "pubKeyHash":"not a base64 encoded pubKeyHash", - "id":"dabafde9-df4a-ddba-2548-748da04cc02f", - "last_modified":5000 - },{ - "subject":"MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5", - "pubKeyHash":"VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=", - "id":"dabafde9-df4a-ddba-2548-748da04cc02g", - "last_modified":5000 - }]}) - } - }; - return responses[`${req.method}:${req.path}?${req.queryString}`] || - responses[req.method]; - -} diff --git a/services/common/tests/unit/test_blocklist_clients.js b/services/common/tests/unit/test_blocklist_clients.js deleted file mode 100644 index 121fac926..000000000 --- a/services/common/tests/unit/test_blocklist_clients.js +++ /dev/null @@ -1,412 +0,0 @@ -const { Constructor: CC } = Components; - -const KEY_PROFILEDIR = "ProfD"; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://testing-common/httpd.js"); -Cu.import("resource://gre/modules/Timer.jsm"); -const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm"); -const { OS } = Cu.import("resource://gre/modules/osfile.jsm"); - -const { loadKinto } = Cu.import("resource://services-common/kinto-offline-client.js"); -const BlocklistClients = Cu.import("resource://services-common/blocklist-clients.js"); - -const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", "setInputStream"); - -const gBlocklistClients = [ - {client: BlocklistClients.AddonBlocklistClient, filename: BlocklistClients.FILENAME_ADDONS_JSON, testData: ["i808","i720", "i539"]}, - {client: BlocklistClients.PluginBlocklistClient, filename: BlocklistClients.FILENAME_PLUGINS_JSON, testData: ["p1044","p32","p28"]}, - {client: BlocklistClients.GfxBlocklistClient, filename: BlocklistClients.FILENAME_GFX_JSON, testData: ["g204","g200","g36"]}, -]; - - -let server; -let kintoClient; - -function kintoCollection(collectionName) { - if (!kintoClient) { - const Kinto = loadKinto(); - const FirefoxAdapter = Kinto.adapters.FirefoxAdapter; - const config = { - // Set the remote to be some server that will cause test failure when - // hit since we should never hit the server directly, only via maybeSync() - remote: "https://firefox.settings.services.mozilla.com/v1/", - adapter: FirefoxAdapter, - bucket: "blocklists" - }; - kintoClient = new Kinto(config); - } - return kintoClient.collection(collectionName); -} - -function* readJSON(filepath) { - const binaryData = yield OS.File.read(filepath); - const textData = (new TextDecoder()).decode(binaryData); - return Promise.resolve(JSON.parse(textData)); -} - -function* clear_state() { - for (let {client} of gBlocklistClients) { - // Remove last server times. - Services.prefs.clearUserPref(client.lastCheckTimePref); - - // Clear local DB. - const collection = kintoCollection(client.collectionName); - try { - yield collection.db.open(); - yield collection.clear(); - } finally { - yield collection.db.close(); - } - } - - // Remove profile data. - for (let {filename} of gBlocklistClients) { - const blocklist = FileUtils.getFile(KEY_PROFILEDIR, [filename]); - if (blocklist.exists()) { - blocklist.remove(true); - } - } -} - - -function run_test() { - // Set up an HTTP Server - server = new HttpServer(); - server.start(-1); - - // Point the blocklist clients to use this local HTTP server. - Services.prefs.setCharPref("services.settings.server", - `http://localhost:${server.identity.primaryPort}/v1`); - - // Setup server fake responses. - function handleResponse(request, response) { - try { - const sample = getSampleResponse(request, server.identity.primaryPort); - if (!sample) { - do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`); - } - - response.setStatusLine(null, sample.status.status, - sample.status.statusText); - // send the headers - for (let headerLine of sample.sampleHeaders) { - let headerElements = headerLine.split(':'); - response.setHeader(headerElements[0], headerElements[1].trimLeft()); - } - response.setHeader("Date", (new Date()).toUTCString()); - - response.write(sample.responseBody); - response.finish(); - } catch (e) { - do_print(e); - } - } - const configPath = "/v1/"; - const addonsRecordsPath = "/v1/buckets/blocklists/collections/addons/records"; - const gfxRecordsPath = "/v1/buckets/blocklists/collections/gfx/records"; - const pluginsRecordsPath = "/v1/buckets/blocklists/collections/plugins/records"; - server.registerPathHandler(configPath, handleResponse); - server.registerPathHandler(addonsRecordsPath, handleResponse); - server.registerPathHandler(gfxRecordsPath, handleResponse); - server.registerPathHandler(pluginsRecordsPath, handleResponse); - - - run_next_test(); - - do_register_cleanup(function() { - server.stop(() => { }); - }); -} - -add_task(function* test_records_obtained_from_server_are_stored_in_db(){ - for (let {client} of gBlocklistClients) { - // Test an empty db populates - let result = yield client.maybeSync(2000, Date.now()); - - // Open the collection, verify it's been populated: - // Our test data has a single record; it should be in the local collection - let collection = kintoCollection(client.collectionName); - yield collection.db.open(); - let list = yield collection.list(); - equal(list.data.length, 1); - yield collection.db.close(); - } -}); -add_task(clear_state); - -add_task(function* test_list_is_written_to_file_in_profile(){ - for (let {client, filename, testData} of gBlocklistClients) { - const profFile = FileUtils.getFile(KEY_PROFILEDIR, [filename]); - strictEqual(profFile.exists(), false); - - let result = yield client.maybeSync(2000, Date.now()); - - strictEqual(profFile.exists(), true); - const content = yield readJSON(profFile.path); - equal(content.data[0].blockID, testData[testData.length - 1]); - } -}); -add_task(clear_state); - -add_task(function* test_current_server_time_is_saved_in_pref(){ - for (let {client} of gBlocklistClients) { - const before = Services.prefs.getIntPref(client.lastCheckTimePref); - const serverTime = Date.now(); - yield client.maybeSync(2000, serverTime); - const after = Services.prefs.getIntPref(client.lastCheckTimePref); - equal(after, Math.round(serverTime / 1000)); - } -}); -add_task(clear_state); - -add_task(function* test_update_json_file_when_addons_has_changes(){ - for (let {client, filename, testData} of gBlocklistClients) { - yield client.maybeSync(2000, Date.now() - 1000); - const before = Services.prefs.getIntPref(client.lastCheckTimePref); - const profFile = FileUtils.getFile(KEY_PROFILEDIR, [filename]); - const fileLastModified = profFile.lastModifiedTime = profFile.lastModifiedTime - 1000; - const serverTime = Date.now(); - - yield client.maybeSync(3001, serverTime); - - // File was updated. - notEqual(fileLastModified, profFile.lastModifiedTime); - const content = yield readJSON(profFile.path); - deepEqual(content.data.map((r) => r.blockID), testData); - // Server time was updated. - const after = Services.prefs.getIntPref(client.lastCheckTimePref); - equal(after, Math.round(serverTime / 1000)); - } -}); -add_task(clear_state); - -add_task(function* test_sends_reload_message_when_blocklist_has_changes(){ - for (let {client, filename} of gBlocklistClients) { - let received = yield new Promise((resolve, reject) => { - Services.ppmm.addMessageListener("Blocklist:reload-from-disk", { - receiveMessage(aMsg) { resolve(aMsg) } - }); - - client.maybeSync(2000, Date.now() - 1000); - }); - - equal(received.data.filename, filename); - } -}); -add_task(clear_state); - -add_task(function* test_do_nothing_when_blocklist_is_up_to_date(){ - for (let {client, filename} of gBlocklistClients) { - yield client.maybeSync(2000, Date.now() - 1000); - const before = Services.prefs.getIntPref(client.lastCheckTimePref); - const profFile = FileUtils.getFile(KEY_PROFILEDIR, [filename]); - const fileLastModified = profFile.lastModifiedTime = profFile.lastModifiedTime - 1000; - const serverTime = Date.now(); - - yield client.maybeSync(3000, serverTime); - - // File was not updated. - equal(fileLastModified, profFile.lastModifiedTime); - // Server time was updated. - const after = Services.prefs.getIntPref(client.lastCheckTimePref); - equal(after, Math.round(serverTime / 1000)); - } -}); -add_task(clear_state); - - - -// get a response for a given request from sample data -function getSampleResponse(req, port) { - const responses = { - "OPTIONS": { - "sampleHeaders": [ - "Access-Control-Allow-Headers: Content-Length,Expires,Backoff,Retry-After,Last-Modified,Total-Records,ETag,Pragma,Cache-Control,authorization,content-type,if-none-match,Alert,Next-Page", - "Access-Control-Allow-Methods: GET,HEAD,OPTIONS,POST,DELETE,OPTIONS", - "Access-Control-Allow-Origin: *", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": "null" - }, - "GET:/v1/?": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"settings":{"batch_max_requests":25}, "url":`http://localhost:${port}/v1/`, "documentation":"https://kinto.readthedocs.org/", "version":"1.5.1", "commit":"cbc6f58", "hello":"kinto"}) - }, - "GET:/v1/buckets/blocklists/collections/addons/records?_sort=-last_modified": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"3000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "prefs": [], - "blockID": "i539", - "last_modified": 3000, - "versionRange": [{ - "targetApplication": [], - "maxVersion": "*", - "minVersion": "0", - "severity": "1" - }], - "guid": "ScorpionSaver@jetpack", - "id": "9d500963-d80e-3a91-6e74-66f3811b99cc" - }]}) - }, - "GET:/v1/buckets/blocklists/collections/plugins/records?_sort=-last_modified": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"3000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "matchFilename": "NPFFAddOn.dll", - "blockID": "p28", - "id": "7b1e0b3c-e390-a817-11b6-a6887f65f56e", - "last_modified": 3000, - "versionRange": [] - }]}) - }, - "GET:/v1/buckets/blocklists/collections/gfx/records?_sort=-last_modified": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"3000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "driverVersionComparator": "LESS_THAN_OR_EQUAL", - "driverVersion": "8.17.12.5896", - "vendor": "0x10de", - "blockID": "g36", - "feature": "DIRECT3D_9_LAYERS", - "devices": ["0x0a6c"], - "featureStatus": "BLOCKED_DRIVER_VERSION", - "last_modified": 3000, - "os": "WINNT 6.1", - "id": "3f947f16-37c2-4e96-d356-78b26363729b" - }]}) - }, - "GET:/v1/buckets/blocklists/collections/addons/records?_sort=-last_modified&_since=3000": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"4000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "prefs": [], - "blockID": "i808", - "last_modified": 4000, - "versionRange": [{ - "targetApplication": [], - "maxVersion": "*", - "minVersion": "0", - "severity": "3" - }], - "guid": "{c96d1ae6-c4cf-4984-b110-f5f561b33b5a}", - "id": "9ccfac91-e463-c30c-f0bd-14143794a8dd" - }, { - "prefs": ["browser.startup.homepage"], - "blockID": "i720", - "last_modified": 3500, - "versionRange": [{ - "targetApplication": [], - "maxVersion": "*", - "minVersion": "0", - "severity": "1" - }], - "guid": "FXqG@xeeR.net", - "id": "cf9b3129-a97e-dbd7-9525-a8575ac03c25" - }]}) - }, - "GET:/v1/buckets/blocklists/collections/plugins/records?_sort=-last_modified&_since=3000": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"4000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "infoURL": "https://get.adobe.com/flashplayer/", - "blockID": "p1044", - "matchFilename": "libflashplayer\\.so", - "last_modified": 4000, - "versionRange": [{ - "targetApplication": [], - "minVersion": "11.2.202.509", - "maxVersion": "11.2.202.539", - "severity": "0", - "vulnerabilityStatus": "1" - }], - "os": "Linux", - "id": "aabad965-e556-ffe7-4191-074f5dee3df3" - }, { - "matchFilename": "npViewpoint.dll", - "blockID": "p32", - "id": "1f48af42-c508-b8ef-b8d5-609d48e4f6c9", - "last_modified": 3500, - "versionRange": [{ - "targetApplication": [{ - "minVersion": "3.0", - "guid": "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}", - "maxVersion": "*" - }] - }] - }]}) - }, - "GET:/v1/buckets/blocklists/collections/gfx/records?_sort=-last_modified&_since=3000": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"4000\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{ - "vendor": "0x8086", - "blockID": "g204", - "feature": "WEBGL_MSAA", - "devices": [], - "id": "c96bca82-e6bd-044d-14c4-9c1d67e9283a", - "last_modified": 4000, - "os": "Darwin 10", - "featureStatus": "BLOCKED_DEVICE" - }, { - "vendor": "0x10de", - "blockID": "g200", - "feature": "WEBGL_MSAA", - "devices": [], - "id": "c3a15ba9-e0e2-421f-e399-c995e5b8d14e", - "last_modified": 3500, - "os": "Darwin 11", - "featureStatus": "BLOCKED_DEVICE" - }]}) - } - }; - return responses[`${req.method}:${req.path}?${req.queryString}`] || - responses[req.method]; - -} diff --git a/services/common/tests/unit/test_blocklist_signatures.js b/services/common/tests/unit/test_blocklist_signatures.js deleted file mode 100644 index b2ee1019a..000000000 --- a/services/common/tests/unit/test_blocklist_signatures.js +++ /dev/null @@ -1,510 +0,0 @@ -"use strict"; - -Cu.import("resource://services-common/blocklist-updater.js"); -Cu.import("resource://testing-common/httpd.js"); - -const { loadKinto } = Cu.import("resource://services-common/kinto-offline-client.js"); -const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); -const { OneCRLBlocklistClient } = Cu.import("resource://services-common/blocklist-clients.js"); - -let server; - -const PREF_BLOCKLIST_BUCKET = "services.blocklist.bucket"; -const PREF_BLOCKLIST_ENFORCE_SIGNING = "services.blocklist.signing.enforced"; -const PREF_BLOCKLIST_ONECRL_COLLECTION = "services.blocklist.onecrl.collection"; -const PREF_SETTINGS_SERVER = "services.settings.server"; -const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash"; - - -const CERT_DIR = "test_blocklist_signatures/"; -const CHAIN_FILES = - ["collection_signing_ee.pem", - "collection_signing_int.pem", - "collection_signing_root.pem"]; - -function getFileData(file) { - const stream = Cc["@mozilla.org/network/file-input-stream;1"] - .createInstance(Ci.nsIFileInputStream); - stream.init(file, -1, 0, 0); - const data = NetUtil.readInputStreamToString(stream, stream.available()); - stream.close(); - return data; -} - -function setRoot() { - const filename = CERT_DIR + CHAIN_FILES[0]; - - const certFile = do_get_file(filename, false); - const b64cert = getFileData(certFile) - .replace(/-----BEGIN CERTIFICATE-----/, "") - .replace(/-----END CERTIFICATE-----/, "") - .replace(/[\r\n]/g, ""); - const certdb = Cc["@mozilla.org/security/x509certdb;1"] - .getService(Ci.nsIX509CertDB); - const cert = certdb.constructX509FromBase64(b64cert); - Services.prefs.setCharPref(PREF_SIGNATURE_ROOT, cert.sha256Fingerprint); -} - -function getCertChain() { - const chain = []; - for (let file of CHAIN_FILES) { - chain.push(getFileData(do_get_file(CERT_DIR + file))); - } - return chain.join("\n"); -} - -function* checkRecordCount(count) { - // open the collection manually - const base = Services.prefs.getCharPref(PREF_SETTINGS_SERVER); - const bucket = Services.prefs.getCharPref(PREF_BLOCKLIST_BUCKET); - const collectionName = - Services.prefs.getCharPref(PREF_BLOCKLIST_ONECRL_COLLECTION); - - const Kinto = loadKinto(); - - const FirefoxAdapter = Kinto.adapters.FirefoxAdapter; - - const config = { - remote: base, - bucket: bucket, - adapter: FirefoxAdapter, - }; - - const db = new Kinto(config); - const collection = db.collection(collectionName); - - yield collection.db.open(); - - // Check we have the expected number of records - let records = yield collection.list(); - do_check_eq(count, records.data.length); - - // Close the collection so the test can exit cleanly - yield collection.db.close(); -} - -// Check to ensure maybeSync is called with correct values when a changes -// document contains information on when a collection was last modified -add_task(function* test_check_signatures(){ - const port = server.identity.primaryPort; - - // a response to give the client when the cert chain is expected - function makeMetaResponseBody(lastModified, signature) { - return { - data: { - id: "certificates", - last_modified: lastModified, - signature: { - x5u: `http://localhost:${port}/test_blocklist_signatures/test_cert_chain.pem`, - public_key: "fake", - "content-signature": `x5u=http://localhost:${port}/test_blocklist_signatures/test_cert_chain.pem;p384ecdsa=${signature}`, - signature_encoding: "rs_base64url", - signature: signature, - hash_algorithm: "sha384", - ref: "1yryrnmzou5rf31ou80znpnq8n" - } - } - }; - } - - function makeMetaResponse(eTag, body, comment) { - return { - comment: comment, - sampleHeaders: [ - "Content-Type: application/json; charset=UTF-8", - `ETag: \"${eTag}\"` - ], - status: {status: 200, statusText: "OK"}, - responseBody: JSON.stringify(body) - }; - } - - function registerHandlers(responses){ - function handleResponse (serverTimeMillis, request, response) { - const key = `${request.method}:${request.path}?${request.queryString}`; - const available = responses[key]; - const sampled = available.length > 1 ? available.shift() : available[0]; - - if (!sampled) { - do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`); - } - - response.setStatusLine(null, sampled.status.status, - sampled.status.statusText); - // send the headers - for (let headerLine of sampled.sampleHeaders) { - let headerElements = headerLine.split(':'); - response.setHeader(headerElements[0], headerElements[1].trimLeft()); - } - - // set the server date - response.setHeader("Date", (new Date(serverTimeMillis)).toUTCString()); - - response.write(sampled.responseBody); - } - - for (let key of Object.keys(responses)) { - const keyParts = key.split(":"); - const method = keyParts[0]; - const valueParts = keyParts[1].split("?"); - const path = valueParts[0]; - - server.registerPathHandler(path, handleResponse.bind(null, 2000)); - } - } - - // First, perform a signature verification with known data and signature - // to ensure things are working correctly - let verifier = Cc["@mozilla.org/security/contentsignatureverifier;1"] - .createInstance(Ci.nsIContentSignatureVerifier); - - const emptyData = '[]'; - const emptySignature = "p384ecdsa=zbugm2FDitsHwk5-IWsas1PpWwY29f0Fg5ZHeqD8fzep7AVl2vfcaHA7LdmCZ28qZLOioGKvco3qT117Q4-HlqFTJM7COHzxGyU2MMJ0ZTnhJrPOC1fP3cVQjU1PTWi9"; - const name = "onecrl.content-signature.mozilla.org"; - ok(verifier.verifyContentSignature(emptyData, emptySignature, - getCertChain(), name)); - - verifier = Cc["@mozilla.org/security/contentsignatureverifier;1"] - .createInstance(Ci.nsIContentSignatureVerifier); - - const collectionData = '[{"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","created":"2016-01-18T14:43:37Z","name":"GlobalSign certs","who":".","why":"."},"enabled":true,"id":"97fbf7c4-3ef2-f54f-0029-1ba6540c63ea","issuerName":"MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==","last_modified":2000,"serialNumber":"BAAAAAABA/A35EU="},{"details":{"bug":"https://bugzilla.mozilla.org/show_bug.cgi?id=1155145","created":"2016-01-18T14:48:11Z","name":"GlobalSign certs","who":".","why":"."},"enabled":true,"id":"e3bd531e-1ee4-7407-27ce-6fdc9cecbbdc","issuerName":"MIGBMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTElMCMGA1UECxMcUHJpbWFyeSBPYmplY3QgUHVibGlzaGluZyBDQTEwMC4GA1UEAxMnR2xvYmFsU2lnbiBQcmltYXJ5IE9iamVjdCBQdWJsaXNoaW5nIENB","last_modified":3000,"serialNumber":"BAAAAAABI54PryQ="}]'; - const collectionSignature = "p384ecdsa=f4pA2tYM5jQgWY6YUmhUwQiBLj6QO5sHLD_5MqLePz95qv-7cNCuQoZnPQwxoptDtW8hcWH3kLb0quR7SB-r82gkpR9POVofsnWJRA-ETb0BcIz6VvI3pDT49ZLlNg3p"; - - ok(verifier.verifyContentSignature(collectionData, collectionSignature, getCertChain(), name)); - - // set up prefs so the kinto updater talks to the test server - Services.prefs.setCharPref(PREF_SETTINGS_SERVER, - `http://localhost:${server.identity.primaryPort}/v1`); - - // Set up some data we need for our test - let startTime = Date.now(); - - // These are records we'll use in the test collections - const RECORD1 = { - details: { - bug: "https://bugzilla.mozilla.org/show_bug.cgi?id=1155145", - created: "2016-01-18T14:43:37Z", - name: "GlobalSign certs", - who: ".", - why: "." - }, - enabled: true, - id: "97fbf7c4-3ef2-f54f-0029-1ba6540c63ea", - issuerName: "MHExKDAmBgNVBAMTH0dsb2JhbFNpZ24gUm9vdFNpZ24gUGFydG5lcnMgQ0ExHTAbBgNVBAsTFFJvb3RTaWduIFBhcnRuZXJzIENBMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMQswCQYDVQQGEwJCRQ==", - last_modified: 2000, - serialNumber: "BAAAAAABA/A35EU=" - }; - - const RECORD2 = { - details: { - bug: "https://bugzilla.mozilla.org/show_bug.cgi?id=1155145", - created: "2016-01-18T14:48:11Z", - name: "GlobalSign certs", - who: ".", - why: "." - }, - enabled: true, - id: "e3bd531e-1ee4-7407-27ce-6fdc9cecbbdc", - issuerName: "MIGBMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTElMCMGA1UECxMcUHJpbWFyeSBPYmplY3QgUHVibGlzaGluZyBDQTEwMC4GA1UEAxMnR2xvYmFsU2lnbiBQcmltYXJ5IE9iamVjdCBQdWJsaXNoaW5nIENB", - last_modified: 3000, - serialNumber: "BAAAAAABI54PryQ=" - }; - - const RECORD3 = { - details: { - bug: "https://bugzilla.mozilla.org/show_bug.cgi?id=1155145", - created: "2016-01-18T14:48:11Z", - name: "GlobalSign certs", - who: ".", - why: "." - }, - enabled: true, - id: "c7c49b69-a4ab-418e-92a9-e1961459aa7f", - issuerName: "MIGBMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTElMCMGA1UECxMcUHJpbWFyeSBPYmplY3QgUHVibGlzaGluZyBDQTEwMC4GA1UEAxMnR2xvYmFsU2lnbiBQcmltYXJ5IE9iamVjdCBQdWJsaXNoaW5nIENB", - last_modified: 4000, - serialNumber: "BAAAAAABI54PryQ=" - }; - - const RECORD1_DELETION = { - deleted: true, - enabled: true, - id: "97fbf7c4-3ef2-f54f-0029-1ba6540c63ea", - last_modified: 3500, - }; - - // Check that a signature on an empty collection is OK - // We need to set up paths on the HTTP server to return specific data from - // specific paths for each test. Here we prepare data for each response. - - // A cert chain response (this the cert chain that contains the signing - // cert, the root and any intermediates in between). This is used in each - // sync. - const RESPONSE_CERT_CHAIN = { - comment: "RESPONSE_CERT_CHAIN", - sampleHeaders: [ - "Content-Type: text/plain; charset=UTF-8" - ], - status: {status: 200, statusText: "OK"}, - responseBody: getCertChain() - }; - - // A server settings response. This is used in each sync. - const RESPONSE_SERVER_SETTINGS = { - comment: "RESPONSE_SERVER_SETTINGS", - sampleHeaders: [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress" - ], - status: {status: 200, statusText: "OK"}, - responseBody: JSON.stringify({"settings":{"batch_max_requests":25}, "url":`http://localhost:${port}/v1/`, "documentation":"https://kinto.readthedocs.org/", "version":"1.5.1", "commit":"cbc6f58", "hello":"kinto"}) - }; - - // This is the initial, empty state of the collection. This is only used - // for the first sync. - const RESPONSE_EMPTY_INITIAL = { - comment: "RESPONSE_EMPTY_INITIAL", - sampleHeaders: [ - "Content-Type: application/json; charset=UTF-8", - "ETag: \"1000\"" - ], - status: {status: 200, statusText: "OK"}, - responseBody: JSON.stringify({"data": []}) - }; - - const RESPONSE_BODY_META_EMPTY_SIG = makeMetaResponseBody(1000, - "vxuAg5rDCB-1pul4a91vqSBQRXJG_j7WOYUTswxRSMltdYmbhLRH8R8brQ9YKuNDF56F-w6pn4HWxb076qgKPwgcEBtUeZAO_RtaHXRkRUUgVzAr86yQL4-aJTbv3D6u"); - - // The collection metadata containing the signature for the empty - // collection. - const RESPONSE_META_EMPTY_SIG = - makeMetaResponse(1000, RESPONSE_BODY_META_EMPTY_SIG, - "RESPONSE_META_EMPTY_SIG"); - - // Here, we map request method and path to the available responses - const emptyCollectionResponses = { - "GET:/test_blocklist_signatures/test_cert_chain.pem?":[RESPONSE_CERT_CHAIN], - "GET:/v1/?": [RESPONSE_SERVER_SETTINGS], - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified": - [RESPONSE_EMPTY_INITIAL], - "GET:/v1/buckets/blocklists/collections/certificates?": - [RESPONSE_META_EMPTY_SIG] - }; - - // .. and use this map to register handlers for each path - registerHandlers(emptyCollectionResponses); - - // With all of this set up, we attempt a sync. This will resolve if all is - // well and throw if something goes wrong. - yield OneCRLBlocklistClient.maybeSync(1000, startTime); - - // Check that some additions (2 records) to the collection have a valid - // signature. - - // This response adds two entries (RECORD1 and RECORD2) to the collection - const RESPONSE_TWO_ADDED = { - comment: "RESPONSE_TWO_ADDED", - sampleHeaders: [ - "Content-Type: application/json; charset=UTF-8", - "ETag: \"3000\"" - ], - status: {status: 200, statusText: "OK"}, - responseBody: JSON.stringify({"data": [RECORD2, RECORD1]}) - }; - - const RESPONSE_BODY_META_TWO_ITEMS_SIG = makeMetaResponseBody(3000, - "dwhJeypadNIyzGj3QdI0KMRTPnHhFPF_j73mNrsPAHKMW46S2Ftf4BzsPMvPMB8h0TjDus13wo_R4l432DHe7tYyMIWXY0PBeMcoe5BREhFIxMxTsh9eGVXBD1e3UwRy"); - - // A signature response for the collection containg RECORD1 and RECORD2 - const RESPONSE_META_TWO_ITEMS_SIG = - makeMetaResponse(3000, RESPONSE_BODY_META_TWO_ITEMS_SIG, - "RESPONSE_META_TWO_ITEMS_SIG"); - - const twoItemsResponses = { - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=1000": - [RESPONSE_TWO_ADDED], - "GET:/v1/buckets/blocklists/collections/certificates?": - [RESPONSE_META_TWO_ITEMS_SIG] - }; - registerHandlers(twoItemsResponses); - yield OneCRLBlocklistClient.maybeSync(3000, startTime); - - // Check the collection with one addition and one removal has a valid - // signature - - // Remove RECORD1, add RECORD3 - const RESPONSE_ONE_ADDED_ONE_REMOVED = { - comment: "RESPONSE_ONE_ADDED_ONE_REMOVED ", - sampleHeaders: [ - "Content-Type: application/json; charset=UTF-8", - "ETag: \"4000\"" - ], - status: {status: 200, statusText: "OK"}, - responseBody: JSON.stringify({"data": [RECORD3, RECORD1_DELETION]}) - }; - - const RESPONSE_BODY_META_THREE_ITEMS_SIG = makeMetaResponseBody(4000, - "MIEmNghKnkz12UodAAIc3q_Y4a3IJJ7GhHF4JYNYmm8avAGyPM9fYU7NzVo94pzjotG7vmtiYuHyIX2rTHTbT587w0LdRWxipgFd_PC1mHiwUyjFYNqBBG-kifYk7kEw"); - - // signature response for the collection containing RECORD2 and RECORD3 - const RESPONSE_META_THREE_ITEMS_SIG = - makeMetaResponse(4000, RESPONSE_BODY_META_THREE_ITEMS_SIG, - "RESPONSE_META_THREE_ITEMS_SIG"); - - const oneAddedOneRemovedResponses = { - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=3000": - [RESPONSE_ONE_ADDED_ONE_REMOVED], - "GET:/v1/buckets/blocklists/collections/certificates?": - [RESPONSE_META_THREE_ITEMS_SIG] - }; - registerHandlers(oneAddedOneRemovedResponses); - yield OneCRLBlocklistClient.maybeSync(4000, startTime); - - // Check the signature is still valid with no operation (no changes) - - // Leave the collection unchanged - const RESPONSE_EMPTY_NO_UPDATE = { - comment: "RESPONSE_EMPTY_NO_UPDATE ", - sampleHeaders: [ - "Content-Type: application/json; charset=UTF-8", - "ETag: \"4000\"" - ], - status: {status: 200, statusText: "OK"}, - responseBody: JSON.stringify({"data": []}) - }; - - const noOpResponses = { - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=4000": - [RESPONSE_EMPTY_NO_UPDATE], - "GET:/v1/buckets/blocklists/collections/certificates?": - [RESPONSE_META_THREE_ITEMS_SIG] - }; - registerHandlers(noOpResponses); - yield OneCRLBlocklistClient.maybeSync(4100, startTime); - - // Check the collection is reset when the signature is invalid - - // Prepare a (deliberately) bad signature to check the collection state is - // reset if something is inconsistent - const RESPONSE_COMPLETE_INITIAL = { - comment: "RESPONSE_COMPLETE_INITIAL ", - sampleHeaders: [ - "Content-Type: application/json; charset=UTF-8", - "ETag: \"4000\"" - ], - status: {status: 200, statusText: "OK"}, - responseBody: JSON.stringify({"data": [RECORD2, RECORD3]}) - }; - - const RESPONSE_COMPLETE_INITIAL_SORTED_BY_ID = { - comment: "RESPONSE_COMPLETE_INITIAL ", - sampleHeaders: [ - "Content-Type: application/json; charset=UTF-8", - "ETag: \"4000\"" - ], - status: {status: 200, statusText: "OK"}, - responseBody: JSON.stringify({"data": [RECORD3, RECORD2]}) - }; - - const RESPONSE_BODY_META_BAD_SIG = makeMetaResponseBody(4000, - "aW52YWxpZCBzaWduYXR1cmUK"); - - const RESPONSE_META_BAD_SIG = - makeMetaResponse(4000, RESPONSE_BODY_META_BAD_SIG, "RESPONSE_META_BAD_SIG"); - - const badSigGoodSigResponses = { - // In this test, we deliberately serve a bad signature initially. The - // subsequent signature returned is a valid one for the three item - // collection. - "GET:/v1/buckets/blocklists/collections/certificates?": - [RESPONSE_META_BAD_SIG, RESPONSE_META_THREE_ITEMS_SIG], - // The first collection state is the three item collection (since - // there's a sync with no updates) - but, since the signature is wrong, - // another request will be made... - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=4000": - [RESPONSE_EMPTY_NO_UPDATE], - // The next request is for the full collection. This will be checked - // against the valid signature - so the sync should succeed. - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified": - [RESPONSE_COMPLETE_INITIAL], - // The next request is for the full collection sorted by id. This will be - // checked against the valid signature - so the sync should succeed. - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=id": - [RESPONSE_COMPLETE_INITIAL_SORTED_BY_ID] - }; - - registerHandlers(badSigGoodSigResponses); - yield OneCRLBlocklistClient.maybeSync(5000, startTime); - - const badSigGoodOldResponses = { - // In this test, we deliberately serve a bad signature initially. The - // subsequent sitnature returned is a valid one for the three item - // collection. - "GET:/v1/buckets/blocklists/collections/certificates?": - [RESPONSE_META_BAD_SIG, RESPONSE_META_EMPTY_SIG], - // The first collection state is the current state (since there's no update - // - but, since the signature is wrong, another request will be made) - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=4000": - [RESPONSE_EMPTY_NO_UPDATE], - // The next request is for the full collection sorted by id. This will be - // checked against the valid signature and last_modified times will be - // compared. Sync should fail, even though the signature is good, - // because the local collection is newer. - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=id": - [RESPONSE_EMPTY_INITIAL], - }; - - // ensure our collection hasn't been replaced with an older, empty one - yield checkRecordCount(2); - - registerHandlers(badSigGoodOldResponses); - yield OneCRLBlocklistClient.maybeSync(5000, startTime); - - const allBadSigResponses = { - // In this test, we deliberately serve only a bad signature. - "GET:/v1/buckets/blocklists/collections/certificates?": - [RESPONSE_META_BAD_SIG], - // The first collection state is the three item collection (since - // there's a sync with no updates) - but, since the signature is wrong, - // another request will be made... - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=-last_modified&_since=4000": - [RESPONSE_EMPTY_NO_UPDATE], - // The next request is for the full collection sorted by id. This will be - // checked against the valid signature - so the sync should succeed. - "GET:/v1/buckets/blocklists/collections/certificates/records?_sort=id": - [RESPONSE_COMPLETE_INITIAL_SORTED_BY_ID] - }; - - registerHandlers(allBadSigResponses); - try { - yield OneCRLBlocklistClient.maybeSync(6000, startTime); - do_throw("Sync should fail (the signature is intentionally bad)"); - } catch (e) { - yield checkRecordCount(2); - } -}); - -function run_test() { - // ensure signatures are enforced - Services.prefs.setBoolPref(PREF_BLOCKLIST_ENFORCE_SIGNING, true); - - // get a signature verifier to ensure nsNSSComponent is initialized - Cc["@mozilla.org/security/contentsignatureverifier;1"] - .createInstance(Ci.nsIContentSignatureVerifier); - - // set the content signing root to our test root - setRoot(); - - // Set up an HTTP Server - server = new HttpServer(); - server.start(-1); - - run_next_test(); - - do_register_cleanup(function() { - server.stop(function() { }); - }); -} - - diff --git a/services/common/tests/unit/test_blocklist_updater.js b/services/common/tests/unit/test_blocklist_updater.js deleted file mode 100644 index 1b71c194a..000000000 --- a/services/common/tests/unit/test_blocklist_updater.js +++ /dev/null @@ -1,173 +0,0 @@ -Cu.import("resource://testing-common/httpd.js"); - -var server; - -const PREF_SETTINGS_SERVER = "services.settings.server"; -const PREF_LAST_UPDATE = "services.blocklist.last_update_seconds"; -const PREF_LAST_ETAG = "services.blocklist.last_etag"; -const PREF_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds"; - -// Check to ensure maybeSync is called with correct values when a changes -// document contains information on when a collection was last modified -add_task(function* test_check_maybeSync(){ - const changesPath = "/v1/buckets/monitor/collections/changes/records"; - - // register a handler - function handleResponse (serverTimeMillis, request, response) { - try { - const sampled = getSampleResponse(request, server.identity.primaryPort); - if (!sampled) { - do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`); - } - - response.setStatusLine(null, sampled.status.status, - sampled.status.statusText); - // send the headers - for (let headerLine of sampled.sampleHeaders) { - let headerElements = headerLine.split(':'); - response.setHeader(headerElements[0], headerElements[1].trimLeft()); - } - - // set the server date - response.setHeader("Date", (new Date(serverTimeMillis)).toUTCString()); - - response.write(sampled.responseBody); - } catch (e) { - dump(`${e}\n`); - } - } - - server.registerPathHandler(changesPath, handleResponse.bind(null, 2000)); - - // set up prefs so the kinto updater talks to the test server - Services.prefs.setCharPref(PREF_SETTINGS_SERVER, - `http://localhost:${server.identity.primaryPort}/v1`); - - // set some initial values so we can check these are updated appropriately - Services.prefs.setIntPref(PREF_LAST_UPDATE, 0); - Services.prefs.setIntPref(PREF_CLOCK_SKEW_SECONDS, 0); - Services.prefs.clearUserPref(PREF_LAST_ETAG); - - - let startTime = Date.now(); - - let updater = Cu.import("resource://services-common/blocklist-updater.js"); - - let syncPromise = new Promise(function(resolve, reject) { - // add a test kinto client that will respond to lastModified information - // for a collection called 'test-collection' - updater.addTestBlocklistClient("test-collection", { - maybeSync(lastModified, serverTime) { - do_check_eq(lastModified, 1000); - do_check_eq(serverTime, 2000); - resolve(); - } - }); - updater.checkVersions(); - }); - - // ensure we get the maybeSync call - yield syncPromise; - - // check the last_update is updated - do_check_eq(Services.prefs.getIntPref(PREF_LAST_UPDATE), 2); - - // How does the clock difference look? - let endTime = Date.now(); - let clockDifference = Services.prefs.getIntPref(PREF_CLOCK_SKEW_SECONDS); - // we previously set the serverTime to 2 (seconds past epoch) - do_check_true(clockDifference <= endTime / 1000 - && clockDifference >= Math.floor(startTime / 1000) - 2); - // Last timestamp was saved. An ETag header value is a quoted string. - let lastEtag = Services.prefs.getCharPref(PREF_LAST_ETAG); - do_check_eq(lastEtag, "\"1100\""); - - // Simulate a poll with up-to-date collection. - Services.prefs.setIntPref(PREF_LAST_UPDATE, 0); - // If server has no change, a 304 is received, maybeSync() is not called. - updater.addTestBlocklistClient("test-collection", { - maybeSync: () => {throw new Error("Should not be called");} - }); - yield updater.checkVersions(); - // Last update is overwritten - do_check_eq(Services.prefs.getIntPref(PREF_LAST_UPDATE), 2); - - - // Simulate a server error. - function simulateErrorResponse (request, response) { - response.setHeader("Date", (new Date(3000)).toUTCString()); - response.setHeader("Content-Type", "application/json; charset=UTF-8"); - response.write(JSON.stringify({ - code: 503, - errno: 999, - error: "Service Unavailable", - })); - response.setStatusLine(null, 503, "Service Unavailable"); - } - server.registerPathHandler(changesPath, simulateErrorResponse); - // checkVersions() fails with adequate error. - let error; - try { - yield updater.checkVersions(); - } catch (e) { - error = e; - } - do_check_eq(error.message, "Polling for changes failed."); - // When an error occurs, last update was not overwritten (see Date header above). - do_check_eq(Services.prefs.getIntPref(PREF_LAST_UPDATE), 2); - - // check negative clock skew times - - // set to a time in the future - server.registerPathHandler(changesPath, handleResponse.bind(null, Date.now() + 10000)); - - yield updater.checkVersions(); - - clockDifference = Services.prefs.getIntPref(PREF_CLOCK_SKEW_SECONDS); - // we previously set the serverTime to Date.now() + 10000 ms past epoch - do_check_true(clockDifference <= 0 && clockDifference >= -10); -}); - -function run_test() { - // Set up an HTTP Server - server = new HttpServer(); - server.start(-1); - - run_next_test(); - - do_register_cleanup(function() { - server.stop(function() { }); - }); -} - -// get a response for a given request from sample data -function getSampleResponse(req, port) { - const responses = { - "GET:/v1/buckets/monitor/collections/changes/records?": { - "sampleHeaders": [ - "Content-Type: application/json; charset=UTF-8", - "ETag: \"1100\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data": [{ - "host": "localhost", - "last_modified": 1100, - "bucket": "blocklists:aurora", - "id": "330a0c5f-fadf-ff0b-40c8-4eb0d924ff6a", - "collection": "test-collection" - }, { - "host": "localhost", - "last_modified": 1000, - "bucket": "blocklists", - "id": "254cbb9e-6888-4d9f-8e60-58b74faa8778", - "collection": "test-collection" - }]}) - } - }; - - if (req.hasHeader("if-none-match") && req.getHeader("if-none-match", "") == "\"1100\"") - return {sampleHeaders: [], status: {status: 304, statusText: "Not Modified"}, responseBody: ""}; - - return responses[`${req.method}:${req.path}?${req.queryString}`] || - responses[req.method]; -} diff --git a/services/common/tests/unit/test_kinto.js b/services/common/tests/unit/test_kinto.js deleted file mode 100644 index 9c5ce58d9..000000000 --- a/services/common/tests/unit/test_kinto.js +++ /dev/null @@ -1,412 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -Cu.import("resource://services-common/kinto-offline-client.js"); -Cu.import("resource://testing-common/httpd.js"); - -const BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1", - "nsIBinaryInputStream", "setInputStream"); - -var server; - -// set up what we need to make storage adapters -const Kinto = loadKinto(); -const FirefoxAdapter = Kinto.adapters.FirefoxAdapter; -const kintoFilename = "kinto.sqlite"; - -let kintoClient; - -function do_get_kinto_collection() { - if (!kintoClient) { - let config = { - remote:`http://localhost:${server.identity.primaryPort}/v1/`, - headers: {Authorization: "Basic " + btoa("user:pass")}, - adapter: FirefoxAdapter - }; - kintoClient = new Kinto(config); - } - return kintoClient.collection("test_collection"); -} - -function* clear_collection() { - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - yield collection.clear(); - } finally { - yield collection.db.close(); - } -} - -// test some operations on a local collection -add_task(function* test_kinto_add_get() { - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - - let newRecord = { foo: "bar" }; - // check a record is created - let createResult = yield collection.create(newRecord); - do_check_eq(createResult.data.foo, newRecord.foo); - // check getting the record gets the same info - let getResult = yield collection.get(createResult.data.id); - deepEqual(createResult.data, getResult.data); - // check what happens if we create the same item again (it should throw - // since you can't create with id) - try { - yield collection.create(createResult.data); - do_throw("Creation of a record with an id should fail"); - } catch (err) { } - // try a few creates without waiting for the first few to resolve - let promises = []; - promises.push(collection.create(newRecord)); - promises.push(collection.create(newRecord)); - promises.push(collection.create(newRecord)); - yield collection.create(newRecord); - yield Promise.all(promises); - } finally { - yield collection.db.close(); - } -}); - -add_task(clear_collection); - -// test some operations on multiple connections -add_task(function* test_kinto_add_get() { - const collection1 = do_get_kinto_collection(); - const collection2 = kintoClient.collection("test_collection_2"); - - try { - yield collection1.db.open(); - yield collection2.db.open(); - - let newRecord = { foo: "bar" }; - - // perform several write operations alternately without waiting for promises - // to resolve - let promises = []; - for (let i = 0; i < 10; i++) { - promises.push(collection1.create(newRecord)); - promises.push(collection2.create(newRecord)); - } - - // ensure subsequent operations still work - yield Promise.all([collection1.create(newRecord), - collection2.create(newRecord)]); - yield Promise.all(promises); - } finally { - yield collection1.db.close(); - yield collection2.db.close(); - } -}); - -add_task(clear_collection); - -add_task(function* test_kinto_update() { - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - const newRecord = { foo: "bar" }; - // check a record is created - let createResult = yield collection.create(newRecord); - do_check_eq(createResult.data.foo, newRecord.foo); - do_check_eq(createResult.data._status, "created"); - // check we can update this OK - let copiedRecord = Object.assign(createResult.data, {}); - deepEqual(createResult.data, copiedRecord); - copiedRecord.foo = "wibble"; - let updateResult = yield collection.update(copiedRecord); - // check the field was updated - do_check_eq(updateResult.data.foo, copiedRecord.foo); - // check the status is still "created", since we haven't synced - // the record - do_check_eq(updateResult.data._status, "created"); - } finally { - yield collection.db.close(); - } -}); - -add_task(clear_collection); - -add_task(function* test_kinto_clear() { - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - - // create an expected number of records - const expected = 10; - const newRecord = { foo: "bar" }; - for (let i = 0; i < expected; i++) { - yield collection.create(newRecord); - } - // check the collection contains the correct number - let list = yield collection.list(); - do_check_eq(list.data.length, expected); - // clear the collection and check again - should be 0 - yield collection.clear(); - list = yield collection.list(); - do_check_eq(list.data.length, 0); - } finally { - yield collection.db.close(); - } -}); - -add_task(clear_collection); - -add_task(function* test_kinto_delete(){ - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - const newRecord = { foo: "bar" }; - // check a record is created - let createResult = yield collection.create(newRecord); - do_check_eq(createResult.data.foo, newRecord.foo); - // check getting the record gets the same info - let getResult = yield collection.get(createResult.data.id); - deepEqual(createResult.data, getResult.data); - // delete that record - let deleteResult = yield collection.delete(createResult.data.id); - // check the ID is set on the result - do_check_eq(getResult.data.id, deleteResult.data.id); - // and check that get no longer returns the record - try { - getResult = yield collection.get(createResult.data.id); - do_throw("there should not be a result"); - } catch (e) { } - } finally { - yield collection.db.close(); - } -}); - -add_task(function* test_kinto_list(){ - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - const expected = 10; - const created = []; - for (let i = 0; i < expected; i++) { - let newRecord = { foo: "test " + i }; - let createResult = yield collection.create(newRecord); - created.push(createResult.data); - } - // check the collection contains the correct number - let list = yield collection.list(); - do_check_eq(list.data.length, expected); - - // check that all created records exist in the retrieved list - for (let createdRecord of created) { - let found = false; - for (let retrievedRecord of list.data) { - if (createdRecord.id == retrievedRecord.id) { - deepEqual(createdRecord, retrievedRecord); - found = true; - } - } - do_check_true(found); - } - } finally { - yield collection.db.close(); - } -}); - -add_task(clear_collection); - -add_task(function* test_loadDump_ignores_already_imported_records(){ - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - const record = {id: "41b71c13-17e9-4ee3-9268-6a41abf9730f", title: "foo", last_modified: 1457896541}; - yield collection.loadDump([record]); - let impactedRecords = yield collection.loadDump([record]); - do_check_eq(impactedRecords.length, 0); - } finally { - yield collection.db.close(); - } -}); - -add_task(clear_collection); - -add_task(function* test_loadDump_should_overwrite_old_records(){ - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - const record = {id: "41b71c13-17e9-4ee3-9268-6a41abf9730f", title: "foo", last_modified: 1457896541}; - yield collection.loadDump([record]); - const updated = Object.assign({}, record, {last_modified: 1457896543}); - let impactedRecords = yield collection.loadDump([updated]); - do_check_eq(impactedRecords.length, 1); - } finally { - yield collection.db.close(); - } -}); - -add_task(clear_collection); - -add_task(function* test_loadDump_should_not_overwrite_unsynced_records(){ - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - const recordId = "41b71c13-17e9-4ee3-9268-6a41abf9730f"; - yield collection.create({id: recordId, title: "foo"}, {useRecordId: true}); - const record = {id: recordId, title: "bar", last_modified: 1457896541}; - let impactedRecords = yield collection.loadDump([record]); - do_check_eq(impactedRecords.length, 0); - } finally { - yield collection.db.close(); - } -}); - -add_task(clear_collection); - -add_task(function* test_loadDump_should_not_overwrite_records_without_last_modified(){ - const collection = do_get_kinto_collection(); - try { - yield collection.db.open(); - const recordId = "41b71c13-17e9-4ee3-9268-6a41abf9730f"; - yield collection.create({id: recordId, title: "foo"}, {synced: true}); - const record = {id: recordId, title: "bar", last_modified: 1457896541}; - let impactedRecords = yield collection.loadDump([record]); - do_check_eq(impactedRecords.length, 0); - } finally { - yield collection.db.close(); - } -}); - -add_task(clear_collection); - -// Now do some sanity checks against a server - we're not looking to test -// core kinto.js functionality here (there is excellent test coverage in -// kinto.js), more making sure things are basically working as expected. -add_task(function* test_kinto_sync(){ - const configPath = "/v1/"; - const recordsPath = "/v1/buckets/default/collections/test_collection/records"; - // register a handler - function handleResponse (request, response) { - try { - const sampled = getSampleResponse(request, server.identity.primaryPort); - if (!sampled) { - do_throw(`unexpected ${request.method} request for ${request.path}?${request.queryString}`); - } - - response.setStatusLine(null, sampled.status.status, - sampled.status.statusText); - // send the headers - for (let headerLine of sampled.sampleHeaders) { - let headerElements = headerLine.split(':'); - response.setHeader(headerElements[0], headerElements[1].trimLeft()); - } - response.setHeader("Date", (new Date()).toUTCString()); - - response.write(sampled.responseBody); - } catch (e) { - dump(`${e}\n`); - } - } - server.registerPathHandler(configPath, handleResponse); - server.registerPathHandler(recordsPath, handleResponse); - - // create an empty collection, sync to populate - const collection = do_get_kinto_collection(); - try { - let result; - - yield collection.db.open(); - result = yield collection.sync(); - do_check_true(result.ok); - - // our test data has a single record; it should be in the local collection - let list = yield collection.list(); - do_check_eq(list.data.length, 1); - - // now sync again; we should now have 2 records - result = yield collection.sync(); - do_check_true(result.ok); - list = yield collection.list(); - do_check_eq(list.data.length, 2); - - // sync again; the second records should have been modified - const before = list.data[0].title; - result = yield collection.sync(); - do_check_true(result.ok); - list = yield collection.list(); - const after = list.data[0].title; - do_check_neq(before, after); - } finally { - yield collection.db.close(); - } -}); - -function run_test() { - // Set up an HTTP Server - server = new HttpServer(); - server.start(-1); - - run_next_test(); - - do_register_cleanup(function() { - server.stop(function() { }); - }); -} - -// get a response for a given request from sample data -function getSampleResponse(req, port) { - const responses = { - "OPTIONS": { - "sampleHeaders": [ - "Access-Control-Allow-Headers: Content-Length,Expires,Backoff,Retry-After,Last-Modified,Total-Records,ETag,Pragma,Cache-Control,authorization,content-type,if-none-match,Alert,Next-Page", - "Access-Control-Allow-Methods: GET,HEAD,OPTIONS,POST,DELETE,OPTIONS", - "Access-Control-Allow-Origin: *", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": "null" - }, - "GET:/v1/?": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"settings":{"batch_max_requests":25}, "url":`http://localhost:${port}/v1/`, "documentation":"https://kinto.readthedocs.org/", "version":"1.5.1", "commit":"cbc6f58", "hello":"kinto"}) - }, - "GET:/v1/buckets/default/collections/test_collection/records?_sort=-last_modified": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"1445606341071\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{"last_modified":1445606341071, "done":false, "id":"68db8313-686e-4fff-835e-07d78ad6f2af", "title":"New test"}]}) - }, - "GET:/v1/buckets/default/collections/test_collection/records?_sort=-last_modified&_since=1445606341071": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"1445607941223\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{"last_modified":1445607941223, "done":false, "id":"901967b0-f729-4b30-8d8d-499cba7f4b1d", "title":"Another new test"}]}) - }, - "GET:/v1/buckets/default/collections/test_collection/records?_sort=-last_modified&_since=1445607941223": { - "sampleHeaders": [ - "Access-Control-Allow-Origin: *", - "Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff", - "Content-Type: application/json; charset=UTF-8", - "Server: waitress", - "Etag: \"1445607541265\"" - ], - "status": {status: 200, statusText: "OK"}, - "responseBody": JSON.stringify({"data":[{"last_modified":1445607541265, "done":false, "id":"901967b0-f729-4b30-8d8d-499cba7f4b1d", "title":"Modified title"}]}) - } - }; - return responses[`${req.method}:${req.path}?${req.queryString}`] || - responses[req.method]; - -} diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index dbec09519..f1185c2c0 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -9,14 +9,6 @@ support-files = # Test load modules first so syntax failures are caught early. [test_load_modules.js] -[test_blocklist_certificates.js] -[test_blocklist_clients.js] -[test_blocklist_updater.js] - -[test_kinto.js] -[test_blocklist_signatures.js] -[test_storage_adapter.js] - [test_utils_atob.js] [test_utils_convert_string.js] [test_utils_dateprefs.js] -- cgit v1.2.3