Bug 1275507 - XPCOM API to create SB v4 update request. r=francois draft
authorHenry Chang <hchang@mozilla.com>
Tue, 19 Jul 2016 18:09:53 +0800
changeset 390466 b208a6b83953b21db7bf1a857453d55fa9ab1c87
parent 389550 5a91e5b49be3c1ba401b057e90c92d7488e3647d
child 526006 522ab13cd194c4fc1b022e1805694433c4d862c5
push id23677
push userhchang@mozilla.com
push dateThu, 21 Jul 2016 06:42:11 +0000
reviewersfrancois
bugs1275507
milestone50.0a1
Bug 1275507 - XPCOM API to create SB v4 update request. r=francois MozReview-Commit-ID: RfM3KFe6kG
toolkit/components/url-classifier/nsIUrlClassifierUtils.idl
toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
toolkit/components/url-classifier/tests/unit/test_safebrowsing_protobuf.js
toolkit/components/url-classifier/tests/unit/test_threat_type_conversion.js
toolkit/components/url-classifier/tests/unit/xpcshell.ini
--- a/toolkit/components/url-classifier/nsIUrlClassifierUtils.idl
+++ b/toolkit/components/url-classifier/nsIUrlClassifierUtils.idl
@@ -25,9 +25,40 @@ interface nsIUrlClassifierUtils : nsISup
   /**
    * Get the protocol version for the given provider.
    *
    * @param provider String the provider name. e.g. "google"
    *
    * @returns String to indicate the protocol version. e.g. "2.2"
    */
   ACString getProtocolVersion(in ACString provider);
+
+  /**
+   * Convert threat type to list name.
+   *
+   * @param Integer to indicate threat type.
+   *
+   * @returns The list name.
+   */
+  ACString convertThreatTypeToListName(in uint32_t threatType);
+
+  /**
+   * Convert list name to threat type.
+   *
+   * @param The list name.
+   *
+   * @returns The threat type in integer.
+   */
+  uint32_t convertListNameToThreatType(in ACString listName);
+
+  /**
+   * Make update request for given lists and their states.
+   *
+   * @param aListNames An array of list name represented in string.
+   * @param aState An array of states (in string) for each list.
+   * @param aCount The array length of aList and aState.
+   *
+   * @returns A string to store request. Not null-terminated.
+   */
+  ACString makeUpdateRequestV4([array, size_is(aCount)] in string aListNames,
+                               [array, size_is(aCount)] in string aStates,
+                               in uint32_t aCount);
 };
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
@@ -6,16 +6,17 @@
 #include "nsString.h"
 #include "nsIURI.h"
 #include "nsUrlClassifierUtils.h"
 #include "nsTArray.h"
 #include "nsReadableUtils.h"
 #include "plbase64.h"
 #include "prprf.h"
 #include "nsPrintfCString.h"
+#include "safebrowsing.pb.h"
 
 #define DEFAULT_PROTOCOL_VERSION "2.2"
 
 static char int_to_hex_digit(int32_t i)
 {
   NS_ASSERTION((i >= 0) && (i <= 15), "int too big in int_to_hex_digit");
   return static_cast<char>(((i < 10) ? (i + '0') : ((i - 10) + 'A')));
 }
@@ -67,16 +68,86 @@ IsOctal(const nsACString & num)
     if (!isdigit(num[i]) || num[i] == '8' || num[i] == '9') {
       return false;
     }
   }
 
   return true;
 }
 
