... |
... |
@@ -320,7 +320,11 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest* request, |
320
|
320
|
return NS_OK;
|
321
|
321
|
}
|
322
|
322
|
|
323
|
|
-static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) {
|
|
323
|
+static bool IsContentPDF(
|
|
324
|
+ nsIChannel* aChannel, const nsACString& aContentType,
|
|
325
|
+ nsAutoCString* aOutExt =
|
|
326
|
+ nullptr // best-guess file extension, useful for non-PDFs
|
|
327
|
+) {
|
324
|
328
|
bool isPDF = aContentType.LowerCaseEqualsASCII(APPLICATION_PDF);
|
325
|
329
|
if (!isPDF && (aContentType.LowerCaseEqualsASCII(APPLICATION_OCTET_STREAM) ||
|
326
|
330
|
aContentType.IsEmpty())) {
|
... |
... |
@@ -328,14 +332,25 @@ static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) { |
328
|
332
|
aChannel->GetContentDispositionFilename(flname);
|
329
|
333
|
isPDF = StringEndsWith(flname, u".pdf"_ns);
|
330
|
334
|
if (!isPDF) {
|
|
335
|
+ nsAutoCString ext;
|
331
|
336
|
nsCOMPtr<nsIURI> uri;
|
332
|
337
|
aChannel->GetURI(getter_AddRefs(uri));
|
333
|
338
|
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
334
|
339
|
if (url) {
|
335
|
|
- nsAutoCString ext;
|
336
|
340
|
url->GetFileExtension(ext);
|
337
|
341
|
isPDF = ext.EqualsLiteral("pdf");
|
338
|
342
|
}
|
|
343
|
+ if (aOutExt) {
|
|
344
|
+ // Fill the extension out param if required
|
|
345
|
+ if (!(isPDF || flname.IsEmpty())) {
|
|
346
|
+ // For non PDFs, fallback to filename from content disposition
|
|
347
|
+ int32_t extStart = flname.RFindChar(u'.');
|
|
348
|
+ if (extStart != kNotFound) {
|
|
349
|
+ CopyUTF16toUTF8(Substring(flname, extStart + 1), ext);
|
|
350
|
+ }
|
|
351
|
+ }
|
|
352
|
+ *aOutExt = ext;
|
|
353
|
+ }
|
339
|
354
|
}
|
340
|
355
|
}
|
341
|
356
|
|
... |
... |
@@ -343,7 +358,7 @@ static bool IsContentPDF(nsIChannel* aChannel, const nsACString& aContentType) { |
343
|
358
|
}
|
344
|
359
|
|
345
|
360
|
static mozilla::Result<bool, nsresult> ShouldHandleExternally(
|
346
|
|
- const nsACString& aMimeType) {
|
|
361
|
+ const nsACString& aMimeType, const nsACString& aExtension) {
|
347
|
362
|
// For a PDF, check if the preference is set that forces attachments to be
|
348
|
363
|
// opened inline. If so, treat it as a non-attachment by clearing
|
349
|
364
|
// 'forceExternalHandling' again. This allows it open a PDF directly
|
... |
... |
@@ -356,7 +371,7 @@ static mozilla::Result<bool, nsresult> ShouldHandleExternally( |
356
|
371
|
return mozilla::Err(NS_ERROR_FAILURE);
|
357
|
372
|
}
|
358
|
373
|
|
359
|
|
- mimeSvc->GetFromTypeAndExtension(aMimeType, EmptyCString(),
|
|
374
|
+ mimeSvc->GetFromTypeAndExtension(aMimeType, aExtension,
|
360
|
375
|
getter_AddRefs(mimeInfo));
|
361
|
376
|
|
362
|
377
|
if (mimeInfo) {
|
... |
... |
@@ -430,31 +445,43 @@ nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request) { |
430
|
445
|
forceExternalHandling = false;
|
431
|
446
|
}
|
432
|
447
|
|
|
448
|
+ nsAutoCString ext;
|
|
449
|
+ bool isPDF =
|
|
450
|
+ forceExternalHandling && IsContentPDF(aChannel, mContentType, &ext);
|
|
451
|
+
|
433
|
452
|
bool maybeForceInternalHandling =
|
434
|
|
- forceExternalHandling &&
|
435
|
|
- (mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline() ||
|
436
|
|
- mozilla::StaticPrefs::browser_download_ignore_content_disposition());
|
|
453
|
+ (isPDF &&
|
|
454
|
+ mozilla::StaticPrefs::browser_download_open_pdf_attachments_inline()) ||
|
|
455
|
+ (
|
|
456
|
+ forceExternalHandling &&
|
|
457
|
+ mozilla::StaticPrefs::browser_download_ignore_content_disposition() &&
|
|
458
|
+ // we want to exclude html and svg files, which could execute
|
|
459
|
+ // scripts (tor-browser#43211)
|
|
460
|
+ kNotFound == mContentType.LowerCaseFindASCII("html") &&
|
|
461
|
+ kNotFound == ext.LowerCaseFindASCII("htm") &&
|
|
462
|
+ kNotFound == mContentType.LowerCaseFindASCII("/svg+") &&
|
|
463
|
+ !ext.EqualsIgnoreCase("svg"));
|
437
|
464
|
|
438
|
465
|
// Check if this is a PDF which should be opened internally. We also handle
|
439
|
466
|
// octet-streams that look like they might be PDFs based on their extension.
|
|
467
|
+ // Additionally, we try to avoid downloading also non-PDF attachments
|
|
468
|
+ // when the general Content-Disposition override preference is set to true.
|
440
|
469
|
if (maybeForceInternalHandling) {
|
441
|
|
- // For a PDF, check if the preference is set that forces attachments to be
|
442
|
|
- // opened inline. If so, treat it as a non-attachment by clearing
|
|
470
|
+ // Preferences are set to open attachments inline by clearing
|
443
|
471
|
// 'forceExternalHandling' again. This allows it open a PDF directly
|
444
|
472
|
// instead of downloading it first. It may still end up being handled by
|
445
|
473
|
// a helper app depending anyway on the later checks.
|
446
|
|
- nsCString mimeType = IsContentPDF(aChannel, mContentType)
|
447
|
|
- ? nsLiteralCString(APPLICATION_PDF)
|
448
|
|
- : mContentType;
|
449
|
|
- auto result = ShouldHandleExternally(mimeType);
|
|
474
|
+ // This may apply to other file types if an internal handler exists.
|
|
475
|
+ auto result = ShouldHandleExternally(
|
|
476
|
+ isPDF ? nsLiteralCString(APPLICATION_PDF) : mContentType, ext);
|
450
|
477
|
if (result.isErr()) {
|
451
|
478
|
return result.unwrapErr();
|
452
|
479
|
}
|
453
|
480
|
forceExternalHandling = result.unwrap();
|
454
|
481
|
|
455
|
|
- // If we're not opening the PDF externally we block it if it's sandboxed.
|
|
482
|
+ // If we're not opening the file externally and it's sandboxed we block it.
|
456
|
483
|
if (IsSandboxed(aChannel) && !forceExternalHandling) {
|
457
|
|
- LOG(("Blocked sandboxed PDF"));
|
|
484
|
+ LOG(("Blocked sandboxed file"));
|
458
|
485
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
459
|
486
|
if (httpChannel) {
|
460
|
487
|
nsContentSecurityUtils::LogMessageToConsole(
|