TrustedTypeUtils.cpp (41326B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/TrustedTypeUtils.h" 8 9 #include "js/RootingAPI.h" 10 #include "mozilla/ErrorResult.h" 11 #include "mozilla/Maybe.h" 12 #include "mozilla/StaticPrefs_dom.h" 13 #include "mozilla/dom/CSPViolationData.h" 14 #include "mozilla/dom/Document.h" 15 #include "mozilla/dom/ElementBinding.h" 16 #include "mozilla/dom/HTMLScriptElementBinding.h" 17 #include "mozilla/dom/PolicyContainer.h" 18 #include "mozilla/dom/TrustedHTML.h" 19 #include "mozilla/dom/TrustedScript.h" 20 #include "mozilla/dom/TrustedScriptURL.h" 21 #include "mozilla/dom/TrustedTypePolicy.h" 22 #include "mozilla/dom/TrustedTypePolicyFactory.h" 23 #include "mozilla/dom/TrustedTypesConstants.h" 24 #include "mozilla/dom/UnionTypes.h" 25 #include "mozilla/dom/WindowOrWorkerGlobalScopeBinding.h" 26 #include "mozilla/dom/WorkerCommon.h" 27 #include "mozilla/dom/WorkerPrivate.h" 28 #include "mozilla/dom/WorkerRunnable.h" 29 #include "mozilla/dom/WorkerScope.h" 30 #include "mozilla/dom/nsCSPUtils.h" 31 #include "mozilla/extensions/WebExtensionPolicy.h" 32 #include "nsContentUtils.h" 33 #include "nsGlobalWindowInner.h" 34 #include "nsIContentSecurityPolicy.h" 35 #include "nsLiteralString.h" 36 #include "nsTArray.h" 37 #include "xpcpublic.h" 38 39 namespace mozilla::dom::TrustedTypeUtils { 40 41 nsString GetTrustedTypeName(TrustedType aTrustedType) { 42 switch (aTrustedType) { 43 case TrustedType::TrustedHTML: 44 return GetTrustedTypeName<TrustedHTML>(); 45 break; 46 case TrustedType::TrustedScript: 47 return GetTrustedTypeName<TrustedScript>(); 48 break; 49 case TrustedType::TrustedScriptURL: 50 return GetTrustedTypeName<TrustedScriptURL>(); 51 break; 52 } 53 MOZ_ASSERT_UNREACHABLE(); 54 return EmptyString(); 55 } 56 57 namespace SinkTypeMismatch { 58 59 static constexpr nsLiteralString kSampleSeparator = u"|"_ns; 60 static constexpr nsLiteralString kFunctionAnonymousPrefix = 61 u"function anonymous"_ns; 62 static constexpr nsLiteralString kAsyncFunctionAnonymousPrefix = 63 u"async function anonymous"_ns; 64 static constexpr nsLiteralString kFunctionStarAnonymousPrefix = 65 u"function* anonymous"_ns; 66 static constexpr nsLiteralString kAsyncFunctionStarAnonymousPrefix = 67 u"async function* anonymous"_ns; 68 } // namespace SinkTypeMismatch 69 70 // Implement reporting of sink type mismatch violations. 71 // https://w3c.github.io/trusted-types/dist/spec/#abstract-opdef-should-sink-type-mismatch-violation-be-blocked-by-content-security-policy 72 void ReportSinkTypeMismatchViolations(nsIContentSecurityPolicy* aCSP, 73 nsICSPEventListener* aCSPEventListener, 74 const nsCString& aFileName, 75 uint32_t aLine, uint32_t aColumn, 76 const nsAString& aSink, 77 const nsAString& aSinkGroup, 78 const nsAString& aSource) { 79 MOZ_ASSERT(aSinkGroup == kTrustedTypesOnlySinkGroup); 80 MOZ_ASSERT(aCSP); 81 MOZ_ASSERT(aCSP->GetRequireTrustedTypesForDirectiveState() != 82 RequireTrustedTypesForDirectiveState::NONE); 83 84 uint32_t numPolicies = 0; 85 aCSP->GetPolicyCount(&numPolicies); 86 87 // First determine the trimmed sample to be used for violation reports. 88 size_t startPos = 0; 89 if (aSink.Equals(u"Function"_ns)) { 90 auto sourceStartsWith = [&aSource, &startPos](const nsAString& aPrefix) { 91 MOZ_ASSERT(startPos == 0); 92 if (aSource.Length() >= aPrefix.Length() && 93 Substring(aSource, 0, aPrefix.Length()).Equals(aPrefix)) { 94 startPos = aPrefix.Length(); 95 return true; 96 } 97 return false; 98 }; 99 for (auto& prefix : {SinkTypeMismatch::kFunctionAnonymousPrefix, 100 SinkTypeMismatch::kAsyncFunctionAnonymousPrefix, 101 SinkTypeMismatch::kFunctionStarAnonymousPrefix, 102 SinkTypeMismatch::kAsyncFunctionStarAnonymousPrefix}) { 103 if (sourceStartsWith(prefix)) { 104 break; 105 } 106 } 107 } 108 109 const nsDependentSubstring trimmedSample = 110 CSPViolationData::MaybeTruncateSample(Substring(aSource, startPos)); 111 const nsString sample = 112 aSink + SinkTypeMismatch::kSampleSeparator + trimmedSample; 113 114 for (uint32_t i = 0; i < numPolicies; ++i) { 115 const auto* policy = aCSP->GetPolicy(i); 116 117 if (!policy->AreTrustedTypesForSinkGroupRequired(aSinkGroup)) { 118 continue; 119 } 120 121 CSPViolationData cspViolationData{ 122 i, 123 CSPViolationData::Resource{ 124 CSPViolationData::BlockedContentSource::TrustedTypesSink}, 125 nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE, 126 aFileName, 127 aLine, 128 aColumn, 129 /* aElement */ nullptr, 130 sample}; 131 132 aCSP->LogTrustedTypesViolationDetailsUnchecked( 133 std::move(cspViolationData), 134 NS_LITERAL_STRING_FROM_CSTRING( 135 REQUIRE_TRUSTED_TYPES_FOR_SCRIPT_OBSERVER_TOPIC), 136 aCSPEventListener); 137 } 138 } 139 140 class LogSinkTypeMismatchViolationsRunnable final 141 : public WorkerMainThreadRunnable { 142 public: 143 LogSinkTypeMismatchViolationsRunnable(WorkerPrivate* aWorker, 144 const nsCString& aFileName, 145 uint32_t aLine, uint32_t aColumn, 146 const nsAString& aSink, 147 const nsAString& aSinkGroup, 148 const nsAString& aSource) 149 : WorkerMainThreadRunnable( 150 aWorker, 151 "RuntimeService :: LogSinkTypeMismatchViolationsRunnable"_ns), 152 mFileName(aFileName), 153 mLine(aLine), 154 mColumn(aColumn), 155 mSink(aSink), 156 mSinkGroup(aSinkGroup), 157 mSource(aSource) { 158 MOZ_ASSERT(aWorker); 159 } 160 161 virtual bool MainThreadRun() override { 162 AssertIsOnMainThread(); 163 MOZ_ASSERT(mWorkerRef); 164 if (nsIContentSecurityPolicy* csp = mWorkerRef->Private()->GetCsp()) { 165 ReportSinkTypeMismatchViolations( 166 csp, mWorkerRef->Private()->CSPEventListener(), mFileName, mLine, 167 mColumn, mSink, mSinkGroup, mSource); 168 } 169 return true; 170 } 171 172 private: 173 ~LogSinkTypeMismatchViolationsRunnable() = default; 174 const nsCString& mFileName; 175 uint32_t mLine; 176 uint32_t mColumn; 177 const nsString mSink; 178 const nsString mSinkGroup; 179 const nsString mSource; 180 }; 181 182 constexpr size_t kNumArgumentsForDetermineTrustedTypePolicyValue = 2; 183 184 template <typename ExpectedType> 185 void ProcessValueWithADefaultPolicy(nsIGlobalObject& aGlobalObject, 186 const nsAString& aInput, 187 const nsAString& aSink, 188 ExpectedType** aResult, 189 ErrorResult& aError) { 190 *aResult = nullptr; 191 192 TrustedTypePolicyFactory* trustedTypePolicyFactory; 193 if (nsPIDOMWindowInner* piDOMWindowInner = aGlobalObject.GetAsInnerWindow()) { 194 nsGlobalWindowInner* globalWindowInner = 195 nsGlobalWindowInner::Cast(piDOMWindowInner); 196 trustedTypePolicyFactory = globalWindowInner->TrustedTypes(); 197 } else { 198 MOZ_ASSERT(IsWorkerGlobal(aGlobalObject.GetGlobalJSObject())); 199 MOZ_ASSERT(!NS_IsMainThread()); 200 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 201 WorkerGlobalScope* scope = workerPrivate->GlobalScope(); 202 trustedTypePolicyFactory = scope->TrustedTypes(); 203 } 204 205 const RefPtr<TrustedTypePolicy> defaultPolicy = 206 trustedTypePolicyFactory->GetDefaultPolicy(); 207 if (!defaultPolicy) { 208 return; 209 } 210 211 JSContext* cx = nsContentUtils::GetCurrentJSContext(); 212 if (!cx) { 213 return; 214 } 215 216 JS::Rooted<JS::Value> trustedTypeName{cx}; 217 using ExpectedTypeArg = 218 std::remove_const_t<std::remove_reference_t<decltype(**aResult)>>; 219 if (!xpc::NonVoidStringToJsval(cx, GetTrustedTypeName<ExpectedTypeArg>(), 220 &trustedTypeName)) { 221 aError.StealExceptionFromJSContext(cx); 222 return; 223 } 224 225 JS::Rooted<JS::Value> sink{cx}; 226 if (!xpc::NonVoidStringToJsval(cx, aSink, &sink)) { 227 aError.StealExceptionFromJSContext(cx); 228 return; 229 } 230 231 AutoTArray<JS::Value, kNumArgumentsForDetermineTrustedTypePolicyValue> 232 arguments = {trustedTypeName, sink}; 233 234 nsString policyValue; 235 if constexpr (std::is_same_v<ExpectedTypeArg, TrustedHTML>) { 236 RefPtr<CreateHTMLCallback> callbackObject = 237 defaultPolicy->GetOptions().mCreateHTMLCallback; 238 defaultPolicy->DetermineTrustedPolicyValue( 239 callbackObject, aInput, arguments, 240 /* aThrowIfMissing */ false, aError, policyValue); 241 } else if constexpr (std::is_same_v<ExpectedTypeArg, TrustedScript>) { 242 RefPtr<CreateScriptCallback> callbackObject = 243 defaultPolicy->GetOptions().mCreateScriptCallback; 244 defaultPolicy->DetermineTrustedPolicyValue( 245 callbackObject, aInput, arguments, 246 /* aThrowIfMissing */ false, aError, policyValue); 247 } else { 248 MOZ_ASSERT((std::is_same_v<ExpectedTypeArg, TrustedScriptURL>)); 249 RefPtr<CreateScriptURLCallback> callbackObject = 250 defaultPolicy->GetOptions().mCreateScriptURLCallback; 251 defaultPolicy->DetermineTrustedPolicyValue( 252 callbackObject, aInput, arguments, 253 /* aThrowIfMissing */ false, aError, policyValue); 254 } 255 256 if (aError.Failed()) { 257 return; 258 } 259 260 if (policyValue.IsVoid()) { 261 return; 262 } 263 264 MakeRefPtr<ExpectedType>(policyValue).forget(aResult); 265 } 266 267 // GetTrustedTypesCompliantString() and GetTrustedTypesCompliantAttributeValue() 268 // deal with "trusted type or string" union types. These union types provide 269 // inline methods Is*() and GetAs*() to test and access the actual value. The 270 // following inline functions provide similar logic to conveniently deal with 271 // the different union types. 272 template <typename TrustedTypeOrStringArg> 273 static inline bool IsString(const TrustedTypeOrStringArg& aInput) { 274 if constexpr ( 275 std::is_same_v<TrustedTypeOrStringArg, TrustedHTMLOrString> || 276 std::is_same_v<TrustedTypeOrStringArg, TrustedScriptOrString> || 277 std::is_same_v<TrustedTypeOrStringArg, FunctionOrTrustedScriptOrString> || 278 std::is_same_v<TrustedTypeOrStringArg, TrustedScriptURLOrString> || 279 std::is_same_v<TrustedTypeOrStringArg, OwningTrustedScriptURLOrString> || 280 std::is_same_v<TrustedTypeOrStringArg, 281 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString>) { 282 return aInput.IsString(); 283 } 284 if constexpr (std::is_same_v<TrustedTypeOrStringArg, 285 TrustedHTMLOrNullIsEmptyString> || 286 std::is_same_v<TrustedTypeOrStringArg, 287 TrustedScriptOrNullIsEmptyString>) { 288 return aInput.IsNullIsEmptyString(); 289 } 290 if constexpr (std::is_same_v<TrustedTypeOrStringArg, 291 TrustedScriptURLOrUSVString>) { 292 return aInput.IsUSVString(); 293 } 294 if constexpr (std::is_same_v<TrustedTypeOrStringArg, const nsAString*>) { 295 (void)aInput; 296 return true; 297 } 298 MOZ_ASSERT_UNREACHABLE(); 299 return false; 300 } 301 302 template <typename TrustedTypeOrStringArg> 303 static inline const nsAString* GetAsString( 304 const TrustedTypeOrStringArg& aInput) { 305 if constexpr ( 306 std::is_same_v<TrustedTypeOrStringArg, TrustedHTMLOrString> || 307 std::is_same_v<TrustedTypeOrStringArg, TrustedScriptOrString> || 308 std::is_same_v<TrustedTypeOrStringArg, FunctionOrTrustedScriptOrString> || 309 std::is_same_v<TrustedTypeOrStringArg, TrustedScriptURLOrString> || 310 std::is_same_v<TrustedTypeOrStringArg, OwningTrustedScriptURLOrString> || 311 std::is_same_v<TrustedTypeOrStringArg, 312 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString>) { 313 return &aInput.GetAsString(); 314 } 315 if constexpr (std::is_same_v<TrustedTypeOrStringArg, 316 TrustedHTMLOrNullIsEmptyString> || 317 std::is_same_v<TrustedTypeOrStringArg, 318 TrustedScriptOrNullIsEmptyString>) { 319 return &aInput.GetAsNullIsEmptyString(); 320 } 321 if constexpr (std::is_same_v<TrustedTypeOrStringArg, 322 TrustedScriptURLOrUSVString>) { 323 return &aInput.GetAsUSVString(); 324 } 325 if constexpr (std::is_same_v<TrustedTypeOrStringArg, const nsAString*>) { 326 return aInput; 327 } 328 MOZ_ASSERT_UNREACHABLE(); 329 return &EmptyString(); 330 } 331 332 template <typename TrustedTypeOrStringArg> 333 static inline bool IsTrustedType(const TrustedTypeOrStringArg& aInput) { 334 if constexpr (std::is_same_v<TrustedTypeOrStringArg, TrustedHTMLOrString> || 335 std::is_same_v<TrustedTypeOrStringArg, 336 TrustedHTMLOrNullIsEmptyString>) { 337 return aInput.IsTrustedHTML(); 338 } 339 if constexpr (std::is_same_v<TrustedTypeOrStringArg, TrustedScriptOrString> || 340 std::is_same_v<TrustedTypeOrStringArg, 341 FunctionOrTrustedScriptOrString> || 342 std::is_same_v<TrustedTypeOrStringArg, 343 TrustedScriptOrNullIsEmptyString>) { 344 return aInput.IsTrustedScript(); 345 } 346 if constexpr (std::is_same_v<TrustedTypeOrStringArg, 347 TrustedScriptURLOrString> || 348 std::is_same_v<TrustedTypeOrStringArg, 349 TrustedScriptURLOrUSVString> || 350 std::is_same_v<TrustedTypeOrStringArg, 351 OwningTrustedScriptURLOrString>) { 352 return aInput.IsTrustedScriptURL(); 353 } 354 if constexpr (std::is_same_v<TrustedTypeOrStringArg, const nsAString*>) { 355 (void)aInput; 356 return false; 357 } 358 MOZ_ASSERT_UNREACHABLE(); 359 return false; 360 }; 361 362 template <typename TrustedTypeOrStringArg> 363 static inline const nsAString* GetAsTrustedType( 364 const TrustedTypeOrStringArg& aInput) { 365 if constexpr (std::is_same_v<TrustedTypeOrStringArg, TrustedHTMLOrString> || 366 std::is_same_v<TrustedTypeOrStringArg, 367 TrustedHTMLOrNullIsEmptyString>) { 368 return &aInput.GetAsTrustedHTML().mData; 369 } 370 if constexpr (std::is_same_v<TrustedTypeOrStringArg, TrustedScriptOrString> || 371 std::is_same_v<TrustedTypeOrStringArg, 372 FunctionOrTrustedScriptOrString> || 373 std::is_same_v<TrustedTypeOrStringArg, 374 TrustedScriptOrNullIsEmptyString>) { 375 return &aInput.GetAsTrustedScript().mData; 376 } 377 if constexpr (std::is_same_v<TrustedTypeOrStringArg, 378 TrustedScriptURLOrString> || 379 std::is_same_v<TrustedTypeOrStringArg, 380 TrustedScriptURLOrUSVString>) { 381 return &aInput.GetAsTrustedScriptURL().mData; 382 } 383 if constexpr (std::is_same_v<TrustedTypeOrStringArg, 384 OwningTrustedScriptURLOrString>) { 385 return &aInput.GetAsTrustedScriptURL()->mData; 386 } 387 if constexpr (std::is_same_v< 388 TrustedTypeOrStringArg, 389 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString>) { 390 if (aInput.IsTrustedHTML()) { 391 return &aInput.GetAsTrustedHTML().mData; 392 } 393 if (aInput.IsTrustedScript()) { 394 return &aInput.GetAsTrustedScript().mData; 395 } 396 MOZ_ASSERT(aInput.IsTrustedScriptURL()); 397 return &aInput.GetAsTrustedScriptURL().mData; 398 } 399 (void)aInput; 400 MOZ_ASSERT_UNREACHABLE(); 401 return &EmptyString(); 402 }; 403 404 template <typename TrustedTypeOrStringArg> 405 static inline const nsAString* GetContent( 406 const TrustedTypeOrStringArg& aInput) { 407 return IsString(aInput) ? GetAsString(aInput) : GetAsTrustedType(aInput); 408 } 409 410 template <typename NodeOrGlobalObject> 411 inline bool PrepareExecutionOfTrustedTypesDefaultPolicy( 412 NodeOrGlobalObject& aNodeOrGlobalObject, nsIPrincipal* aPrincipalOrNull, 413 nsIGlobalObject*& aGlobalObject, nsPIDOMWindowInner*& aPIDOMWindowInner, 414 RequireTrustedTypesForDirectiveState* aRequireTrustedTypesForDirectiveState, 415 ErrorResult& aError) { 416 if (!StaticPrefs::dom_security_trusted_types_enabled()) { 417 // A trusted type might've been created before the pref was set to `false`, 418 // so we cannot assume aInput.IsString(). 419 return false; 420 } 421 422 // Exempt web extension content scripts from trusted types policies defined by 423 // the page in which they are running. 424 if (auto* principal = BasePrincipal::Cast(aPrincipalOrNull)) { 425 if (principal->ContentScriptAddonPolicyCore()) { 426 return false; 427 } 428 } 429 430 // Below, we use fast paths when there are no require-trusted-types-for 431 // directives. Note that the global object's CSP may differ from the 432 // owner-document's one. E.g. when aDocument was created by 433 // `document.implementation.createHTMLDocument` and it's not connected to a 434 // browsing context. 435 using NodeOrGlobalObjectArg = std::remove_const_t< 436 std::remove_reference_t<decltype(aNodeOrGlobalObject)>>; 437 nsIGlobalObject* globalObject = nullptr; 438 nsPIDOMWindowInner* piDOMWindowInner = nullptr; 439 if constexpr (std::is_same_v<NodeOrGlobalObjectArg, nsINode>) { 440 if (aNodeOrGlobalObject.HasBeenInUAWidget()) { 441 return false; 442 } 443 Document* ownerDoc = aNodeOrGlobalObject.OwnerDoc(); 444 const bool ownerDocLoadedAsData = ownerDoc->IsLoadedAsData(); 445 if (!ownerDoc->HasPolicyWithRequireTrustedTypesForDirective() && 446 !ownerDocLoadedAsData) { 447 return false; 448 } 449 globalObject = ownerDoc->GetScopeObject(); 450 if (!globalObject) { 451 aError.ThrowTypeError("No global object"); 452 return false; 453 } 454 piDOMWindowInner = globalObject->GetAsInnerWindow(); 455 if (!piDOMWindowInner) { 456 // Global object is not a Window. This can happen when DOM APIs are used 457 // in some contexts where Trusted Types don't apply (e.g. bug 1942517), 458 // so just return the input string. 459 return false; 460 } 461 if (ownerDocLoadedAsData && piDOMWindowInner->GetExtantDoc() && 462 !piDOMWindowInner->GetExtantDoc() 463 ->HasPolicyWithRequireTrustedTypesForDirective()) { 464 return false; 465 } 466 } else if constexpr (std::is_same_v<NodeOrGlobalObjectArg, nsIGlobalObject>) { 467 piDOMWindowInner = aNodeOrGlobalObject.GetAsInnerWindow(); 468 if (piDOMWindowInner) { 469 const Document* extantDoc = piDOMWindowInner->GetExtantDoc(); 470 if (extantDoc && 471 !extantDoc->HasPolicyWithRequireTrustedTypesForDirective()) { 472 return false; 473 } 474 } 475 globalObject = &aNodeOrGlobalObject; 476 } 477 MOZ_ASSERT(globalObject); 478 479 // Now retrieve the CSP from the global object. 480 // Because there is only one sink group, its associated 481 // RequireTrustedTypesForDirectiveState actually provides the results of 482 // "Does sink type require trusted types?" 483 // (https://w3c.github.io/trusted-types/dist/spec/#abstract-opdef-does-sink-type-require-trusted-types) 484 // and "Should sink type mismatch violation be blocked by CSP?" 485 // (https://w3c.github.io/trusted-types/dist/spec/#should-block-sink-type-mismatch). 486 RequireTrustedTypesForDirectiveState requireTrustedTypesForDirectiveState = 487 RequireTrustedTypesForDirectiveState::NONE; 488 if (piDOMWindowInner) { 489 RefPtr<nsIContentSecurityPolicy> csp = 490 PolicyContainer::GetCSP(piDOMWindowInner->GetPolicyContainer()); 491 if (!csp) { 492 return false; 493 } 494 requireTrustedTypesForDirectiveState = 495 csp->GetRequireTrustedTypesForDirectiveState(); 496 // The following assert is guaranteed by above calls to 497 // HasPolicyWithRequireTrustedTypesForDirective. 498 MOZ_ASSERT(requireTrustedTypesForDirectiveState != 499 RequireTrustedTypesForDirectiveState::NONE); 500 } else if (IsWorkerGlobal(globalObject->GetGlobalJSObject())) { 501 MOZ_ASSERT(!NS_IsMainThread()); 502 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 503 const mozilla::ipc::CSPInfo& cspInfo = workerPrivate->GetCSPInfo(); 504 requireTrustedTypesForDirectiveState = 505 cspInfo.requireTrustedTypesForDirectiveState(); 506 if (requireTrustedTypesForDirectiveState == 507 RequireTrustedTypesForDirectiveState::NONE) { 508 return false; 509 } 510 } else { 511 // Global object is neither Window nor WorkerGlobalScope. This can happen 512 // when DOM APIs are used in some contexts where Trusted Types don't apply 513 // (e.g. bugs 1942517 and 1936219), so just return the input string. 514 return false; 515 } 516 517 aGlobalObject = globalObject; 518 aPIDOMWindowInner = piDOMWindowInner; 519 *aRequireTrustedTypesForDirectiveState = requireTrustedTypesForDirectiveState; 520 return true; 521 } 522 523 bool CanSkipTrustedTypesEnforcement(const nsINode& aNode) { 524 nsIGlobalObject* dummyGlobal = nullptr; 525 nsPIDOMWindowInner* dummyWindow = nullptr; 526 RequireTrustedTypesForDirectiveState dummyState; 527 // Specify a null principal, as ScriptElement::UpdateTrustWorthiness() has 528 // already checked whether this is running in an isolated web extension 529 // context. 530 return !PrepareExecutionOfTrustedTypesDefaultPolicy( 531 aNode, nullptr /* aPrincipalOrNull */, dummyGlobal, dummyWindow, 532 &dummyState, IgnoreErrors()); 533 } 534 535 template <typename ExpectedType, typename TrustedTypeOrString, 536 typename NodeOrGlobalObject> 537 MOZ_CAN_RUN_SCRIPT inline const nsAString* GetTrustedTypesCompliantString( 538 const TrustedTypeOrString& aInput, const nsAString& aSink, 539 const nsAString& aSinkGroup, NodeOrGlobalObject& aNodeOrGlobalObject, 540 nsIPrincipal* aPrincipalOrNull, Maybe<nsAutoString>& aResultHolder, 541 ErrorResult& aError) { 542 MOZ_ASSERT(aSinkGroup == kTrustedTypesOnlySinkGroup); 543 544 if (IsTrustedType(aInput)) { 545 return GetAsTrustedType(aInput); 546 } 547 548 nsIGlobalObject* globalObject = nullptr; 549 nsPIDOMWindowInner* piDOMWindowInner = nullptr; 550 RequireTrustedTypesForDirectiveState requireTrustedTypesForDirectiveState; 551 if (!PrepareExecutionOfTrustedTypesDefaultPolicy( 552 aNodeOrGlobalObject, aPrincipalOrNull, globalObject, piDOMWindowInner, 553 &requireTrustedTypesForDirectiveState, aError)) { 554 return aError.Failed() ? nullptr : GetAsString(aInput); 555 } 556 557 RefPtr<ExpectedType> convertedInput; 558 nsCOMPtr<nsIGlobalObject> pinnedGlobalObject = globalObject; 559 ProcessValueWithADefaultPolicy<ExpectedType>( 560 *pinnedGlobalObject, *GetAsString(aInput), aSink, 561 getter_AddRefs(convertedInput), aError); 562 563 if (aError.Failed()) { 564 return nullptr; 565 } 566 567 if (!convertedInput) { 568 auto location = JSCallingLocation::Get(); 569 if (piDOMWindowInner) { 570 RefPtr<nsIContentSecurityPolicy> csp = 571 PolicyContainer::GetCSP(piDOMWindowInner->GetPolicyContainer()); 572 ReportSinkTypeMismatchViolations(csp, nullptr /* aCSPEventListener */, 573 location.FileName(), location.mLine, 574 location.mColumn, aSink, aSinkGroup, 575 *GetAsString(aInput)); 576 } else { 577 MOZ_ASSERT(IsWorkerGlobal(globalObject->GetGlobalJSObject())); 578 MOZ_ASSERT(!NS_IsMainThread()); 579 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 580 RefPtr<LogSinkTypeMismatchViolationsRunnable> runnable = 581 new LogSinkTypeMismatchViolationsRunnable( 582 workerPrivate, location.FileName(), location.mLine, 583 location.mColumn, aSink, aSinkGroup, *GetAsString(aInput)); 584 ErrorResult rv; 585 runnable->Dispatch(workerPrivate, Killing, rv); 586 if (NS_WARN_IF(rv.Failed())) { 587 rv.SuppressException(); 588 } 589 } 590 if (requireTrustedTypesForDirectiveState == 591 RequireTrustedTypesForDirectiveState::REPORT_ONLY) { 592 return GetAsString(aInput); 593 } 594 595 aError.ThrowTypeError("Sink type mismatch violation blocked by CSP"_ns); 596 return nullptr; 597 } 598 599 aResultHolder = Some(convertedInput->mData); 600 return aResultHolder.ptr(); 601 } 602 603 #define IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING( \ 604 _trustedTypeOrString, _expectedType, _nodeOrGlobalObject) \ 605 const nsAString* GetTrustedTypesCompliantString( \ 606 const _trustedTypeOrString& aInput, const nsAString& aSink, \ 607 const nsAString& aSinkGroup, _nodeOrGlobalObject& aNodeOrGlobal, \ 608 nsIPrincipal* aPrincipalOrNull, Maybe<nsAutoString>& aResultHolder, \ 609 ErrorResult& aError) { \ 610 return GetTrustedTypesCompliantString<_expectedType>( \ 611 aInput, aSink, aSinkGroup, aNodeOrGlobal, aPrincipalOrNull, \ 612 aResultHolder, aError); \ 613 } 614 615 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(TrustedHTMLOrString, TrustedHTML, 616 const nsINode); 617 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(TrustedHTMLOrNullIsEmptyString, 618 TrustedHTML, const nsINode); 619 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(TrustedHTMLOrString, TrustedHTML, 620 nsIGlobalObject); 621 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(TrustedScriptOrString, TrustedScript, 622 const nsINode); 623 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(TrustedScriptOrNullIsEmptyString, 624 TrustedScript, const nsINode); 625 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(FunctionOrTrustedScriptOrString, 626 TrustedScript, nsIGlobalObject); 627 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(TrustedScriptURLOrString, 628 TrustedScriptURL, const nsINode); 629 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(TrustedScriptURLOrUSVString, 630 TrustedScriptURL, nsIGlobalObject); 631 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(TrustedScriptURLOrUSVString, 632 TrustedScriptURL, const nsINode); 633 IMPL_GET_TRUSTED_TYPES_COMPLIANT_STRING(OwningTrustedScriptURLOrString, 634 TrustedScriptURL, nsIGlobalObject); 635 636 MOZ_CAN_RUN_SCRIPT const nsAString* 637 GetTrustedTypesCompliantStringForTrustedHTML(const nsAString& aInput, 638 const nsAString& aSink, 639 const nsAString& aSinkGroup, 640 const nsINode& aNode, 641 nsIPrincipal* aPrincipalOrNull, 642 Maybe<nsAutoString>& aResultHolder, 643 ErrorResult& aError) { 644 return GetTrustedTypesCompliantString<TrustedHTML>(&aInput, aSink, aSinkGroup, 645 aNode, aPrincipalOrNull, 646 aResultHolder, aError); 647 } 648 649 MOZ_CAN_RUN_SCRIPT const nsAString* 650 GetTrustedTypesCompliantStringForTrustedScript( 651 const nsAString& aInput, const nsAString& aSink, 652 const nsAString& aSinkGroup, nsIGlobalObject& aGlobalObject, 653 nsIPrincipal* aPrincipalOrNull, Maybe<nsAutoString>& aResultHolder, 654 ErrorResult& aError) { 655 return GetTrustedTypesCompliantString<TrustedScript>( 656 &aInput, aSink, aSinkGroup, aGlobalObject, aPrincipalOrNull, 657 aResultHolder, aError); 658 } 659 660 MOZ_CAN_RUN_SCRIPT const nsAString* 661 GetTrustedTypesCompliantStringForTrustedScript( 662 const nsAString& aInput, const nsAString& aSink, 663 const nsAString& aSinkGroup, const nsINode& aNode, 664 Maybe<nsAutoString>& aResultHolder, ErrorResult& aError) { 665 // Specify a null principal, as ScriptElement::UpdateTrustWorthiness() has 666 // already checked whether this is running in an isolated web extension 667 // context. 668 return GetTrustedTypesCompliantString<TrustedScript>( 669 &aInput, aSink, aSinkGroup, aNode, nullptr /* aPrincipalOrNull */, 670 aResultHolder, aError); 671 } 672 673 bool GetTrustedTypeDataForAttribute(const nsAtom* aElementName, 674 int32_t aElementNamespaceID, 675 nsAtom* aAttributeName, 676 int32_t aAttributeNamespaceID, 677 TrustedType& aTrustedType, 678 nsAString& aSink) { 679 if (aElementNamespaceID != kNameSpaceID_XHTML && 680 aElementNamespaceID != kNameSpaceID_SVG && 681 aElementNamespaceID != kNameSpaceID_MathML) { 682 return false; 683 } 684 685 // The spec is not really clear about which "event handler content attributes" 686 // we should consider, so we just include everything but XUL's specific ones. 687 // See https://github.com/w3c/trusted-types/issues/520. 688 if (aAttributeNamespaceID == kNameSpaceID_None && 689 nsContentUtils::IsEventAttributeName( 690 aAttributeName, EventNameType_All & ~EventNameType_XUL)) { 691 aTrustedType = TrustedType::TrustedScript; 692 aSink.AssignLiteral(u"Element "); 693 aSink.Append(*aAttributeName); 694 return true; 695 } 696 if (aElementNamespaceID == kNameSpaceID_XHTML) { 697 if (aElementName == nsGkAtoms::iframe) { 698 // HTMLIFrameElement 699 if (aAttributeNamespaceID == kNameSpaceID_None && 700 aAttributeName == nsGkAtoms::srcdoc) { 701 aTrustedType = TrustedType::TrustedHTML; 702 aSink.AssignLiteral(u"HTMLIFrameElement srcdoc"); 703 return true; 704 } 705 } else if (aElementName == nsGkAtoms::script) { 706 // HTMLScriptElement 707 if (aAttributeNamespaceID == kNameSpaceID_None && 708 aAttributeName == nsGkAtoms::src) { 709 aTrustedType = TrustedType::TrustedScriptURL; 710 aSink.AssignLiteral(u"HTMLScriptElement src"); 711 return true; 712 } 713 } 714 } else if (aElementNamespaceID == kNameSpaceID_SVG) { 715 if (aElementName == nsGkAtoms::script) { 716 // SVGScriptElement 717 if ((aAttributeNamespaceID == kNameSpaceID_None || 718 aAttributeNamespaceID == kNameSpaceID_XLink) && 719 aAttributeName == nsGkAtoms::href) { 720 aTrustedType = TrustedType::TrustedScriptURL; 721 aSink.AssignLiteral(u"SVGScriptElement href"); 722 return true; 723 } 724 } 725 } 726 727 return false; 728 } 729 730 template <typename TrustedTypeOrStringArg> 731 MOZ_CAN_RUN_SCRIPT const nsAString* GetTrustedTypesCompliantAttributeValue( 732 const nsINode& aElement, nsAtom* aAttributeName, 733 int32_t aAttributeNamespaceID, const TrustedTypeOrStringArg& aNewValue, 734 nsIPrincipal* aPrincipalOrNull, Maybe<nsAutoString>& aResultHolder, 735 ErrorResult& aError) { 736 if (!StaticPrefs::dom_security_trusted_types_enabled()) { 737 // A trusted type might've been created before the pref was set to `false`, 738 // so we cannot assume aNewValue.IsString(). 739 return GetContent(aNewValue); 740 } 741 742 // In the common situation of non-data document without any 743 // require-trusted-types-for directive, we just return immediately. 744 const NodeInfo* nodeInfo = aElement.NodeInfo(); 745 Document* ownerDoc = nodeInfo->GetDocument(); 746 const bool ownerDocLoadedAsData = ownerDoc->IsLoadedAsData(); 747 if (!ownerDoc->HasPolicyWithRequireTrustedTypesForDirective() && 748 !ownerDocLoadedAsData) { 749 return GetContent(aNewValue); 750 } 751 752 TrustedType expectedType; 753 nsAutoString sink; 754 if (!GetTrustedTypeDataForAttribute( 755 nodeInfo->NameAtom(), nodeInfo->NamespaceID(), aAttributeName, 756 aAttributeNamespaceID, expectedType, sink)) { 757 return GetContent(aNewValue); 758 } 759 760 if constexpr (std::is_same_v< 761 TrustedTypeOrStringArg, 762 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString>) { 763 if ((expectedType == TrustedType::TrustedHTML && 764 aNewValue.IsTrustedHTML()) || 765 (expectedType == TrustedType::TrustedScript && 766 aNewValue.IsTrustedScript()) || 767 (expectedType == TrustedType::TrustedScriptURL && 768 aNewValue.IsTrustedScriptURL())) { 769 return GetAsTrustedType(aNewValue); 770 } 771 } else { 772 MOZ_ASSERT((std::is_same_v<TrustedTypeOrStringArg, const nsAString*>)); 773 } 774 775 const nsAString* input = GetContent(aNewValue); 776 switch (expectedType) { 777 case TrustedType::TrustedHTML: 778 return GetTrustedTypesCompliantString<TrustedHTML>( 779 input, sink, kTrustedTypesOnlySinkGroup, aElement, aPrincipalOrNull, 780 aResultHolder, aError); 781 case TrustedType::TrustedScript: 782 return GetTrustedTypesCompliantString<TrustedScript>( 783 input, sink, kTrustedTypesOnlySinkGroup, aElement, aPrincipalOrNull, 784 aResultHolder, aError); 785 case TrustedType::TrustedScriptURL: 786 return GetTrustedTypesCompliantString<TrustedScriptURL>( 787 input, sink, kTrustedTypesOnlySinkGroup, aElement, aPrincipalOrNull, 788 aResultHolder, aError); 789 } 790 MOZ_ASSERT_UNREACHABLE(); 791 return nullptr; 792 } 793 794 MOZ_CAN_RUN_SCRIPT const nsAString* GetTrustedTypesCompliantAttributeValue( 795 const nsINode& aElement, nsAtom* aAttributeName, 796 int32_t aAttributeNamespaceID, 797 const TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString& aNewValue, 798 nsIPrincipal* aPrincipalOrNull, Maybe<nsAutoString>& aResultHolder, 799 ErrorResult& aError) { 800 return GetTrustedTypesCompliantAttributeValue< 801 TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString>( 802 aElement, aAttributeName, aAttributeNamespaceID, aNewValue, 803 aPrincipalOrNull, aResultHolder, aError); 804 } 805 806 MOZ_CAN_RUN_SCRIPT const nsAString* GetTrustedTypesCompliantAttributeValue( 807 const nsINode& aElement, nsAtom* aAttributeName, 808 int32_t aAttributeNamespaceID, const nsAString& aNewValue, 809 nsIPrincipal* aPrincipalOrNull, Maybe<nsAutoString>& aResultHolder, 810 ErrorResult& aError) { 811 return GetTrustedTypesCompliantAttributeValue<const nsAString*>( 812 aElement, aAttributeName, aAttributeNamespaceID, &aNewValue, 813 aPrincipalOrNull, aResultHolder, aError); 814 } 815 816 bool HostGetCodeForEval(JSContext* aCx, JS::Handle<JSObject*> aCode, 817 JS::MutableHandle<JSString*> aOutCode) { 818 JS::Rooted<JSObject*> obj(aCx, aCode); 819 TrustedScript* trustedScript; 820 if (StaticPrefs::dom_security_trusted_types_enabled() && 821 NS_SUCCEEDED(UNWRAP_OBJECT(TrustedScript, &obj, trustedScript))) { 822 if (JSString* copy = JS_NewUCStringCopyZ(aCx, trustedScript->mData.get())) { 823 aOutCode.set(copy); 824 return true; 825 } 826 return false; 827 } 828 aOutCode.set(nullptr); 829 return true; 830 } 831 832 bool AreArgumentsTrustedForEnsureCSPDoesNotBlockStringCompilation( 833 JSContext* aCx, JS::Handle<JSString*> aCodeString, 834 JS::CompilationType aCompilationType, 835 JS::Handle<JS::StackGCVector<JSString*>> aParameterStrings, 836 JS::Handle<JSString*> aBodyString, 837 JS::Handle<JS::StackGCVector<JS::Value>> aParameterArgs, 838 JS::Handle<JS::Value> aBodyArg, nsIPrincipal* aPrincipalOrNull, 839 ErrorResult& aError) { 840 // EnsureCSPDoesNotBlockStringCompilation is essentially HTML's implementation 841 // of HostEnsureCanCompileStrings, so we only consider the cases described in 842 // the Dynamic Code Brand Checks spec. The algorithm is also supposed to be 843 // called for "TIMER" too but in that case it does not execute the specific 844 // part implemented in the present method (step 2). 845 // https://html.spec.whatwg.org/multipage/webappapis.html#hostensurecancompilestrings(realm,-parameterstrings,-bodystring,-codestring,-compilationtype,-parameterargs,-bodyarg) 846 // https://tc39.es/proposal-dynamic-code-brand-checks/#sec-hostensurecancompilestrings 847 // https://html.spec.whatwg.org/#timer-initialisation-steps 848 if (!StaticPrefs::dom_security_trusted_types_enabled() || 849 aCompilationType == JS::CompilationType::Undefined) { 850 return true; 851 } 852 853 // https://html.spec.whatwg.org/multipage/webappapis.html#hostensurecancompilestrings(realm,-parameterstrings,-bodystring,-codestring,-compilationtype,-parameterargs,-bodyarg) 854 // https://w3c.github.io/webappsec-csp/#can-compile-strings 855 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 856 if (!global) { 857 aError.Throw(NS_ERROR_NULL_POINTER); 858 return false; 859 } 860 861 // Exit early for some cases where GetTrustedTypesCompliantString 862 // would have no effect on aCodeString. 863 if (nsPIDOMWindowInner* piDOMWindowInner = global->GetAsInnerWindow()) { 864 const Document* extantDoc = piDOMWindowInner->GetExtantDoc(); 865 if (extantDoc && 866 !extantDoc->HasPolicyWithRequireTrustedTypesForDirective()) { 867 return true; 868 } 869 } else { 870 JSObject* globalJSObject = global->GetGlobalJSObject(); 871 if (!globalJSObject || !IsWorkerGlobal(globalJSObject)) { 872 // Global object is neither a Window not a WorkerGlobalScope, this can 873 // happen in some contexts where Trusted Types don't apply (chrome JS 874 // globals) so just treat arguments as trusted. 875 return true; 876 } 877 MOZ_ASSERT(!NS_IsMainThread()); 878 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 879 const mozilla::ipc::CSPInfo& cspInfo = workerPrivate->GetCSPInfo(); 880 if (cspInfo.requireTrustedTypesForDirectiveState() == 881 RequireTrustedTypesForDirectiveState::NONE) { 882 return true; 883 } 884 } 885 886 // Steps 2.2 - 2.4. 887 bool isTrusted = true; 888 auto isArgumentTrusted = [&aCx](JS::Handle<JS::Value> aValue, 889 JS::Handle<JSString*> aString, 890 ErrorResult& aError) { 891 if (!aValue.isObject()) { 892 return false; 893 } 894 JS::Rooted<JSObject*> object(aCx, &aValue.toObject()); 895 TrustedScript* trustedScript; 896 if (NS_FAILED(UNWRAP_OBJECT(TrustedScript, &object, trustedScript))) { 897 return false; 898 } 899 nsAutoJSString jsString; 900 if (NS_WARN_IF(!jsString.init(aCx, aString))) { 901 aError.StealExceptionFromJSContext(aCx); 902 return false; 903 } 904 return jsString.Equals(trustedScript->mData); 905 }; 906 if (aCompilationType == JS::CompilationType::DirectEval || 907 aCompilationType == JS::CompilationType::IndirectEval) { 908 // The following assertions are guanranteed by the steps of PerformEval. 909 MOZ_ASSERT(aParameterArgs.empty()); 910 MOZ_ASSERT(aParameterStrings.empty()); 911 MOZ_ASSERT(aBodyString); 912 MOZ_ASSERT(aBodyArg.isString() || aBodyArg.isObject()); 913 isTrusted = aBodyArg.isObject(); 914 #ifdef DEBUG 915 bool trusted = isArgumentTrusted(aBodyArg, aBodyString, aError); 916 if (aError.Failed()) { 917 return false; 918 } 919 // The following assertion is guaranteed by the HTML implementation of 920 // HostGetCodeForEval. 921 MOZ_ASSERT(isTrusted == trusted); 922 #endif 923 } else { 924 MOZ_ASSERT(aCompilationType == JS::CompilationType::Function); 925 if (aBodyString) { 926 isTrusted = isArgumentTrusted(aBodyArg, aBodyString, aError); 927 if (aError.Failed()) { 928 return false; 929 } 930 } 931 if (isTrusted) { 932 MOZ_ASSERT(aParameterArgs.length() == aParameterStrings.length()); 933 for (size_t index = 0; index < aParameterArgs.length(); index++) { 934 isTrusted = isArgumentTrusted(aParameterArgs[index], 935 aParameterStrings[index], aError); 936 if (aError.Failed()) { 937 return false; 938 } 939 if (!isTrusted) { 940 break; 941 } 942 } 943 } 944 } 945 946 // If successful, the steps below always ends up with sourceString == 947 // codeString. Moreover if isTrusted == true, passing a new TrustedScript to 948 // GetTrustedTypesCompliantStringForTrustedScript would just return codeString 949 // immediately, so we can skip all these steps. 950 if (isTrusted) { 951 return true; 952 } 953 954 // Steps 2.5 - 2.6. 955 nsAutoJSString codeString; 956 if (NS_WARN_IF(!codeString.init(aCx, aCodeString))) { 957 aError.StealExceptionFromJSContext(aCx); 958 return false; 959 } 960 961 Maybe<nsAutoString> compliantStringHolder; 962 constexpr nsLiteralString evalSink = u"eval"_ns; 963 constexpr nsLiteralString functionSink = u"Function"_ns; 964 nsCOMPtr<nsIGlobalObject> pinnedGlobal = global; 965 const nsAString* compliantString = 966 dom::TrustedTypeUtils::GetTrustedTypesCompliantStringForTrustedScript( 967 codeString, 968 aCompilationType == JS::CompilationType::Function ? functionSink 969 : evalSink, 970 kTrustedTypesOnlySinkGroup, *pinnedGlobal, aPrincipalOrNull, 971 compliantStringHolder, aError); 972 973 // Step 2.7-2.8. 974 // Callers will take care of throwing an EvalError when we return false. 975 if (aError.Failed()) { 976 aError.SuppressException(); 977 return false; 978 } 979 return compliantString->Equals(codeString); 980 } 981 982 MOZ_CAN_RUN_SCRIPT const nsAString* 983 GetConvertedScriptSourceForPreNavigationCheck( 984 nsIGlobalObject& aGlobalObject, const nsAString& aEncodedScriptSource, 985 const nsAString& aSink, Maybe<nsAutoString>& aResultHolder, 986 ErrorResult& aError) { 987 RefPtr<TrustedScript> convertedScriptSource; 988 nsCOMPtr<nsIGlobalObject> pinnedGlobalObject = &aGlobalObject; 989 ProcessValueWithADefaultPolicy<TrustedScript>( 990 *pinnedGlobalObject, aEncodedScriptSource, aSink, 991 getter_AddRefs(convertedScriptSource), aError); 992 993 // If that algorithm threw an error or convertedScriptSource is not a 994 // TrustedScript object, return "Blocked" and abort further steps. 995 if (aError.Failed()) { 996 return nullptr; 997 } 998 if (!convertedScriptSource) { 999 aError.ThrowTypeError("Pre-Navigation check Blocked"_ns); 1000 return nullptr; 1001 } 1002 1003 aResultHolder = Some(convertedScriptSource->mData); 1004 return aResultHolder.ptr(); 1005 } 1006 1007 } // namespace mozilla::dom::TrustedTypeUtils