forked from mirrors/gecko-dev
		
	 a181785400
			
		
	
	
		a181785400
		
	
	
	
	
		
			
			Depends on D135989 Differential Revision: https://phabricator.services.mozilla.com/D135990
		
			
				
	
	
		
			355 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // These tables share the same updateURL.
 | |
| const TEST_TABLE_DATA_LIST = [
 | |
|   // 0:
 | |
|   {
 | |
|     tableName: "test-listmanager0-digest256",
 | |
|     providerName: "google",
 | |
|     updateUrl: "http://localhost:4444/safebrowsing/update",
 | |
|     gethashUrl: "http://localhost:4444/safebrowsing/gethash0",
 | |
|   },
 | |
| 
 | |
|   // 1:
 | |
|   {
 | |
|     tableName: "test-listmanager1-digest256",
 | |
|     providerName: "google",
 | |
|     updateUrl: "http://localhost:4444/safebrowsing/update",
 | |
|     gethashUrl: "http://localhost:4444/safebrowsing/gethash1",
 | |
|   },
 | |
| 
 | |
|   // 2.
 | |
|   {
 | |
|     tableName: "test-listmanager2-digest256",
 | |
|     providerName: "google",
 | |
|     updateUrl: "http://localhost:4444/safebrowsing/update",
 | |
|     gethashUrl: "http://localhost:4444/safebrowsing/gethash2",
 | |
|   },
 | |
| ];
 | |
| 
 | |
| // These tables have a different update URL (for v4).
 | |
| const TEST_TABLE_DATA_V4 = {
 | |
|   tableName: "test-phish-proto",
 | |
|   providerName: "google4",
 | |
|   updateUrl: "http://localhost:5555/safebrowsing/update?",
 | |
|   gethashUrl: "http://localhost:5555/safebrowsing/gethash-v4",
 | |
| };
 | |
| const TEST_TABLE_DATA_V4_DISABLED = {
 | |
|   tableName: "test-unwanted-proto",
 | |
|   providerName: "google4",
 | |
|   updateUrl: "http://localhost:5555/safebrowsing/update?",
 | |
|   gethashUrl: "http://localhost:5555/safebrowsing/gethash-v4",
 | |
| };
 | |
| 
 | |
| const PREF_NEXTUPDATETIME =
 | |
|   "browser.safebrowsing.provider.google.nextupdatetime";
 | |
| const PREF_NEXTUPDATETIME_V4 =
 | |
|   "browser.safebrowsing.provider.google4.nextupdatetime";
 | |
| 
 | |
| let gListManager = Cc["@mozilla.org/url-classifier/listmanager;1"].getService(
 | |
|   Ci.nsIUrlListManager
 | |
| );
 | |
| 
 | |
| let gUrlUtils = Cc["@mozilla.org/url-classifier/utils;1"].getService(
 | |
|   Ci.nsIUrlClassifierUtils
 | |
| );
 | |
| 
 | |
| // Global test server for serving safebrowsing updates.
 | |
| let gHttpServ = null;
 | |
| let gUpdateResponse = "";
 | |
| let gExpectedUpdateRequest = "";
 | |
| let gExpectedQueryV4 = "";
 | |
| 
 | |
| // Handles request for TEST_TABLE_DATA_V4.
 | |
| let gHttpServV4 = null;
 | |
| 
 | |
| // These two variables are used to synchronize the last two racing updates
 | |
| // (in terms of "update URL") in test_update_all_tables().
 | |
| let gUpdatedCntForTableData = 0; // For TEST_TABLE_DATA_LIST.
 | |
| let gIsV4Updated = false; // For TEST_TABLE_DATA_V4.
 | |
| 
 | |
| const NEW_CLIENT_STATE = "sta\0te";
 | |
| const CHECKSUM =
 | |
|   "\x30\x67\xc7\x2c\x5e\x50\x1c\x31\xe3\xfe\xca\x73\xf0\x47\xdc\x34\x1a\x95\x63\x99\xec\x70\x5e\x0a\xee\x9e\xfb\x17\xa1\x55\x35\x78";
 | |
| 
 | |
| Services.prefs.setBoolPref("browser.safebrowsing.debug", true);
 | |
| 
 | |
| // The "\xFF\xFF" is to generate a base64 string with "/".
 | |
| Services.prefs.setCharPref("browser.safebrowsing.id", "Firefox\xFF\xFF");
 | |
| 
 | |
| // Register tables.
 | |
