| 
Commits:
38cb023e
 by hackademix   at 2023-06-28T08:12:58+02:00 
 Bug 41854: Allow overriding download spam protection.
d3b18f88
 by hackademix   at 2023-06-28T08:16:35+02:00 
 fixup! Firefox preference overrides.
 
5 changed files:
Changes:
browser/app/profile/001-base-profile.js
 
| ... | ... | @@ -48,6 +48,9 @@ pref("security.nocertdb", true); |  
| 48 | 48 |  pref("browser.download.useDownloadDir", false);
 |  
| 49 | 49 |  pref("browser.download.manager.addToRecentDocs", false);
 |  
| 50 | 50 |  
 |  
|  | 51 | +// Prevent download stuffing / DOS (tor-browser#41764)
 |  
|  | 52 | +pref("browser.download.enable_spam_prevention", true);
 |  
|  | 53 | +
 |  
| 51 | 54 |  // Misc privacy: Disk
 |  
| 52 | 55 |  pref("signon.rememberSignons", false);
 |  
| 53 | 56 |  pref("browser.formfill.enable", false);
 |  browser/components/downloads/DownloadSpamProtection.jsm
 
 
| ... | ... | @@ -18,6 +18,8 @@ var { XPCOMUtils } = ChromeUtils.import( |  
| 18 | 18 |    "resource://gre/modules/XPCOMUtils.jsm"
 |  
| 19 | 19 |  );
 |  
| 20 | 20 |  
 |  
|  | 21 | +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 |  
|  | 22 | +
 |  
