Bug 1965504 part 5 - integrate Content Analysis into Downloads a=diannaS

The actual call to content analysis is in DownloadCore.sys.mjs's
_checkReputationAndMove() method.

Original Revision: https://phabricator.services.mozilla.com/D251881

Differential Revision: https://phabricator.services.mozilla.com/D258207
This commit is contained in:
Greg Stoll
2025-07-23 17:30:23 +00:00
committed by dsmith@mozilla.com
parent 143dc7420f
commit 099ed944b9
9 changed files with 493 additions and 36 deletions

View File

@@ -143,6 +143,7 @@ export var DownloadsCommon = {
DOWNLOAD_BLOCKED_PARENTAL: 6,
DOWNLOAD_DIRTY: 8,
DOWNLOAD_BLOCKED_POLICY: 9,
DOWNLOAD_BLOCKED_CONTENT_ANALYSIS: 10,
// The following are the possible values of the "attention" property.
ATTENTION_NONE: "",
@@ -295,6 +296,18 @@ export var DownloadsCommon = {
if (download.error.becauseBlockedByReputationCheck) {
return DownloadsCommon.DOWNLOAD_DIRTY;
}
if (download.error.becauseBlockedByContentAnalysis) {
// BLOCK_VERDICT_MALWARE indicates that the download was
// blocked by the content analysis service, so return
// DOWNLOAD_BLOCKED_CONTENT_ANALYSIS to indicate this.
// Otherwise, the content analysis service returned
// WARN, so the user has a chance to unblock the download,
// which corresponds with DOWNLOAD_DIRTY.
return download.error.reputationCheckVerdict ===
lazy.Downloads.Error.BLOCK_VERDICT_MALWARE
? DownloadsCommon.DOWNLOAD_BLOCKED_CONTENT_ANALYSIS
: DownloadsCommon.DOWNLOAD_DIRTY;
}
return DownloadsCommon.DOWNLOAD_FAILED;
}
if (download.canceled) {
@@ -311,7 +324,7 @@ export var DownloadsCommon = {
*/
async deleteDownload(download) {
// Check hasBlockedData to avoid double counting if you click the X button
// in the Libarary view and then delete the download from the history.
// in the Library view and then delete the download from the history.
if (
download.error?.becauseBlockedByReputationCheck &&
download.hasBlockedData
@@ -331,6 +344,9 @@ export var DownloadsCommon = {
}
let list = await lazy.Downloads.getList(lazy.Downloads.ALL);
await list.remove(download);
if (download.error?.becauseBlockedByContentAnalysis) {
await download.respondToContentAnalysisWarnWithBlock();
}
await download.finalize(true);
},
@@ -359,6 +375,9 @@ export var DownloadsCommon = {
await list.remove(download);
}
await download.manuallyRemoveData();
if (download.error?.becauseBlockedByContentAnalysis) {
await download.respondToContentAnalysisWarnWithBlock();
}
if (clearHistory < 2) {
lazy.DownloadHistory.updateMetaData(download).catch(console.error);
}
@@ -629,6 +648,8 @@ export var DownloadsCommon = {
* the "Downloads.Error.BLOCK_VERDICT_" constants. If an unknown
* reason is specified, "Downloads.Error.BLOCK_VERDICT_MALWARE" is
* assumed.
* becauseBlockedByReputationCheck:
* Whether the the download was blocked by a reputation check.
* window:
* The window with which this action is associated.
* dialogType:
@@ -645,7 +666,12 @@ export var DownloadsCommon = {
* - "confirmBlock" to delete the blocked data permanently.
* - "cancel" to do nothing and cancel the operation.
*/
async confirmUnblockDownload({ verdict, window, dialogType }) {
async confirmUnblockDownload({
verdict,
becauseBlockedByReputationCheck,
window,
dialogType,
}) {
let s = DownloadsCommon.strings;
// All the dialogs have an action button and a cancel button, while only
@@ -685,12 +711,18 @@ export var DownloadsCommon = {
}
let message;
let tip = s.unblockTip2;
switch (verdict) {
case lazy.Downloads.Error.BLOCK_VERDICT_UNCOMMON:
message = s.unblockTypeUncommon2;
break;
case lazy.Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
message = s.unblockTypePotentiallyUnwanted2;
if (becauseBlockedByReputationCheck) {
message = s.unblockTypePotentiallyUnwanted2;
} else {
message = s.unblockTypeContentAnalysisWarn;
tip = s.unblockContentAnalysisTip;
}
break;
case lazy.Downloads.Error.BLOCK_VERDICT_INSECURE:
message = s.unblockInsecure2;
@@ -700,7 +732,7 @@ export var DownloadsCommon = {
message = s.unblockTypeMalware;
break;
}
message += "\n\n" + s.unblockTip2;
message += "\n\n" + tip;
Services.ww.registerNotification(function onOpen(subj, topic) {
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
@@ -860,7 +892,10 @@ DownloadsDataCtor.prototype = {
download,
DownloadsCommon.stateOfDownload(download)
);
if (download.error?.becauseBlockedByReputationCheck) {
if (
download.error?.becauseBlockedByReputationCheck ||
download.error?.becauseBlockedByContentAnalysis
) {
this._notifyDownloadEvent("error");
}
},

View File

@@ -770,7 +770,10 @@ DownloadsViewUI.DownloadElementShell.prototype = {
lazy.DownloadsCommon.strings.stateBlockedParentalControls
);
this.hideButton();
} else if (this.download.error.becauseBlockedByReputationCheck) {
} else if (
this.download.error.becauseBlockedByReputationCheck ||
this.download.error.becauseBlockedByContentAnalysis
) {
verdict = this.download.error.reputationCheckVerdict;
let hover = "";
if (!this.download.hasBlockedData) {
@@ -882,7 +885,8 @@ DownloadsViewUI.DownloadElementShell.prototype = {
let s = lazy.DownloadsCommon.strings;
if (
!this.download.error ||
!this.download.error.becauseBlockedByReputationCheck
(!this.download.error.becauseBlockedByReputationCheck &&
!this.download.error.becauseBlockedByContentAnalysis)
) {
return [null, null];
}
@@ -895,14 +899,37 @@ DownloadsViewUI.DownloadElementShell.prototype = {
[s.unblockInsecure2, s.unblockTip2],
];
case lazy.Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
if (this.download.error.becauseBlockedByReputationCheck) {
return [
s.blockedPotentiallyUnwanted,
[s.unblockTypePotentiallyUnwanted2, s.unblockTip2],
];
}
if (!this.download.error.becauseBlockedByContentAnalysis) {
// We expect one of becauseBlockedByReputationCheck or
// becauseBlockedByContentAnalysis to be true; if not,
// fall through to the error case.
break;
}
return [
s.blockedPotentiallyUnwanted,
[s.unblockTypePotentiallyUnwanted2, s.unblockTip2],
s.warnedByContentAnalysis,
[s.unblockTypeContentAnalysisWarn, s.unblockContentAnalysisWarnTip],
];
case lazy.Downloads.Error.BLOCK_VERDICT_MALWARE:
return [s.blockedMalware, [s.unblockTypeMalware, s.unblockTip2]];
case lazy.Downloads.Error.BLOCK_VERDICT_DOWNLOAD_SPAM:
if (this.download.error.becauseBlockedByReputationCheck) {
return [s.blockedMalware, [s.unblockTypeMalware, s.unblockTip2]];
}
if (!this.download.error.becauseBlockedByContentAnalysis) {
// We expect one of becauseBlockedByReputationCheck or
// becauseBlockedByContentAnalysis to be true; if not,
// fall through to the error case.
break;
}
return [
s.blockedByContentAnalysis,
[s.unblockContentAnalysis1, s.unblockContentAnalysis2],
];
case lazy.Downloads.Error.BLOCK_VERDICT_DOWNLOAD_SPAM: {
let title = {
id: "downloads-files-not-downloaded",
args: {
@@ -914,6 +941,7 @@ DownloadsViewUI.DownloadElementShell.prototype = {
args: { url: DownloadsViewUI.getStrippedUrl(this.download) },
};
return [{ l10n: title }, [{ l10n: details }, null]];
}
}
throw new Error(
"Unexpected reputationCheckVerdict: " +
@@ -944,6 +972,8 @@ DownloadsViewUI.DownloadElementShell.prototype = {
confirmUnblock(window, dialogType) {
lazy.DownloadsCommon.confirmUnblockDownload({
verdict: this.download.error.reputationCheckVerdict,
becauseBlockedByReputationCheck:
this.download.error.becauseBlockedByReputationCheck,
window,
dialogType,
})
@@ -990,6 +1020,7 @@ DownloadsViewUI.DownloadElementShell.prototype = {
case lazy.DownloadsCommon.DOWNLOAD_FINISHED:
return "downloadsCmd_open";
case lazy.DownloadsCommon.DOWNLOAD_BLOCKED_PARENTAL:
case lazy.DownloadsCommon.DOWNLOAD_BLOCKED_CONTENT_ANALYSIS:
return "downloadsCmd_openReferrer";
case lazy.DownloadsCommon.DOWNLOAD_DIRTY:
return "downloadsCmd_showBlockedInfo";

View File

@@ -41,9 +41,10 @@
display: none;
}
.download-state:not([state="6"],/* Blocked (parental) */
[state="8"],/* Blocked (dirty) */
[state="9"] /* Blocked (policy) */)
.download-state:not([state="6"], /* Blocked (parental) */
[state="8"], /* Blocked (dirty) */
[state="9"], /* Blocked (policy) */
[state="10"] /* Blocked (content analysis) */)
.downloadBlockedBadge,
.download-state:not([state="-1"],/* Starting (initial) */

View File

@@ -1710,6 +1710,14 @@ var DownloadsBlockedSubview = {
let e = this.elements;
let s = DownloadsCommon.strings;
e.deleteButton.hidden =
download.error?.becauseBlockedByContentAnalysis &&
download.error?.reputationCheckVerdict === "Malware";
e.unblockButton.hidden =
download.error?.becauseBlockedByContentAnalysis &&
download.error?.reputationCheckVerdict === "Malware";
title.l10n
? document.l10n.setAttributes(e.title, title.l10n.id, title.l10n.args)
: (e.title.textContent = title);