Commit 25b1c10c authored by Pier Angelo Vendrame's avatar Pier Angelo Vendrame 🎃 Committed by clairehurst
Browse files

BB 42220: Allow for more file types to be forced-inline.

Firefox allows to open some files in the browser without any
confirmation, but this will result in a disk leak, because the file will
be downloaded to the temporary directory first (and not deleted, in some
cases).
A preference allows PDFs to be opened without being downloaded to disk.
So, we introduce a similar one to do the same for all the files that are
set to be opened automatically in the browser, except svg and html files
to prevent XSS hazards (see BB 43211).
parent 9afa5397
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1622,6 +1622,12 @@
  value: false
  mirror: always
# tor-browser#42220
- name: browser.download.ignore_content_disposition
  type: bool
  value: true
  mirror: always
# See bug 1811830
- name: browser.download.force_save_internally_handled_attachments
  type: bool
+43 −12
Original line number Diff line number Diff line
@@ -321,7 +321,11 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest* request,
  return NS_OK;
}

static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) {
static bool IsContentPDF(
    nsIChannel* aChannel, const nsACString& aContentType,
    nsAutoCString* aOutExt =
        nullptr  // best-guess file extension, useful for non-PDFs
) {
  bool isPDF = aContentType.LowerCaseEqualsASCII(APPLICATION_PDF);
  if (!isPDF && (aContentType.LowerCaseEqualsASCII(APPLICATION_OCTET_STREAM) ||
                 aContentType.IsEmpty())) {
@@ -329,14 +333,25 @@ static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) {
    aChannel->GetContentDispositionFilename(flname);
    isPDF = StringEndsWith(flname, u".pdf"_ns);
    if (!isPDF) {
      nsAutoCString ext;
      nsCOMPtr<nsIURI> uri;
      aChannel->GetURI(getter_AddRefs(uri));
      nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
      if (url) {
        nsAutoCString ext;
        url->GetFileExtension(ext);
        isPDF = ext.EqualsLiteral("pdf");
      }
      if (aOutExt) {
        // Fill the extension out param if required
        if (!(isPDF || flname.IsEmpty())) {
          // For non PDFs, fallback to filename from content disposition
          int32_t extStart = flname.RFindChar(u'.');
          if (extStart != kNotFound) {
            CopyUTF16toUTF8(Substring(flname, extStart + 1), ext);
          }
        }
        *aOutExt = ext;
      }
    }
  }

@@ -344,7 +359,7 @@ static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) {
}

static mozilla::Result<bool, nsresult> ShouldHandleExternally(
    const nsACString& aMimeType) {
    const nsACString& aMimeType, const nsACString& aExtension) {
  // For a PDF, check if the preference is set that forces attachments to be
  // opened inline. If so, treat it as a non-attachment by clearing
  // 'forceExternalHandling' again. This allows it open a PDF directly
@@ -357,7 +372,7 @@ static mozilla::Result<bool, nsresult> ShouldHandleExternally(
    return mozilla::Err(NS_ERROR_FAILURE);
  }

  mimeSvc->GetFromTypeAndExtension(aMimeType, EmptyCString(),
  mimeSvc->GetFromTypeAndExtension(aMimeType, aExtension,
                                   getter_AddRefs(mimeInfo));

  if (mimeInfo) {
@@ -431,27 +446,43 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request) {
    forceExternalHandling = false;
  }

  nsAutoCString ext;
  bool isPDF =
      forceExternalHandling && IsContentPDF(aChannel, mContentType, &ext);

  bool maybeForceInternalHandling =
      (isPDF &&
        mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline()) ||
       (
        forceExternalHandling &&
      mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline();
        mozilla::StaticPrefs::browser_download_ignore_content_disposition() &&
        // we want to exclude html and svg files, which could execute
        // scripts (tor-browser#43211)
        kNotFound == mContentType.LowerCaseFindASCII("html") &&
        kNotFound == ext.LowerCaseFindASCII("htm") &&
        kNotFound == mContentType.LowerCaseFindASCII("/svg+") &&
        !ext.EqualsIgnoreCase("svg"));

  // Check if this is a PDF which should be opened internally. We also handle
  // octet-streams that look like they might be PDFs based on their extension.
  if (maybeForceInternalHandling && IsContentPDF(aChannel, mContentType)) {
    // For a PDF, check if the preference is set that forces attachments to be
    // opened inline. If so, treat it as a non-attachment by clearing
  // Additionally, we try to avoid downloading also non-PDF attachments
  // when the general Content-Disposition override preference is set to true.
  if (maybeForceInternalHandling) {
    // Preferences are set to open attachments inline by clearing
    // 'forceExternalHandling' again. This allows it open a PDF directly
    // instead of downloading it first. It may still end up being handled by
    // a helper app depending anyway on the later checks.
    auto result = ShouldHandleExternally(nsLiteralCString(APPLICATION_PDF));
    // This may apply to other file types if an internal handler exists.
    auto result = ShouldHandleExternally(
        isPDF ? nsLiteralCString(APPLICATION_PDF) : mContentType, ext);
    if (result.isErr()) {
      return result.unwrapErr();
    }
    forceExternalHandling = result.unwrap();

    // If we're not opening the PDF externally we block it if it's sandboxed.
    // If we're not opening the file externally and it's sandboxed we block it.
    if (IsSandboxed(aChannel) && !forceExternalHandling) {
      LOG(("Blocked sandboxed PDF"));
      LOG(("Blocked sandboxed file"));
      nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
      if (httpChannel) {
        nsContentSecurityUtils::LogMessageToConsole(