| 21 | 23 |  XPCOMUtils.defineLazyModuleGetters(this, {
 |  
| 22 | 24 |    BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
 |  
| 23 | 25 |    Downloads: "resource://gre/modules/Downloads.jsm",
 |  
| ... | ... | @@ -45,17 +47,18 @@ class DownloadSpamProtection { |  
| 45 | 47 |      return this.list;
 |  
| 46 | 48 |    }
 |  
| 47 | 49 |  
 |  
| 48 |  | -  update(url) {
 |  
|  | 50 | +  update(url, principal) {
 |  
| 49 | 51 |      if (this._blockedURLToDownloadSpam.has(url)) {
 |  
| 50 | 52 |        let downloadSpam = this._blockedURLToDownloadSpam.get(url);
 |  
| 51 | 53 |        this.spamList.remove(downloadSpam);
 |  
|  | 54 | +      downloadSpam.principal = principal;
 |  
| 52 | 55 |        downloadSpam.blockedDownloadsCount += 1;
 |  
| 53 | 56 |        this.spamList.add(downloadSpam);
 |  
| 54 | 57 |        this._indicator.onDownloadStateChanged(downloadSpam);
 |  
| 55 | 58 |        return;
 |  
| 56 | 59 |      }
 |  
| 57 | 60 |  
 |  
| 58 |  | -    let downloadSpam = new DownloadSpam(url);
 |  
|  | 61 | +    let downloadSpam = new DownloadSpam(url, principal, this);
 |  
| 59 | 62 |      this.spamList.add(downloadSpam);
 |  
| 60 | 63 |      this._blockedURLToDownloadSpam.set(url, downloadSpam);
 |  
| 61 | 64 |      let hasActiveDownloads = DownloadsCommon.summarizeDownloads(
 |  
| ... | ... | @@ -85,8 +88,10 @@ class DownloadSpamProtection { |  
| 85 | 88 |   * @extends Download
 |  
| 86 | 89 |   */
 |  
| 87 | 90 |  class DownloadSpam extends Download {
 |  
| 88 |  | -  constructor(url) {
 |  
|  | 91 | +  constructor(url, principal, protectionController) {
 |  
| 89 | 92 |      super();
 |  
|  | 93 | +    this.protectionController = protectionController;
 |  
|  | 94 | +    this.principal = principal.QueryInterface(Ci.nsIPrincipal);
 |  
| 90 | 95 |      this.hasBlockedData = true;
 |  
| 91 | 96 |      this.stopped = true;
 |  
| 92 | 97 |      this.error = new DownloadError({
 |  
| ... | ... | @@ -97,4 +102,16 @@ class DownloadSpam extends Download { |  
| 97 | 102 |      this.source = { url };
 |  
| 98 | 103 |      this.blockedDownloadsCount = 1;
 |  
| 99 | 104 |    }
 |  
|  | 105 | +  allow() {
 |  
|  | 106 | +    const pm = Services.perms;
 |  
|  | 107 | +    pm.addFromPrincipal(
 |  
|  | 108 | +      this.principal,
 |  
|  | 109 | +      "automatic-download",
 |  
|  | 110 | +      pm.ALLOW_ACTION,
 |  
|  | 111 | +      pm.EXPIRE_SESSION
 |  
|  | 112 | +    );
 |  
|  | 113 | +    this.hasBlockedData = this.hasPartialData = false;
 |  
|  | 114 | +    this.protectionController.clearDownloadSpam(this.source.url);
 |  
|  | 115 | +    this._notifyChange();
 |  
|  | 116 | +  }
 |  
| 100 | 117 |  } |  toolkit/components/downloads/DownloadCore.jsm
 
 
| ... | ... | @@ -717,6 +717,10 @@ Download.prototype = { |  
| 717 | 717 |      }
 |  
| 718 | 718 |  
 |  
| 719 | 719 |      this._promiseUnblock = (async () => {
 |  
|  | 720 | +      if (this.allow) {
 |  
|  | 721 | +        this.allow();
 |  
|  | 722 | +        return;
 |  
|  | 723 | +      }
 |  
| 720 | 724 |        try {
 |  
| 721 | 725 |          await IOUtils.move(this.target.partFilePath, this.target.path);
 |  
| 722 | 726 |          await this.target.refresh();
 |  
| ... | ... | @@ -725,7 +729,6 @@ Download.prototype = { |  
| 725 | 729 |          this._promiseUnblock = null;
 |  
| 726 | 730 |          throw ex;
 |  
| 727 | 731 |        }
 |  
| 728 |  | -
 |  
| 729 | 732 |        this.succeeded = true;
 |  
| 730 | 733 |        this.hasBlockedData = false;
 |  
| 731 | 734 |        this._notifyChange();
 |  
| ... | ... | @@ -955,7 +958,9 @@ Download.prototype = { |  
| 955 | 958 |              await this._promiseCanceled;
 |  
| 956 | 959 |            }
 |  
| 957 | 960 |            // Ask the saver object to remove any partial data.
 |  
| 958 |  | -          await this.saver.removeData();
 |  
|  | 961 | +          if (this.saver) {
 |  
|  | 962 | +            await this.saver.removeData();
 |  
|  | 963 | +          }
 |  
| 959 | 964 |            // For completeness, clear the number of bytes transferred.
 |  
| 960 | 965 |            if (this.currentBytes != 0 || this.hasPartialData) {
 |  
| 961 | 966 |              this.currentBytes = 0;
 |  toolkit/components/downloads/DownloadIntegration.jsm
 
 
| ... | ... | @@ -1234,7 +1234,7 @@ var DownloadObserver = { |  
| 1234 | 1234 |          ) {
 |  
| 1235 | 1235 |            DownloadIntegration._initializeDownloadSpamProtection();
 |  
| 1236 | 1236 |          }
 |  
| 1237 |  | -        DownloadIntegration.downloadSpamProtection.update(aData);
 |  
|  | 1237 | +        DownloadIntegration.downloadSpamProtection.update(aData, aSubject);
 |  
| 1238 | 1238 |          break;
 |  
| 1239 | 1239 |      }
 |  
| 1240 | 1240 |    },
 |  uriloader/exthandler/nsExternalHelperAppService.cpp
 
 
| ... | ... | @@ -1975,7 +1975,7 @@ bool nsExternalAppHandler::IsDownloadSpam(nsIChannel* aChannel) { |  
| 1975 | 1975 |        nsAutoCString cStringURI;
 |  
| 1976 | 1976 |        loadInfo->TriggeringPrincipal()->GetPrePath(cStringURI);
 |  
| 1977 | 1977 |        observerService->NotifyObservers(
 |  
| 1978 |  | -          nullptr, "blocked-automatic-download",
 |  
|  | 1978 | +          principal, "blocked-automatic-download",
 |  
| 1979 | 1979 |            NS_ConvertASCIItoUTF16(cStringURI.get()).get());
 |  
| 1980 | 1980 |        // FIXME: In order to escape memory leaks, currently we cancel blocked
 |  
| 1981 | 1981 |        // downloads. This is temporary solution, because download data should be
 |  
| ... | ... | @@ -1989,7 +1989,7 @@ bool nsExternalAppHandler::IsDownloadSpam(nsIChannel* aChannel) { |  
| 1989 | 1989 |    if (!loadInfo->GetHasValidUserGestureActivation()) {
 |  
| 1990 | 1990 |      permissionManager->AddFromPrincipal(
 |  
| 1991 | 1991 |          principal, type, nsIPermissionManager::PROMPT_ACTION,
 |  
| 1992 |  | -        nsIPermissionManager::EXPIRE_NEVER, 0 /* expire time */);
 |  
|  | 1992 | +        nsIPermissionManager::EXPIRE_SESSION, 0 /* expire time */);
 |  
| 1993 | 1993 |    }
 |  
| 1994 | 1994 |  
 |  
| 1995 | 1995 |    return false;
 |  
 |