+/////////////////////////////////////////////////////////////////
+// SafeBrowsing V4 related utits.
+
+namespace mozilla {
+namespace safebrowsing {
+
+static PlatformType
+GetPlatformType()
+{
+#if defined(ANDROID)
+  return ANDROID_PLATFORM;
+#elif defined(XP_MACOSX)
+  return OSX_PLATFORM;
+#elif defined(XP_LINUX)
+  return LINUX_PLATFORM;
+#elif defined(XP_WIN)
+  return WINDOWS_PLATFORM;
+#else
+  #error Unrecognized platform type.
+#endif
+}
+
+typedef FetchThreatListUpdatesRequest_ListUpdateRequest ListUpdateRequest;
+typedef FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints Constraints;
+
+static void
+InitListUpdateRequest(ThreatType aThreatType,
+                      const char* aState,
+                      ListUpdateRequest* aListUpdateRequest)
+{
+  aListUpdateRequest->set_threat_type(aThreatType);
+  aListUpdateRequest->set_platform_type(GetPlatformType());
+  aListUpdateRequest->set_threat_entry_type(URL);
+
+  // Only RAW data is supported for now.
+  // TODO: Bug 1285848 Supports Rice-Golomb encoding.
+  Constraints* contraints = new Constraints();
+  contraints->add_supported_compressions(RAW);
+  aListUpdateRequest->set_allocated_constraints(contraints);
+
+  // Only set non-empty state.
+  if (aState[0] != '\0') {
+    aListUpdateRequest->set_state(aState);
+  }
+}
+
+static ClientInfo*
+CreateClientInfo()
+{
+  ClientInfo* c = new ClientInfo();
+
+  nsCOMPtr<nsIPrefBranch> prefBranch =
+    do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+  nsXPIDLCString clientId;
+  nsresult rv = prefBranch->GetCharPref("browser.safebrowsing.id",
+                                        getter_Copies(clientId));
+
+  if (NS_FAILED(rv)) {
+    clientId = "Firefox"; // Use "Firefox" as fallback.
+  }
+
+  c->set_client_id(clientId.get());
+
+  return c;
+}
+
+} // end of namespace safebrowsing.
+} // end of namespace mozilla.
+
 nsUrlClassifierUtils::nsUrlClassifierUtils() : mEscapeCharmap(nullptr)
 {
 }
 
 nsresult
 nsUrlClassifierUtils::Init()
 {
   // Everything but alpha numerics, - and .
@@ -122,16 +193,55 @@ nsUrlClassifierUtils::GetKeyForURI(nsIUR
   rv = CanonicalizePath(path, temp);
   NS_ENSURE_SUCCESS(rv, rv);
 
   _retval.Append(temp);
 
   return NS_OK;
 }
 
+// We use "goog-*-proto" as the list name for v4, where "proto" indicates
+// it's updated (as well as hash completion) via protobuf.
+static const struct {
+  const char* mListName;
+  uint32_t mThreatType;
+} THREAT_TYPE_CONV_TABLE[] = {
+  { "goog-malware-proto",  MALWARE_THREAT},            // 1
+  { "goog-phish-proto",    SOCIAL_ENGINEERING_PUBLIC}, // 2
+  { "goog-unwanted-proto", UNWANTED_SOFTWARE},         // 3
+};
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::ConvertThreatTypeToListName(uint32_t aThreatType,
+                                                  nsACString& aListName)
+{
+  for (uint32_t i = 0; i < ArrayLength(THREAT_TYPE_CONV_TABLE); i++) {
+    if (aThreatType == THREAT_TYPE_CONV_TABLE[i].mThreatType) {
+      aListName = THREAT_TYPE_CONV_TABLE[i].mListName;
+      return NS_OK;
+    }
+  }
+
+  return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::ConvertListNameToThreatType(const nsACString& aListName,
+                                                  uint32_t* aThreatType)
+{
+  for (uint32_t i = 0; i < ArrayLength(THREAT_TYPE_CONV_TABLE); i++) {
+    if (aListName.EqualsASCII(THREAT_TYPE_CONV_TABLE[i].mListName)) {
+      *aThreatType = THREAT_TYPE_CONV_TABLE[i].mThreatType;
+      return NS_OK;
+    }
+  }
+
+  return NS_ERROR_FAILURE;
+}
+
 NS_IMETHODIMP
 nsUrlClassifierUtils::GetProtocolVersion(const nsACString& aProvider,
                                          nsACString& aVersion)
 {
   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefBranch) {
       nsPrintfCString prefName("browser.safebrowsing.provider.%s.pver",
                                nsCString(aProvider).get());
@@ -141,16 +251,50 @@ nsUrlClassifierUtils::GetProtocolVersion
       aVersion = NS_SUCCEEDED(rv) ? version : DEFAULT_PROTOCOL_VERSION;
   } else {
       aVersion = DEFAULT_PROTOCOL_VERSION;
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsUrlClassifierUtils::MakeUpdateRequestV4(const char** aListNames,
+                                          const char** aStates,
+                                          uint32_t aCount,
+                                          nsACString &aRequest)
+{
+  using namespace mozilla::safebrowsing;
+
+  FetchThreatListUpdatesRequest r;
+  r.set_allocated_client(CreateClientInfo());
+
+  for (uint32_t i = 0; i < aCount; i++) {
+    nsCString listName(aListNames[i]);
+    uint32_t threatType;
+    nsresult rv = ConvertListNameToThreatType(listName, &threatType);
+    if (NS_FAILED(rv)) {
+      continue; // Unknown list name.
+    }
+    auto lur = r.mutable_list_update_requests()->Add();
+    InitListUpdateRequest(static_cast<ThreatType>(threatType), aStates[i], lur);
+  }
+
+  // Then serialize.
+  std::string s;
+  r.SerializeToString(&s);
+
+  nsCString out;
+  out.Assign(s.c_str(), s.size());
+
+  aRequest = out;
+
+  return NS_OK;
+}
+
 /////////////////////////////////////////////////////////////////////////////
 // non-interface methods
 
 nsresult
 nsUrlClassifierUtils::CanonicalizeHostname(const nsACString & hostname,
                                            nsACString & _retval)
 {
   nsAutoCString unescaped;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_safebrowsing_protobuf.js
@@ -0,0 +1,23 @@
+function run_test() {
+  let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"]
+                   .getService(Ci.nsIUrlClassifierUtils);
+
+  // No list at all.
+  let requestNoList = urlUtils.makeUpdateRequestV4([], [], 0);
+
+  // Only one valid list name.
+  let requestOneValid =
+    urlUtils.makeUpdateRequestV4(["goog-phish-proto"], ["AAAAAA"], 1);
+
+  // Only one invalid list name.
+  let requestOneInvalid =
+    urlUtils.makeUpdateRequestV4(["bad-list-name"], ["AAAAAA"], 1);
+
+  // One valid and one invalid list name.
+  let requestOneInvalidOneValid =
+    urlUtils.makeUpdateRequestV4(["goog-phish-proto", "bad-list-name"],
+                                 ["AAAAAA", "AAAAAA"], 2);
+
+  equal(requestNoList, requestOneInvalid);
+  equal(requestOneValid, requestOneInvalidOneValid);
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_threat_type_conversion.js
@@ -0,0 +1,35 @@
+function run_test() {
+  let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"]
+                   .getService(Ci.nsIUrlClassifierUtils);
+
+  // Test list name to threat type conversion.
+
+  equal(urlUtils.convertListNameToThreatType("goog-malware-proto"), 1);
+  equal(urlUtils.convertListNameToThreatType("goog-phish-proto"), 2);
+  equal(urlUtils.convertListNameToThreatType("goog-unwanted-proto"), 3);
+
+  try {
+    urlUtils.convertListNameToThreatType("bad-list-name");
+    ok(false, "Bad list name should lead to exception.");
+  } catch (e) {}
+
+  try {
+    urlUtils.convertListNameToThreatType("bad-list-name");
+    ok(false, "Bad list name should lead to exception.");
+  } catch (e) {}
+
+  // Test threat type to list name conversion.
+  equal(urlUtils.convertThreatTypeToListName(1), "goog-malware-proto");
+  equal(urlUtils.convertThreatTypeToListName(2), "goog-phish-proto");
+  equal(urlUtils.convertThreatTypeToListName(3), "goog-unwanted-proto");
+
+  try {
+    urlUtils.convertThreatTypeToListName(0);
+    ok(false, "Bad threat type should lead to exception.");
+  } catch (e) {}
+
+  try {
+    urlUtils.convertThreatTypeToListName(100);
+    ok(false, "Bad threat type should lead to exception.");
+  } catch (e) {}
+}
\ No newline at end of file
--- a/toolkit/components/url-classifier/tests/unit/xpcshell.ini
+++ b/toolkit/components/url-classifier/tests/unit/xpcshell.ini
@@ -10,13 +10,15 @@ support-files =
 [test_bug1274685_unowned_list.js]
 [test_backoff.js]
 [test_dbservice.js]
 [test_hashcompleter.js]
 # Bug 752243: Profile cleanup frequently fails
 #skip-if = os == "mac" || os == "linux"
 [test_partial.js]
 [test_prefixset.js]
+[test_threat_type_conversion.js]
 [test_provider_url.js]
 [test_streamupdater.js]
 [test_digest256.js]
 [test_listmanager.js]
 [test_pref.js]
+[test_safebrowsing_protobuf.js]