Bug 1254100 - Part 1 - The Application Reputation interface should provide information about the verdict. r=gcp draft
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Sun, 20 Mar 2016 13:54:11 +0000
changeset 342693 0dc22ed16dd77c2ebd51ae7419febef3113e01ce
parent 340999 341344bdec8f10bf50646cd6ef2355361435cbf6
child 342694 e4439dc7335ba0ecb40300d394f57424f6ac8a28
push id13439
push userpaolo.mozmail@amadzone.org
push dateSun, 20 Mar 2016 14:07:39 +0000
reviewersgcp
bugs1254100
milestone48.0a1
Bug 1254100 - Part 1 - The Application Reputation interface should provide information about the verdict. r=gcp MozReview-Commit-ID: 2qpg7297NUB
toolkit/components/downloads/ApplicationReputation.cpp
toolkit/components/downloads/nsIApplicationReputation.idl
--- a/toolkit/components/downloads/ApplicationReputation.cpp
+++ b/toolkit/components/downloads/ApplicationReputation.cpp
@@ -160,24 +160,26 @@ private:
   // Returns true if the file is likely to be binary.
   bool IsBinaryFile();
 
   // Returns the type of download binary for the file.
   ClientDownloadRequest::DownloadType GetDownloadType(const nsAString& aFilename);
 
   // Clean up and call the callback. PendingLookup must not be used after this
   // function is called.
-  nsresult OnComplete(bool shouldBlock, nsresult rv);
+  nsresult OnComplete(bool shouldBlock, nsresult rv,
+    uint32_t verdict = nsIApplicationReputationService::VERDICT_SAFE);
 
   // Wrapper function for nsIStreamListener.onStopRequest to make it easy to
   // guarantee calling the callback
   nsresult OnStopRequestInternal(nsIRequest *aRequest,
                                  nsISupports *aContext,
                                  nsresult aResult,
-                                 bool* aShouldBlock);
+                                 bool* aShouldBlock,
+                                 uint32_t* aVerdict);
 
   // Strip url parameters, fragments, and user@pass fields from the URI spec
   // using nsIURL. If aURI is not an nsIURL, returns the original nsIURI.spec.
   nsresult GetStrippedSpec(nsIURI* aUri, nsACString& spec);
 
   // Escape '/' and '%' in certificate attribute values.
   nsCString EscapeCertificateAttribute(const nsACString& aAttribute);
 
@@ -339,17 +341,18 @@ PendingDBLookup::HandleEvent(const nsACS
   // 2) PendingLookup::LookupNext if the URL does not match the blocklist.
   // Blocklisting trumps allowlisting.
   nsAutoCString blockList;
   Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &blockList);
   if (!mAllowlistOnly && FindInReadable(blockList, tables)) {
     mPendingLookup->mBlocklistCount++;
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, BLOCK_LIST);
     LOG(("Found principal %s on blocklist [this = %p]", mSpec.get(), this));
-    return mPendingLookup->OnComplete(true, NS_OK);
+    return mPendingLookup->OnComplete(true, NS_OK,
+      nsIApplicationReputationService::VERDICT_DANGEROUS);
   }
 
   nsAutoCString allowList;
   Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allowList);
   if (FindInReadable(allowList, tables)) {
     mPendingLookup->mAllowlistCount++;
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_LOCAL, ALLOW_LIST);
     LOG(("Found principal %s on allowlist [this = %p]", mSpec.get(), this));