| TEST_TABLE_DATA_LIST.forEach(function(t) {
 | |
|   gListManager.registerTable(
 | |
|     t.tableName,
 | |
|     t.providerName,
 | |
|     t.updateUrl,
 | |
|     t.gethashUrl
 | |
|   );
 | |
| });
 | |
| 
 | |
| gListManager.registerTable(
 | |
|   TEST_TABLE_DATA_V4.tableName,
 | |
|   TEST_TABLE_DATA_V4.providerName,
 | |
|   TEST_TABLE_DATA_V4.updateUrl,
 | |
|   TEST_TABLE_DATA_V4.gethashUrl
 | |
| );
 | |
| 
 | |
| // To test Bug 1302044.
 | |
| gListManager.registerTable(
 | |
|   TEST_TABLE_DATA_V4_DISABLED.tableName,
 | |
|   TEST_TABLE_DATA_V4_DISABLED.providerName,
 | |
|   TEST_TABLE_DATA_V4_DISABLED.updateUrl,
 | |
|   TEST_TABLE_DATA_V4_DISABLED.gethashUrl
 | |
| );
 | |
| 
 | |
| const SERVER_INVOLVED_TEST_CASE_LIST = [
 | |
|   // - Do table0 update.
 | |
|   // - Server would respond "a:5:32:32\n[DATA]".
 | |
|   function test_update_table0() {
 | |
|     disableAllUpdates();
 | |
| 
 | |
|     gListManager.enableUpdate(TEST_TABLE_DATA_LIST[0].tableName);
 | |
|     gExpectedUpdateRequest = TEST_TABLE_DATA_LIST[0].tableName + ";\n";
 | |
| 
 | |
|     gUpdateResponse = "n:1000\ni:" + TEST_TABLE_DATA_LIST[0].tableName + "\n";
 | |
|     gUpdateResponse += readFileToString("data/digest2.chunk");
 | |
| 
 | |
|     forceTableUpdate();
 | |
|   },
 | |
| 
 | |
|   // - Do table0 update again. Since chunk 5 was added to table0 in the last
 | |
|   //   update, the expected request contains "a:5".
 | |
|   // - Server would respond "s;2-12\n[DATA]".
 | |
|   function test_update_table0_with_existing_chunks() {
 | |
|     disableAllUpdates();
 | |
| 
 | |
|     gListManager.enableUpdate(TEST_TABLE_DATA_LIST[0].tableName);
 | |
|     gExpectedUpdateRequest = TEST_TABLE_DATA_LIST[0].tableName + ";a:5\n";
 | |
| 
 | |
|     gUpdateResponse = "n:1000\ni:" + TEST_TABLE_DATA_LIST[0].tableName + "\n";
 | |
|     gUpdateResponse += readFileToString("data/digest1.chunk");
 | |
| 
 | |
|     forceTableUpdate();
 | |
|   },
 | |
| 
 | |
|   // - Do all-table update.
 | |
|   // - Server would respond no chunk control.
 | |
|   //
 | |
|   // Note that this test MUST be the last one in the array since we rely on
 | |
|   // the number of sever-involved test case to synchronize the racing last
 | |
|   // two udpates for different URL.
 | |
|   function test_update_all_tables() {
 | |
|     disableAllUpdates();
 | |
| 
 | |
|     // Enable all tables including TEST_TABLE_DATA_V4!
 | |
|     TEST_TABLE_DATA_LIST.forEach(function(t) {
 | |
|       gListManager.enableUpdate(t.tableName);
 | |
|     });
 | |
| 
 | |
|     // We register two v4 tables but only enable one of them
 | |
|     // to verify that the disabled tables are not updated.
 | |
|     // See Bug 1302044.
 | |
|     gListManager.enableUpdate(TEST_TABLE_DATA_V4.tableName);
 | |
|     gListManager.disableUpdate(TEST_TABLE_DATA_V4_DISABLED.tableName);
 | |
| 
 | |
|     // Expected results for v2.
 | |
|     gExpectedUpdateRequest =
 | |
|       TEST_TABLE_DATA_LIST[0].tableName +
 | |
|       ";a:5:s:2-12\n" +
 | |
|       TEST_TABLE_DATA_LIST[1].tableName +
 | |
|       ";\n" +
 | |
|       TEST_TABLE_DATA_LIST[2].tableName +
 | |
|       ";\n";
 | |
|     gUpdateResponse = "n:1000\n";
 | |
| 
 | |
|     // We test the request against the query string since v4 request
 | |
|     // would be appened to the query string. The request is generated
 | |
|     // by protobuf API (binary) then encoded to base64 format.
 | |
|     let requestV4 = gUrlUtils.makeUpdateRequestV4(
 | |
|       [TEST_TABLE_DATA_V4.tableName],
 | |
|       [""]
 | |
|     );
 | |
|     gExpectedQueryV4 = "&$req=" + requestV4;
 | |
| 
 | |
|     forceTableUpdate();
 | |
|   },
 | |
| ];
 | |
