Bug 1254100 - Part 2 - Downloads blocked by Application Reputation should provide information about the verdict. r=mak

MozReview-Commit-ID: FYH5Tdtbzn

--HG--
extra : rebase_source : a59e79ac247d5f6d039d25f6ee6aea6cf2b5dbe6
This commit is contained in:
Paolo Amadini 2016-03-16 14:29:23 +00:00
parent 9badfa332e
commit 44ddde4b6e
7 changed files with 93 additions and 31 deletions

View File

@ -140,13 +140,6 @@ PrefObserver.register({
* and provides shared methods for all the instances of the user interface.
*/
this.DownloadsCommon = {
/**
* Constants with the different types of unblock messages.
*/
BLOCK_VERDICT_MALWARE: "Malware",
BLOCK_VERDICT_POTENTIALLY_UNWANTED: "PotentiallyUnwanted",
BLOCK_VERDICT_UNCOMMON: "Uncommon",
/**
* Returns an object whose keys are the string names from the downloads string
* bundle, and whose values are either the translated strings or functions
@ -528,15 +521,17 @@ this.DownloadsCommon = {
* Displays an alert message box which asks the user if they want to
* unblock the downloaded file or not.
*
* @param aType
* The type of malware the downloaded file contains.
* @param aVerdict
* The detailed reason why the download was blocked, according to the
* "Downloads.Error.BLOCK_VERDICT_" constants. If an unknown reason is
* specified, "Downloads.Error.BLOCK_VERDICT_MALWARE" is assumed.
* @param aOwnerWindow
* The window with which this action is associated.
*
* @return True to unblock the file, false to keep the user safe and
* cancel the operation.
*/
confirmUnblockDownload: Task.async(function* (aType, aOwnerWindow) {
confirmUnblockDownload: Task.async(function* (aVerdict, aOwnerWindow) {
let s = DownloadsCommon.strings;
let title = s.unblockHeader;
let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
@ -547,15 +542,15 @@ this.DownloadsCommon = {
let okButton = s.unblockButtonContinue;
let cancelButton = s.unblockButtonCancel;
switch (aType) {
case this.BLOCK_VERDICT_MALWARE:
type = s.unblockTypeMalware;
switch (aVerdict) {
case Downloads.Error.BLOCK_VERDICT_UNCOMMON:
type = s.unblockTypeUncommon;
break;
case this.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
type = s.unblockTypePotentiallyUnwanted;
break;
case this.BLOCK_VERDICT_UNCOMMON:
type = s.unblockTypeUncommon;
default: // Assume Downloads.Error.BLOCK_VERDICT_MALWARE
type = s.unblockTypeMalware;
break;
}

View File

@ -372,8 +372,8 @@ HistoryDownloadElementShell.prototype = {
},
downloadsCmd_unblock() {
DownloadsCommon.confirmUnblockDownload(DownloadsCommon.BLOCK_VERDICT_MALWARE,
window).then((confirmed) => {
let verdict = this.download.error.reputationCheckVerdict;
DownloadsCommon.confirmUnblockDownload(verdict, window).then(confirmed => {
if (confirmed) {
return this.download.unblock();
}

View File

@ -1099,8 +1099,8 @@ DownloadsViewItem.prototype = {
downloadsCmd_unblock() {
DownloadsPanel.hidePanel();
DownloadsCommon.confirmUnblockDownload(DownloadsCommon.BLOCK_VERDICT_MALWARE,
window).then((confirmed) => {
let verdict = this.download.error.reputationCheckVerdict;
DownloadsCommon.confirmUnblockDownload(verdict, window).then(confirmed => {
if (confirmed) {
return this.download.unblock();
}

View File

@ -33,14 +33,14 @@ function addDialogOpenObserver(buttonAction) {
add_task(function* test_confirm_unblock_dialog_unblock() {
addDialogOpenObserver("accept");
let result = yield DownloadsCommon.confirmUnblockDownload(DownloadsCommon.BLOCK_VERDICT_MALWARE,
let result = yield DownloadsCommon.confirmUnblockDownload(Downloads.Error.BLOCK_VERDICT_MALWARE,
window);
ok(result, "Should return true when the user clicks on `Unblock` button.");
});
add_task(function* test_confirm_unblock_dialog_keep_safe() {
addDialogOpenObserver("cancel");
let result = yield DownloadsCommon.confirmUnblockDownload(DownloadsCommon.BLOCK_VERDICT_MALWARE,
let result = yield DownloadsCommon.confirmUnblockDownload(Downloads.Error.BLOCK_VERDICT_MALWARE,
window);
ok(!result, "Should return false when the user clicks on `Keep me safe` button.");
});

View File

@ -1529,6 +1529,7 @@ this.DownloadError = function (aProperties)
} else if (aProperties.becauseBlockedByReputationCheck) {
this.becauseBlocked = true;
this.becauseBlockedByReputationCheck = true;
this.reputationCheckVerdict = aProperties.reputationCheckVerdict || "";
} else if (aProperties.becauseBlockedByRuntimePermissions) {
this.becauseBlocked = true;
this.becauseBlockedByRuntimePermissions = true;
@ -1543,6 +1544,16 @@ this.DownloadError = function (aProperties)
this.stack = new Error().stack;
}
/**
* These constants are used by the reputationCheckVerdict property and indicate
* the detailed reason why a download is blocked.
*
* @note These values should not be changed because they can be serialized.
*/
this.DownloadError.BLOCK_VERDICT_MALWARE = "Malware";
this.DownloadError.BLOCK_VERDICT_POTENTIALLY_UNWANTED = "PotentiallyUnwanted";
this.DownloadError.BLOCK_VERDICT_UNCOMMON = "Uncommon";
this.DownloadError.prototype = {
__proto__: Error.prototype,
@ -1588,6 +1599,15 @@ this.DownloadError.prototype = {
*/
becauseBlockedByRuntimePermissions: false,
/**
* If becauseBlockedByReputationCheck is true, indicates the detailed reason
* why the download was blocked, according to the "BLOCK_VERDICT_" constants.
*
* If the download was not blocked or the reason for the block is unknown,
* this will be an empty string.
*/
reputationCheckVerdict: "",
/**
* If this DownloadError was caused by an exception this property will
* contain the original exception. This will not be serialized when saving
@ -1611,6 +1631,7 @@ this.DownloadError.prototype = {
becauseBlockedByParentalControls: this.becauseBlockedByParentalControls,
becauseBlockedByReputationCheck: this.becauseBlockedByReputationCheck,
becauseBlockedByRuntimePermissions: this.becauseBlockedByRuntimePermissions,
reputationCheckVerdict: this.reputationCheckVerdict,
};
serializeUnknownProperties(this, serializable);
@ -1636,7 +1657,8 @@ this.DownloadError.fromSerializable = function (aSerializable) {
property != "becauseBlocked" &&
property != "becauseBlockedByParentalControls" &&
property != "becauseBlockedByReputationCheck" &&
property != "becauseBlockedByRuntimePermissions");
property != "becauseBlockedByRuntimePermissions" &&
property != "reputationCheckVerdict");
return e;
};
@ -2148,7 +2170,9 @@ this.DownloadCopySaver.prototype = {
let targetPath = this.download.target.path;
let partFilePath = this.download.target.partFilePath;
if (yield DownloadIntegration.shouldBlockForReputationCheck(download)) {
let { shouldBlock, verdict } =
yield DownloadIntegration.shouldBlockForReputationCheck(download);
if (shouldBlock) {
download.progress = 100;
download.hasPartialData = false;
@ -2166,7 +2190,10 @@ this.DownloadCopySaver.prototype = {
download.hasBlockedData = true;
}
throw new DownloadError({ becauseBlockedByReputationCheck: true });
throw new DownloadError({
becauseBlockedByReputationCheck: true,
reputationCheckVerdict: verdict,
});
}
if (partFilePath) {

View File

@ -125,6 +125,20 @@ const kObserverTopics = [
"xpcom-will-shutdown",
];
/**
* Maps nsIApplicationReputationService verdicts with the DownloadError ones.
*/
const kVerdictMap = {
[Ci.nsIApplicationReputationService.VERDICT_DANGEROUS]:
Downloads.Error.BLOCK_VERDICT_MALWARE,
[Ci.nsIApplicationReputationService.VERDICT_UNCOMMON]:
Downloads.Error.BLOCK_VERDICT_UNCOMMON,
[Ci.nsIApplicationReputationService.VERDICT_POTENTIALLY_UNWANTED]:
Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED,
[Ci.nsIApplicationReputationService.VERDICT_DANGEROUS_HOST]:
Downloads.Error.BLOCK_VERDICT_MALWARE,
};
////////////////////////////////////////////////////////////////////////////////
//// DownloadIntegration
@ -148,6 +162,7 @@ this.DownloadIntegration = {
dontCheckApplicationReputation: true,
#endif
shouldBlockInTestForApplicationReputation: false,
verdictInTestForApplicationReputation: "",
shouldKeepBlockedDataInTest: false,
dontOpenFileAndFolder: false,
downloadDoneCalled: false,
@ -531,11 +546,20 @@ this.DownloadIntegration = {
* The download object.
*
* @return {Promise}
* @resolves The boolean indicates to block downloads or not.
* @resolves Object with the following properties:
* {
* shouldBlock: Whether the download should be blocked.
* verdict: Detailed reason for the block, according to the
* "Downloads.Error.BLOCK_VERDICT_" constants, or empty
* string if the reason is unknown.
* }
*/
shouldBlockForReputationCheck: function (aDownload) {
if (this.dontCheckApplicationReputation) {
return Promise.resolve(this.shouldBlockInTestForApplicationReputation);
return Promise.resolve({
shouldBlock: this.shouldBlockInTestForApplicationReputation,
verdict: this.verdictInTestForApplicationReputation,
});
}
let hash;
let sigInfo;
@ -546,10 +570,16 @@ this.DownloadIntegration = {
channelRedirects = aDownload.saver.getRedirects();
} catch (ex) {
// Bail if DownloadSaver doesn't have a hash or signature info.
return Promise.resolve(false);
return Promise.resolve({
shouldBlock: false,
verdict: "",
});
}
if (!hash || !sigInfo) {
return Promise.resolve(false);
return Promise.resolve({
shouldBlock: false,
verdict: "",
});
}
let deferred = Promise.defer();
let aReferrer = null;
@ -564,8 +594,11 @@ this.DownloadIntegration = {
suggestedFileName: OS.Path.basename(aDownload.target.path),
signatureInfo: sigInfo,
redirects: channelRedirects },
function onComplete(aShouldBlock, aRv) {
deferred.resolve(aShouldBlock);
function onComplete(aShouldBlock, aRv, aVerdict) {
deferred.resolve({
shouldBlock: aShouldBlock,
verdict: (aShouldBlock && kVerdictMap[aVerdict]) || "",
});
});
return deferred.promise;
},

View File

@ -1711,12 +1711,15 @@ add_task(function* test_getSha256Hash()
var promiseBlockedDownload = Task.async(function* (options) {
function cleanup() {
DownloadIntegration.shouldBlockInTestForApplicationReputation = false;
DownloadIntegration.verdictInTestForApplicationReputation = "";
DownloadIntegration.shouldKeepBlockedDataInTest = false;
}
do_register_cleanup(cleanup);
let {keepPartialData, keepBlockedData} = options;
DownloadIntegration.shouldBlockInTestForApplicationReputation = true;
DownloadIntegration.verdictInTestForApplicationReputation =
Downloads.Error.BLOCK_VERDICT_UNCOMMON;
DownloadIntegration.shouldKeepBlockedDataInTest = keepBlockedData;
let download;
@ -1740,7 +1743,11 @@ var promiseBlockedDownload = Task.async(function* (options) {
throw ex;
}
do_check_true(ex.becauseBlockedByReputationCheck);
do_check_eq(ex.reputationCheckVerdict,
Downloads.Error.BLOCK_VERDICT_UNCOMMON);
do_check_true(download.error.becauseBlockedByReputationCheck);
do_check_eq(download.error.reputationCheckVerdict,
Downloads.Error.BLOCK_VERDICT_UNCOMMON);
}
do_check_true(download.stopped);