@@ -442,30 +445,32 @@ PendingLookup::GetDownloadType(const nsA
 
 nsresult
 PendingLookup::LookupNext()
 {
   // We must call LookupNext or SendRemoteQuery upon return.
   // Look up all of the URLs that could allow or block this download.
   // Blocklist first.
   if (mBlocklistCount > 0) {
-    return OnComplete(true, NS_OK);
+    return OnComplete(true, NS_OK,
+                      nsIApplicationReputationService::VERDICT_DANGEROUS);
   }
   int index = mAnylistSpecs.Length() - 1;
   nsCString spec;
   if (index >= 0) {
     // Check the source URI, referrer and redirect chain.
     spec = mAnylistSpecs[index];
     mAnylistSpecs.RemoveElementAt(index);
     RefPtr<PendingDBLookup> lookup(new PendingDBLookup(this));
     return lookup->LookupSpec(spec, false);
   }
   // If any of mAnylistSpecs matched the blocklist, go ahead and block.
   if (mBlocklistCount > 0) {
-    return OnComplete(true, NS_OK);
+    return OnComplete(true, NS_OK,
+                      nsIApplicationReputationService::VERDICT_DANGEROUS);
   }
   // If any of mAnylistSpecs matched the allowlist, go ahead and pass.
   if (mAllowlistCount > 0) {
     return OnComplete(false, NS_OK);
   }
   // Only binary signatures remain.
   index = mAllowlistSpecs.Length() - 1;
   if (index >= 0) {
@@ -754,40 +759,44 @@ PendingLookup::DoLookupInternal()
   rv = GenerateWhitelistStrings();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Start the call chain.
   return LookupNext();
 }
 
 nsresult
-PendingLookup::OnComplete(bool shouldBlock, nsresult rv)
+PendingLookup::OnComplete(bool shouldBlock, nsresult rv, uint32_t verdict)
 {
+  MOZ_ASSERT(!shouldBlock || verdict != VERDICT_SAFE);
+
   if (NS_FAILED(rv)) {
     nsAutoCString errorName;
     mozilla::GetErrorName(rv, errorName);
     LOG(("Failed sending remote query for application reputation "
          "[rv = %s, this = %p]", errorName.get(), this));
   }
 
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
     mTimeoutTimer = nullptr;
   }
 
   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
     shouldBlock);
   double t = (TimeStamp::Now() - mStartTime).ToMilliseconds();
+  LOG(("Application Reputation verdict is %lu, obtained in %f ms [this = %p]",
+       verdict, t, this));
   if (shouldBlock) {
-    LOG(("Application Reputation check failed, blocking bad binary in %f ms "
-         "[this = %p]", t, this));
+    LOG(("Application Reputation check failed, blocking bad binary [this = %p]",
+        this));
   } else {
-    LOG(("Application Reputation check passed in %f ms [this = %p]", t, this));
+    LOG(("Application Reputation check passed [this = %p]", this));
   }
-  nsresult res = mCallback->OnComplete(shouldBlock, rv);
+  nsresult res = mCallback->OnComplete(shouldBlock, rv, verdict);
   return res;
 }
 
 nsresult
 PendingLookup::ParseCertificates(nsIArray* aSigArray)
 {
   // If we haven't been set for any reason, bail.
   NS_ENSURE_ARG_POINTER(aSigArray);
@@ -1050,34 +1059,37 @@ PendingLookup::OnStartRequest(nsIRequest
 
 NS_IMETHODIMP
 PendingLookup::OnStopRequest(nsIRequest *aRequest,
                              nsISupports *aContext,
                              nsresult aResult) {
   NS_ENSURE_STATE(mCallback);
 
   bool shouldBlock = false;
+  uint32_t verdict = nsIApplicationReputationService::VERDICT_SAFE;
   nsresult rv = OnStopRequestInternal(aRequest, aContext, aResult,
-                                      &shouldBlock);
-  OnComplete(shouldBlock, rv);
+                                      &shouldBlock, &verdict);
+  OnComplete(shouldBlock, rv, verdict);
   return rv;
 }
 
 nsresult
 PendingLookup::OnStopRequestInternal(nsIRequest *aRequest,
                                      nsISupports *aContext,
                                      nsresult aResult,
-                                     bool* aShouldBlock) {
+                                     bool* aShouldBlock,
+                                     uint32_t* aVerdict) {
   if (NS_FAILED(aResult)) {
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
       SERVER_RESPONSE_FAILED);
     return aResult;
   }
 
   *aShouldBlock = false;
+  *aVerdict = nsIApplicationReputationService::VERDICT_SAFE;
   nsresult rv;
   nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
   if (NS_FAILED(rv)) {
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
       SERVER_RESPONSE_FAILED);
     return rv;
   }
 