| 
 | |
| SERVER_INVOLVED_TEST_CASE_LIST.forEach(t => add_test(t));
 | |
| 
 | |
| add_test(function test_partialUpdateV4() {
 | |
|   disableAllUpdates();
 | |
| 
 | |
|   gListManager.enableUpdate(TEST_TABLE_DATA_V4.tableName);
 | |
| 
 | |
|   // Since the new client state has been responded and saved in
 | |
|   // test_update_all_tables, this update request should send
 | |
|   // a partial update to the server.
 | |
|   let requestV4 = gUrlUtils.makeUpdateRequestV4(
 | |
|     [TEST_TABLE_DATA_V4.tableName],
 | |
|     [btoa(NEW_CLIENT_STATE)]
 | |
|   );
 | |
|   gExpectedQueryV4 = "&$req=" + requestV4;
 | |
| 
 | |
|   forceTableUpdate();
 | |
| });
 | |
| 
 | |
| // Tests nsIUrlListManager.getGethashUrl.
 | |
| add_test(function test_getGethashUrl() {
 | |
|   TEST_TABLE_DATA_LIST.forEach(function(t) {
 | |
|     equal(gListManager.getGethashUrl(t.tableName), t.gethashUrl);
 | |
|   });
 | |
|   equal(
 | |
|     gListManager.getGethashUrl(TEST_TABLE_DATA_V4.tableName),
 | |
|     TEST_TABLE_DATA_V4.gethashUrl
 | |
|   );
 | |
|   run_next_test();
 | |
| });
 | |
| 
 | |
