Firefox · DOM
CVE-2026-16388
Sandbox Escape in DOM
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
dom/base/Document.cppdom/base/Document.hdom/fetch/FetchParent.cppdom/security/nsContentSecurityManager.cppmodules/libpref/init/StaticPrefList.yamlnetwerk/base/nsNetUtil.cpp
Patch
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
index 85d3493fff4..9c7acbe17a0 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -1616,6 +1616,13 @@ void Document::ReloadWithHttpsOnlyException() {
}
}
+void Document::ForceSkipDTDSecurityChecks() {
+ MOZ_ASSERT(StaticPrefs::dom_fetch_allow_force_allowed_dtd(),
+ "Loading DTDs that skip security checks requires "
+ "dom.fetch.allow_force_allowed_dtd to be enabled");
+ mSkipDTDSecurityChecks = true;
+}
+
// Given an nsresult that is assumed to be synthesized by PSM and describes a
// certificate or TLS error, attempts to convert it into a string
// representation of the underlying NSS error.
diff --git a/dom/base/Document.h b/dom/base/Document.h
index 42c105bb323..63c60ffe411 100644
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3066,7 +3066,7 @@ class Document : public nsINode,
void ForceEnableXULXBL() { mAllowXULXBL = eTriTrue; }
- void ForceSkipDTDSecurityChecks() { mSkipDTDSecurityChecks = true; }
+ void ForceSkipDTDSecurityChecks();
/**
* Returns the template content owner document that owns the content of
diff --git a/dom/fetch/FetchParent.cpp b/dom/fetch/FetchParent.cpp
index 512b1305ef4..8c046ee2964 100644
--- a/dom/fetch/FetchParent.cpp
+++ b/dom/fetch/FetchParent.cpp
@@ -15,6 +15,7 @@
#include "mozilla/dom/ProcessIsolation.h"
#include "mozilla/dom/ServiceWorkerDescriptor.h"
#include "mozilla/ipc/BackgroundParent.h"
+#include "nsIContentPolicy.h"
#include "nsThreadUtils.h"
using namespace mozilla::ipc;
@@ -123,6 +124,14 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
}
}
+ if (contentHandle &&
+ aArgs.request().contentPolicyType() ==
+ nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD &&
+ !StaticPrefs::dom_fetch_allow_force_allowed_dtd()) {
+ return IPC_FAIL(this,
+ "RecvFetchOp FORCE_ALLOWED_DTD not allowed from content");
+ }
+
mRequest = MakeSafeRefPtr<InternalRequest>(std::move(aArgs.request()));
mIsWorkerFetch = aArgs.isWorkerRequest();
mPrincipalInfo = std::move(aArgs.principalInfo());
diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp
index 4278fcf8761..d7af515730f 100644
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -281,7 +281,8 @@ static nsresult DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
// In practice, these DTDs are just used for localization, so applying the
// same principal check as Fluent.
if (aLoadInfo->InternalContentPolicyType() ==
- nsIContentPolicy::TYPE_INTERNAL_DTD) {
+ nsIContentPolicy::TYPE_INTERNAL_DTD &&
+ mozilla::StaticPrefs::dom_fetch_allow_force_allowed_dtd()) {
RefPtr<Document> doc;
aLoadInfo->GetLoadingDocument(getter_AddRefs(doc));
bool allowed = false;
@@ -295,7 +296,8 @@ static nsresult DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
// that need to access localization DTDs. We just allow through
// TYPE_INTERNAL_FORCE_ALLOWED_DTD no matter what the triggering principal is.
if (aLoadInfo->InternalContentPolicyType() ==
- nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD) {
+ nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD &&
+ mozilla::StaticPrefs::dom_fetch_allow_force_allowed_dtd()) {
return NS_OK;
}
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
index 7370b663313..e2fe1e8deb6 100644
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -3147,6 +3147,18 @@
value: true
mirror: always
+# Whether to allow the TYPE_INTERNAL_FORCE_ALLOWED_DTD content policy type on
+# fetch requests received from a content process. Only consumers such as
+# Thunderbird that still rely on DTD loads should enable this.
+- name: dom.fetch.allow_force_allowed_dtd
+ type: RelaxedAtomicBool
+#if defined(MOZ_THUNDERBIRD)
+ value: true
+#else
+ value: false
+#endif
+ mirror: always
+
- name: dom.fetchObserver.enabled
type: RelaxedAtomicBool
value: false
diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp
index 7636af5ef8a..9941a85331b 100644
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -17,6 +17,7 @@
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Monitor.h"
#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_extensions.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_privacy.h"
@@ -286,6 +287,13 @@ nsresult NS_NewChannelInternal(
// loadinfo attached.
NS_ENSURE_ARG_POINTER(outChannel);
+ if (aLoadInfo &&
+ aLoadInfo->InternalContentPolicyType() ==
+ nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD &&
+ !mozilla::StaticPrefs::dom_fetch_allow_force_allowed_dtd()) {
+ return NS_ERROR_CONTENT_BLOCKED;
+ }
+
nsCOMPtr<nsIIOService> grip;
nsresult rv = net_EnsureIOService(&aIoService, grip);
NS_ENSURE_SUCCESS(rv, rv);
@@ -452,6 +460,11 @@ nsresult NS_NewChannelInternal(
uint32_t aSandboxFlags /* = 0 */) {
NS_ENSURE_ARG_POINTER(outChannel);
+ if (aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD &&
+ !mozilla::StaticPrefs::dom_fetch_allow_force_allowed_dtd()) {
+ return NS_ERROR_CONTENT_BLOCKED;
+ }
+
nsCOMPtr<nsIIOService> grip;
nsresult rv = net_EnsureIOService(&aIoService, grip);
NS_ENSURE_SUCCESS(rv, rv);
Loading diff…
References
On This Page