ma1 pushed to branch mullvad-browser-128.13.0esr-14.5-1 at The Tor Project / Applications / Mullvad Browser
Commits:
-
16d48c99
by Cathy Lu at 2025-07-22T01:59:35+02:00
-
2ac18296
by Tom Schuster at 2025-07-22T01:59:37+02:00
-
6221ed58
by Tom Schuster at 2025-07-22T01:59:38+02:00
-
00cf1f42
by Andreas Pehrson at 2025-07-22T01:59:40+02:00
-
7581b39d
by Tom Schuster at 2025-07-22T01:59:41+02:00
-
15787e0c
by Pier Angelo Vendrame at 2025-07-22T01:59:43+02:00
9 changed files:
- dom/media/MediaManager.cpp
- dom/security/nsContentSecurityUtils.cpp
- dom/security/nsContentSecurityUtils.h
- dom/xslt/xpath/txXPathNode.h
- dom/xslt/xslt/txNodeSorter.cpp
- mobile/android/components/geckoview/GeckoViewStreamListener.cpp
- modules/libpref/init/StaticPrefList.yaml
- + testing/web-platform/tests/content-security-policy/frame-src/frame-src-blocked-path-matching.sub.html
- uriloader/exthandler/nsExternalHelperAppService.cpp
Changes:
| ... | ... | @@ -3513,7 +3513,9 @@ void MediaManager::OnCameraMute(bool aMute) { |
| 3513 | 3513 | mCamerasMuted = aMute;
|
| 3514 | 3514 | // This is safe since we're on main-thread, and the windowlist can only
|
| 3515 | 3515 | // be added to from the main-thread
|
| 3516 | - for (const auto& window : mActiveWindows.Values()) {
|
|
| 3516 | + for (const auto& window :
|
|
| 3517 | + ToTArray<AutoTArray<RefPtr<GetUserMediaWindowListener>, 2>>(
|
|
| 3518 | + mActiveWindows.Values())) {
|
|
| 3517 | 3519 | window->MuteOrUnmuteCameras(aMute);
|
| 3518 | 3520 | }
|
| 3519 | 3521 | }
|
| ... | ... | @@ -3524,7 +3526,9 @@ void MediaManager::OnMicrophoneMute(bool aMute) { |
| 3524 | 3526 | mMicrophonesMuted = aMute;
|
| 3525 | 3527 | // This is safe since we're on main-thread, and the windowlist can only
|
| 3526 | 3528 | // be added to from the main-thread
|
| 3527 | - for (const auto& window : mActiveWindows.Values()) {
|
|
| 3529 | + for (const auto& window :
|
|
| 3530 | + ToTArray<AutoTArray<RefPtr<GetUserMediaWindowListener>, 2>>(
|
|
| 3531 | + mActiveWindows.Values())) {
|
|
| 3528 | 3532 | window->MuteOrUnmuteMicrophones(aMute);
|
| 3529 | 3533 | }
|
| 3530 | 3534 | }
|
| ... | ... | @@ -4698,7 +4702,7 @@ void GetUserMediaWindowListener::MuteOrUnmuteCameras(bool aMute) { |
| 4698 | 4702 | }
|
| 4699 | 4703 | mCamerasAreMuted = aMute;
|
| 4700 | 4704 | |
| 4701 | - for (auto& l : mActiveListeners) {
|
|
| 4705 | + for (auto& l : mActiveListeners.Clone()) {
|
|
| 4702 | 4706 | if (l->GetDevice()->Kind() == MediaDeviceKind::Videoinput) {
|
| 4703 | 4707 | l->MuteOrUnmuteCamera(aMute);
|
| 4704 | 4708 | }
|
| ... | ... | @@ -4713,7 +4717,7 @@ void GetUserMediaWindowListener::MuteOrUnmuteMicrophones(bool aMute) { |
| 4713 | 4717 | }
|
| 4714 | 4718 | mMicrophonesAreMuted = aMute;
|
| 4715 | 4719 | |
| 4716 | - for (auto& l : mActiveListeners) {
|
|
| 4720 | + for (auto& l : mActiveListeners.Clone()) {
|
|
| 4717 | 4721 | if (l->GetDevice()->Kind() == MediaDeviceKind::Audioinput) {
|
| 4718 | 4722 | l->MuteOrUnmuteMicrophone(aMute);
|
| 4719 | 4723 | }
|
| ... | ... | @@ -1663,11 +1663,17 @@ void nsContentSecurityUtils::LogMessageToConsole(nsIHttpChannel* aChannel, |
| 1663 | 1663 | }
|
| 1664 | 1664 | |
| 1665 | 1665 | /* static */
|
| 1666 | -long nsContentSecurityUtils::ClassifyDownload(
|
|
| 1667 | - nsIChannel* aChannel, const nsAutoCString& aMimeTypeGuess) {
|
|
| 1666 | +long nsContentSecurityUtils::ClassifyDownload(nsIChannel* aChannel) {
|
|
| 1668 | 1667 | MOZ_ASSERT(aChannel, "IsDownloadAllowed without channel?");
|
| 1669 | 1668 | |
| 1670 | 1669 | nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
| 1670 | + if ((loadInfo->GetTriggeringSandboxFlags() & SANDBOXED_ALLOW_DOWNLOADS) ||
|
|
| 1671 | + (loadInfo->GetSandboxFlags() & SANDBOXED_ALLOW_DOWNLOADS)) {
|
|
| 1672 | + if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
|
|
| 1673 | + LogMessageToConsole(httpChannel, "IframeSandboxBlockedDownload");
|
|
| 1674 | + }
|
|
| 1675 | + return nsITransfer::DOWNLOAD_FORBIDDEN;
|
|
| 1676 | + }
|
|
| 1671 | 1677 | |
| 1672 | 1678 | nsCOMPtr<nsIURI> contentLocation;
|
| 1673 | 1679 | aChannel->GetURI(getter_AddRefs(contentLocation));
|
| ... | ... | @@ -1698,27 +1704,11 @@ long nsContentSecurityUtils::ClassifyDownload( |
| 1698 | 1704 | |
| 1699 | 1705 | if (StaticPrefs::dom_block_download_insecure() &&
|
| 1700 | 1706 | decission != nsIContentPolicy::ACCEPT) {
|
| 1701 | - nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
|
| 1702 | - if (httpChannel) {
|
|
| 1707 | + if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
|
|
| 1703 | 1708 | LogMessageToConsole(httpChannel, "MixedContentBlockedDownload");
|
| 1704 | 1709 | }
|
| 1705 | 1710 | return nsITransfer::DOWNLOAD_POTENTIALLY_UNSAFE;
|
| 1706 | 1711 | }
|
| 1707 | 1712 | |
| 1708 | - if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
|
|
| 1709 | - return nsITransfer::DOWNLOAD_ACCEPTABLE;
|
|
| 1710 | - }
|
|
| 1711 | - |
|
| 1712 | - uint32_t triggeringFlags = loadInfo->GetTriggeringSandboxFlags();
|
|
| 1713 | - uint32_t currentflags = loadInfo->GetSandboxFlags();
|
|
| 1714 | - |
|
| 1715 | - if ((triggeringFlags & SANDBOXED_ALLOW_DOWNLOADS) ||
|
|
| 1716 | - (currentflags & SANDBOXED_ALLOW_DOWNLOADS)) {
|
|
| 1717 | - nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
|
| 1718 | - if (httpChannel) {
|
|
| 1719 | - LogMessageToConsole(httpChannel, "IframeSandboxBlockedDownload");
|
|
| 1720 | - }
|
|
| 1721 | - return nsITransfer::DOWNLOAD_FORBIDDEN;
|
|
| 1722 | - }
|
|
| 1723 | 1713 | return nsITransfer::DOWNLOAD_ACCEPTABLE;
|
| 1724 | 1714 | } |
| ... | ... | @@ -75,8 +75,7 @@ class nsContentSecurityUtils { |
| 75 | 75 | const mozilla::dom::Element& aElement);
|
| 76 | 76 | |
| 77 | 77 | // Helper function to Check if a Download is allowed;
|
| 78 | - static long ClassifyDownload(nsIChannel* aChannel,
|
|
| 79 | - const nsAutoCString& aMimeTypeGuess);
|
|
| 78 | + static long ClassifyDownload(nsIChannel* aChannel);
|
|
| 80 | 79 | |
| 81 | 80 | // Public only for testing
|
| 82 | 81 | static FilenameTypeAndDetails FilenameToFilenameType(
|
| ... | ... | @@ -66,6 +66,8 @@ class txXPathNode { |
| 66 | 66 | bool operator!=(const txXPathNode& aNode) const { return !(*this == aNode); }
|
| 67 | 67 | ~txXPathNode() { MOZ_COUNT_DTOR(txXPathNode); }
|
| 68 | 68 | |
| 69 | + mozilla::dom::Document* OwnerDoc() const { return mNode->OwnerDoc(); }
|
|
| 70 | + |
|
| 69 | 71 | private:
|
| 70 | 72 | friend class txXPathNativeNode;
|
| 71 | 73 | friend class txXPathNodeUtils;
|
| ... | ... | @@ -13,10 +13,13 @@ |
| 13 | 13 | |
| 14 | 14 | #include "mozilla/CheckedInt.h"
|
| 15 | 15 | #include "mozilla/UniquePtrExtensions.h"
|
| 16 | +#include "nsRFPService.h"
|
|
| 16 | 17 | |
| 17 | 18 | using mozilla::CheckedUint32;
|
| 18 | 19 | using mozilla::MakeUnique;
|
| 19 | 20 | using mozilla::MakeUniqueFallible;
|
| 21 | +using mozilla::nsRFPService;
|
|
| 22 | +using mozilla::RFPTarget;
|
|
| 20 | 23 | using mozilla::UniquePtr;
|
| 21 | 24 | |
| 22 | 25 | /*
|
| ... | ... | @@ -74,6 +77,10 @@ nsresult txNodeSorter::addSortElement(Expr* aSelectExpr, Expr* aLangExpr, |
| 74 | 77 | if (aLangExpr) {
|
| 75 | 78 | rv = aLangExpr->evaluateToString(aContext, lang);
|
| 76 | 79 | NS_ENSURE_SUCCESS(rv, rv);
|
| 80 | + } else if (aContext->getContextNode()
|
|
| 81 | + .OwnerDoc()
|
|
| 82 | + ->ShouldResistFingerprinting(RFPTarget::JSLocale)) {
|
|
| 83 | + CopyUTF8toUTF16(nsRFPService::GetSpoofedJSLocale(), lang);
|
|
| 77 | 84 | }
|
| 78 | 85 | |
| 79 | 86 | // Case-order
|
| ... | ... | @@ -16,6 +16,8 @@ |
| 16 | 16 | #include "nsIWebProgressListener.h"
|
| 17 | 17 | #include "nsIX509Cert.h"
|
| 18 | 18 | #include "nsPrintfCString.h"
|
| 19 | +#include "nsContentSecurityUtils.h"
|
|
| 20 | +#include "nsITransfer.h"
|
|
| 19 | 21 | |
| 20 | 22 | #include "nsNetUtil.h"
|
| 21 | 23 | |
| ... | ... | @@ -85,6 +87,16 @@ GeckoViewStreamListener::OnStartRequest(nsIRequest* aRequest) { |
| 85 | 87 | return NS_OK;
|
| 86 | 88 | }
|
| 87 | 89 | |
| 90 | + nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
|
| 91 | + if (channel) {
|
|
| 92 | + int32_t classification = nsContentSecurityUtils::ClassifyDownload(channel);
|
|
| 93 | + if (classification == nsITransfer::DOWNLOAD_FORBIDDEN) {
|
|
| 94 | + channel->Cancel(NS_ERROR_ABORT);
|
|
| 95 | + CompleteWithError(NS_ERROR_ABORT, channel);
|
|
| 96 | + return NS_OK;
|
|
| 97 | + }
|
|
| 98 | + }
|
|
| 99 | + |
|
| 88 | 100 | // We're expecting data later via OnDataAvailable, so create the stream now.
|
| 89 | 101 | InitializeStreamSupport(aRequest);
|
| 90 | 102 |
| ... | ... | @@ -14658,7 +14658,7 @@ |
| 14658 | 14658 | |
| 14659 | 14659 | - name: security.csp.truncate_blocked_uri_for_frame_navigations
|
| 14660 | 14660 | type: bool
|
| 14661 | - value: true
|
|
| 14661 | + value: false
|
|
| 14662 | 14662 | mirror: always
|
| 14663 | 14663 | |
| 14664 | 14664 | # Limit the number of CSP reports that are send in a specific timespan.
|
| 1 | +<!DOCTYPE html>
|
|
| 2 | +<html>
|
|
| 3 | +<head>
|
|
| 4 | + <!-- Make sure frame-src does path matching -->
|
|
| 5 | + <meta http-equiv="Content-Security-Policy" content="frame-src data: https://{{hosts[][www1]}}:{{ports[https][0]}}/content-security-policy/support/;">
|
|
| 6 | + <title>frame-src-blocked-path-matching</title>
|
|
| 7 | + <script src="/resources/testharness.js"></script>
|
|
| 8 | + <script src="/resources/testharnessreport.js"></script>
|
|
| 9 | +</head>
|
|
| 10 | +<body>
|
|
| 11 | + <script>
|
|
| 12 | + async_test(t => {
|
|
| 13 | + let frame = document.createElement("iframe");
|
|
| 14 | + frame.src = "https://{{hosts[][www1]}}:{{ports[https][0]}}/content-security-policy/support/postmessage-pass.html";
|
|
| 15 | + |
|
| 16 | + window.addEventListener('message', t.step_func(e => {
|
|
| 17 | + if (e.source === frame.contentWindow) {
|
|
| 18 | + assert_equals(e.data, "PASS");
|
|
| 19 | + t.done();
|
|
| 20 | + }
|
|
| 21 | + }));
|
|
| 22 | + |
|
| 23 | + document.body.append(frame);
|
|
| 24 | + }, "Cross-origin frame with allowed path loads");
|
|
| 25 | + |
|
| 26 | + async_test(t => {
|
|
| 27 | + let frame = document.createElement("iframe");
|
|
| 28 | + frame.src = "https://{{hosts[][www1]}}:{{ports[https][0]}}/content-security-policy/resource/";
|
|
| 29 | + |
|
| 30 | + window.addEventListener('securitypolicyviolation', t.step_func_done(e => {
|
|
| 31 | + assert_equals(e.blockedURI, "https://{{hosts[][www1]}}:{{ports[https][0]}}");
|
|
| 32 | + assert_equals(e.effectiveDirective, "frame-src");
|
|
| 33 | + }), { once: true });
|
|
| 34 | + |
|
| 35 | + document.body.append(frame);
|
|
| 36 | + }, "Cross-origin frame with other path is blocked");
|
|
| 37 | + |
|
| 38 | + async_test(t => {
|
|
| 39 | + let frame = document.createElement("iframe");
|
|
| 40 | + frame.src = "data:text/html,<h1>Hello World</h1>"
|
|
| 41 | + frame.onload = t.step_func(() => {
|
|
| 42 | + frame.src = "https://{{hosts[][www1]}}:{{ports[https][0]}}/content-security-policy/resource/";
|
|
| 43 | + |
|
| 44 | + window.addEventListener('securitypolicyviolation', t.step_func_done(e => {
|
|
| 45 | + assert_equals(e.blockedURI, "https://{{hosts[][www1]}}:{{ports[https][0]}}");
|
|
| 46 | + assert_equals(e.effectiveDirective, "frame-src");
|
|
| 47 | + }), { once: true });
|
|
| 48 | + });
|
|
| 49 | + document.body.append(frame);
|
|
| 50 | + }, "Cross-origin frame with other path is blocked even after replacing the already loaded URL");
|
|
| 51 | + </script>
|
|
| 52 | + </body>
|
|
| 53 | +</html> |
| ... | ... | @@ -1584,8 +1584,7 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) { |
| 1584 | 1584 | return NS_OK;
|
| 1585 | 1585 | }
|
| 1586 | 1586 | |
| 1587 | - mDownloadClassification =
|
|
| 1588 | - nsContentSecurityUtils::ClassifyDownload(aChannel, MIMEType);
|
|
| 1587 | + mDownloadClassification = nsContentSecurityUtils::ClassifyDownload(aChannel);
|
|
| 1589 | 1588 | |
| 1590 | 1589 | if (mDownloadClassification == nsITransfer::DOWNLOAD_FORBIDDEN) {
|
| 1591 | 1590 | // If the download is rated as forbidden,
|