| function run_test() {
 | |
|   // Setup primary testing server.
 | |
|   gHttpServ = new HttpServer();
 | |
|   gHttpServ.registerDirectory("/", do_get_cwd());
 | |
| 
 | |
|   gHttpServ.registerPathHandler("/safebrowsing/update", function(
 | |
|     request,
 | |
|     response
 | |
|   ) {
 | |
|     let body = NetUtil.readInputStreamToString(
 | |
|       request.bodyInputStream,
 | |
|       request.bodyInputStream.available()
 | |
|     );
 | |
| 
 | |
|     // Verify if the request is as expected.
 | |
|     equal(body, gExpectedUpdateRequest);
 | |
| 
 | |
|     // Respond the update which is controlled by the test case.
 | |
|     response.setHeader(
 | |
|       "Content-Type",
 | |
|       "application/vnd.google.safebrowsing-update",
 | |
|       false
 | |
|     );
 | |
|     response.setStatusLine(request.httpVersion, 200, "OK");
 | |
|     response.bodyOutputStream.write(gUpdateResponse, gUpdateResponse.length);
 | |
| 
 | |
|     gUpdatedCntForTableData++;
 | |
| 
 | |
|     if (gUpdatedCntForTableData !== SERVER_INVOLVED_TEST_CASE_LIST.length) {
 | |
|       // This is not the last test case so run the next once upon the
 | |
|       // the update success.
 | |
|       waitForUpdateSuccess(run_next_test);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (gIsV4Updated) {
 | |
|       run_next_test(); // All tests are done. Just finish.
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     info("Waiting for TEST_TABLE_DATA_V4 to be tested ...");
 | |
|   });
 | |
| 
 | |
|   gHttpServ.start(4444);
 | |
| 
 | |
|   // Setup v4 testing server for the different update URL.
 | |
|   gHttpServV4 = new HttpServer();
 | |
|   gHttpServV4.registerDirectory("/", do_get_cwd());
 | |
| 
 | |
|   gHttpServV4.registerPathHandler("/safebrowsing/update", function(
 | |
|     request,
 | |
|     response
 | |
|   ) {
 | |
|     // V4 update request body should be empty.
 | |
|     equal(request.bodyInputStream.available(), 0);
 | |
| 
 | |
|     // Not on the spec. Found in Chromium source code...
 | |
|     equal(request.getHeader("X-HTTP-Method-Override"), "POST");
 | |
| 
 | |
|     // V4 update request uses GET.
 | |
|     equal(request.method, "GET");
 | |
| 
 | |
|     // V4 append the base64 encoded request to the query string.
 | |
|     equal(request.queryString, gExpectedQueryV4);
 | |
|     equal(request.queryString.indexOf("+"), -1);
 | |
|     equal(request.queryString.indexOf("/"), -1);
 | |
| 
 | |
|     // Respond a V2 compatible content for now. In the future we can
 | |
|     // send a meaningful response to test Bug 1284178 to see if the
 | |
|     // update is successfully stored to database.
 | |
|     response.setHeader(
 | |
|       "Content-Type",
 | |
|       "application/vnd.google.safebrowsing-update",
 | |
|       false
 | |
|     );
 | |
|     response.setStatusLine(request.httpVersion, 200, "OK");
 | |
| 
 | |
|     // The protobuf binary represention of response:
 | |
|     //
 | |
|     // [
 | |
|     //   {
 | |
|     //     'threat_type': 2, // SOCIAL_ENGINEERING_PUBLIC
 | |
|     //     'response_type': 2, // FULL_UPDATE
 | |
|     //     'new_client_state': 'sta\x00te', // NEW_CLIENT_STATE
 | |
|     //     'checksum': { "sha256": CHECKSUM }, // CHECKSUM
 | |
|     //     'additions': { 'compression_type': RAW,
 | |
|     //                    'prefix_size': 4,
 | |
|     //                    'raw_hashes': "00000001000000020000000300000004"}
 | |
|     //   }
 | |
|     // ]
 | |
|     //
 | |
|     let content =
 | |
|       "\x0A\x4A\x08\x02\x20\x02\x2A\x18\x08\x01\x12\x14\x08\x04\x12\x10\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x3A\x06\x73\x74\x61\x00\x74\x65\x42\x22\x0A\x20\x30\x67\xC7\x2C\x5E\x50\x1C\x31\xE3\xFE\xCA\x73\xF0\x47\xDC\x34\x1A\x95\x63\x99\xEC\x70\x5E\x0A\xEE\x9E\xFB\x17\xA1\x55\x35\x78\x12\x08\x08\x08\x10\x80\x94\xEB\xDC\x03";
 | |
| 
 | |
|     response.bodyOutputStream.write(content, content.length);
 | |
| 
 | |
|     if (gIsV4Updated) {
 | |
|       // This falls to the case where test_partialUpdateV4 is running.
 | |
|       // We are supposed to have verified the update request contains
 | |
|       // the state we set in the previous request.
 | |
|       waitForUpdateSuccess(run_next_test);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     waitUntilMetaDataSaved(NEW_CLIENT_STATE, CHECKSUM, () => {
 | |
|       gIsV4Updated = true;
 | |
| 
 | |
|       if (gUpdatedCntForTableData === SERVER_INVOLVED_TEST_CASE_LIST.length) {
 | |
|         // All tests are done!
 | |
|         run_next_test();
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       info("Wait for all sever-involved tests to be done ...");
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   gHttpServV4.start(5555);
 | |
| 
 | |
|   registerCleanupFunction(function() {
 | |
|     return (async function() {
 | |
|       await Promise.all([gHttpServ.stop(), gHttpServV4.stop()]);
 | |
|     })();
 | |
|   });
 | |
| 
 | |
|   run_next_test();
 | |
| }
 | |
| 
 | |
| // A trick to force updating tables. However, before calling this, we have to
 | |
| // call disableAllUpdates() first to clean up the updateCheckers in listmanager.
 | |
| function forceTableUpdate() {
 | |
|   throwOnUpdateErrors();
 | |
|   Services.prefs.setCharPref(PREF_NEXTUPDATETIME, "1");
 | |
|   Services.prefs.setCharPref(PREF_NEXTUPDATETIME_V4, "1");
 | |
|   gListManager.maybeToggleUpdateChecking();
 | |
| }
 | |
| 
 | |
| function disableAllUpdates() {
 | |
|   stopThrowingOnUpdateErrors();
 | |
|   TEST_TABLE_DATA_LIST.forEach(t => gListManager.disableUpdate(t.tableName));
 | |
|   gListManager.disableUpdate(TEST_TABLE_DATA_V4.tableName);
 | |
| }
 | |
| 
 | |
| function waitForUpdateSuccess(callback) {
 | |
|   Services.obs.addObserver(function listener() {
 | |
|     Services.obs.removeObserver(listener, "safebrowsing-update-finished");
 | |
|     callback();
 | |
|   }, "safebrowsing-update-finished");
 | |
| }
 |