tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit ba4d82ada3b05ed2d5729d1b790140d1b2d02bc3
parent fb83fbd6982a5c535cff012f8dc8158a12f2e4e5
Author: Pier Angelo Vendrame <pierov@torproject.org>
Date:   Tue, 10 Sep 2024 18:54:30 +0200

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).

Diffstat:
Mmodules/libpref/init/StaticPrefList.yaml | 6++++++
Muriloader/base/nsURILoader.cpp | 54++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 48 insertions(+), 12 deletions(-)

diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml @@ -1636,6 +1636,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 diff --git a/uriloader/base/nsURILoader.cpp b/uriloader/base/nsURILoader.cpp @@ -318,7 +318,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())) { @@ -326,14 +330,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; + } } } @@ -341,7 +356,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 @@ -354,7 +369,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) { @@ -428,27 +443,42 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request) { forceExternalHandling = false; } + nsAutoCString ext; + bool isPDF = + forceExternalHandling && IsContentPDF(aChannel, mContentType, &ext); + bool maybeForceInternalHandling = - forceExternalHandling && - mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline(); + (isPDF && + mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline()) || + (forceExternalHandling && + 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(