@@ -1107,25 +1119,29 @@ PendingLookup::OnStopRequestInternal(nsI
   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER,
     SERVER_RESPONSE_VALID);
   // Clamp responses 0-7, we only know about 0-4 for now.
   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SERVER_VERDICT,
     std::min<uint32_t>(response.verdict(), 7));
   switch(response.verdict()) {
     case safe_browsing::ClientDownloadResponse::DANGEROUS:
       *aShouldBlock = Preferences::GetBool(PREF_BLOCK_DANGEROUS, true);
+      *aVerdict = nsIApplicationReputationService::VERDICT_DANGEROUS;
       break;
     case safe_browsing::ClientDownloadResponse::DANGEROUS_HOST:
       *aShouldBlock = Preferences::GetBool(PREF_BLOCK_DANGEROUS_HOST, true);
+      *aVerdict = nsIApplicationReputationService::VERDICT_DANGEROUS_HOST;
       break;
     case safe_browsing::ClientDownloadResponse::POTENTIALLY_UNWANTED:
       *aShouldBlock = Preferences::GetBool(PREF_BLOCK_POTENTIALLY_UNWANTED, false);
+      *aVerdict = nsIApplicationReputationService::VERDICT_POTENTIALLY_UNWANTED;
       break;
     case safe_browsing::ClientDownloadResponse::UNCOMMON:
       *aShouldBlock = Preferences::GetBool(PREF_BLOCK_UNCOMMON, false);
+      *aVerdict = nsIApplicationReputationService::VERDICT_UNCOMMON;
       break;
     default:
       // Treat everything else as safe
       break;
   }
 
   return NS_OK;
 }
@@ -1170,17 +1186,18 @@ ApplicationReputationService::QueryReput
   NS_ENSURE_ARG_POINTER(aQuery);
   NS_ENSURE_ARG_POINTER(aCallback);
 
   Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_COUNT, true);
   nsresult rv = QueryReputationInternal(aQuery, aCallback);
   if (NS_FAILED(rv)) {
     Accumulate(mozilla::Telemetry::APPLICATION_REPUTATION_SHOULD_BLOCK,
       false);
-    aCallback->OnComplete(false, rv);
+    aCallback->OnComplete(false, rv,
+                          nsIApplicationReputationService::VERDICT_SAFE);
   }
   return NS_OK;
 }
 
 nsresult ApplicationReputationService::QueryReputationInternal(
   nsIApplicationReputationQuery* aQuery,
   nsIApplicationReputationCallback* aCallback) {
   nsresult rv;
--- a/toolkit/components/downloads/nsIApplicationReputation.idl
+++ b/toolkit/components/downloads/nsIApplicationReputation.idl
@@ -13,16 +13,25 @@ interface nsIURI;
 
 /*
  * A service for asynchronously querying an application reputation service
  * based on metadata of the downloaded file.
  */
 [scriptable, uuid(c9f03479-fd68-4393-acb2-c88d4f563174)]
 interface nsIApplicationReputationService : nsISupports {
   /**
+   * Indicates the reason for the application reputation block.
+   */
+  const unsigned long VERDICT_SAFE = 0;
+  const unsigned long VERDICT_DANGEROUS = 1;
+  const unsigned long VERDICT_UNCOMMON = 2;
+  const unsigned long VERDICT_POTENTIALLY_UNWANTED = 3;
+  const unsigned long VERDICT_DANGEROUS_HOST = 4;
+
+  /**
    * Start querying the application reputation service.
    *
    * @param aQuery
    *        The nsIApplicationReputationQuery containing metadata of the
    *        downloaded file.
    *
    * @param aCallback
    *        The callback for receiving the results of the query.
@@ -96,12 +105,18 @@ interface nsIApplicationReputationCallba
    * @param aStatus
    *        NS_OK if and only if the query succeeded. If it did, then
    *        shouldBlock is meaningful (otherwise it defaults to false). This
    *        may be NS_ERROR_FAILURE if the response cannot be parsed, or
    *        NS_ERROR_NOT_AVAILABLE if the service has been disabled or is not
    *        reachable.
    * @param aShouldBlock
    *        Whether or not the download should be blocked.
+   * @param aVerdict
+   *        Indicates the result of the lookup that determines whether the
+   *        download should be blocked, according to the "VERDICT_" constants.
+   *        This may be set to a value different than "VERDICT_SAFE" even if
+   *        aShouldBlock is false, so you should always check aShouldBlock.
    */
   void onComplete(in bool aShouldBlock,
-                  in nsresult aStatus);
+                  in nsresult aStatus,
+                  in unsigned long aVerdict);
 };