Sandbox.cpp (78799B)
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 /* 8 * The Components.Sandbox object. 9 */ 10 11 #include "AccessCheck.h" 12 #include "jsfriendapi.h" 13 #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject 14 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable 15 #include "js/CharacterEncoding.h" 16 #include "js/CompilationAndEvaluation.h" 17 #include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot 18 #include "js/PropertyAndElement.h" // JS_DefineFunction, JS_DefineFunctions, JS_DefineProperty, JS_GetElement, JS_GetProperty, JS_HasProperty, JS_SetProperty, JS_SetPropertyById 19 #include "js/PropertyDescriptor.h" // JS::PropertyDescriptor, JS_GetOwnPropertyDescriptorById, JS_GetPropertyDescriptorById 20 #include "js/PropertySpec.h" 21 #include "js/Proxy.h" 22 #include "js/SourceText.h" 23 #include "js/StructuredClone.h" 24 #include "nsContentUtils.h" 25 #include "nsGlobalWindowInner.h" 26 #include "nsIException.h" // for nsIStackFrame 27 #include "nsIScriptContext.h" 28 #include "nsIScriptObjectPrincipal.h" 29 #include "nsIURI.h" 30 #include "nsJSUtils.h" 31 #include "nsNetUtil.h" 32 #include "ExpandedPrincipal.h" 33 #include "WrapperFactory.h" 34 #include "xpcprivate.h" 35 #include "xpc_make_class.h" 36 #include "XPCWrapper.h" 37 #include "Crypto.h" 38 #include "mozilla/Result.h" 39 #include "mozilla/dom/AbortControllerBinding.h" 40 #include "mozilla/dom/AutoEntryScript.h" 41 #include "mozilla/dom/BindingCallContext.h" 42 #include "mozilla/dom/BindingUtils.h" 43 #include "mozilla/dom/BlobBinding.h" 44 #include "mozilla/dom/cache/CacheStorage.h" 45 #include "mozilla/dom/CSSBinding.h" 46 #include "mozilla/dom/CSSPositionTryDescriptorsBinding.h" 47 #include "mozilla/dom/CSSRuleBinding.h" 48 #include "mozilla/dom/DirectoryBinding.h" 49 #include "mozilla/dom/DocumentBinding.h" 50 #include "mozilla/dom/DOMExceptionBinding.h" 51 #include "mozilla/dom/DOMParserBinding.h" 52 #include "mozilla/dom/DOMTokenListBinding.h" 53 #include "mozilla/dom/ElementBinding.h" 54 #include "mozilla/dom/ElementInternalsBinding.h" 55 #include "mozilla/dom/EventBinding.h" 56 #include "mozilla/dom/Exceptions.h" 57 #include "mozilla/dom/IndexedDatabaseManager.h" 58 #include "mozilla/dom/Fetch.h" 59 #include "mozilla/dom/FileBinding.h" 60 #include "mozilla/dom/HeadersBinding.h" 61 #include "mozilla/dom/IOUtilsBinding.h" 62 #include "mozilla/dom/InspectorUtilsBinding.h" 63 #include "mozilla/dom/LockManager.h" 64 #include "mozilla/dom/MessageChannelBinding.h" 65 #include "mozilla/dom/MessagePortBinding.h" 66 #include "mozilla/dom/MIDIInputMapBinding.h" 67 #include "mozilla/dom/MIDIOutputMapBinding.h" 68 #include "mozilla/dom/ModuleLoader.h" 69 #include "mozilla/dom/NodeBinding.h" 70 #include "mozilla/dom/NodeFilterBinding.h" 71 #include "mozilla/dom/PathUtilsBinding.h" 72 #include "mozilla/dom/PerformanceBinding.h" 73 #include "mozilla/dom/PromiseBinding.h" 74 #include "mozilla/dom/PromiseDebuggingBinding.h" 75 #include "mozilla/dom/RangeBinding.h" 76 #include "mozilla/dom/RequestBinding.h" 77 #include "mozilla/dom/ReadableStreamBinding.h" 78 #include "mozilla/dom/ResponseBinding.h" 79 #ifdef MOZ_WEBRTC 80 # include "mozilla/dom/RTCIdentityProviderRegistrar.h" 81 #endif 82 #include "mozilla/dom/FileReaderBinding.h" 83 #include "mozilla/dom/ScriptLoader.h" 84 #include "mozilla/dom/ScriptSettings.h" 85 #include "mozilla/dom/SelectionBinding.h" 86 #include "mozilla/dom/StorageManager.h" 87 #include "mozilla/dom/StorageManagerBinding.h" 88 #include "mozilla/dom/TextDecoderBinding.h" 89 #include "mozilla/dom/TextEncoderBinding.h" 90 #include "mozilla/dom/TrustedHTML.h" 91 #include "mozilla/dom/TrustedScript.h" 92 #include "mozilla/dom/TrustedScriptURL.h" 93 #include "mozilla/dom/URLBinding.h" 94 #include "mozilla/dom/URLSearchParamsBinding.h" 95 #include "mozilla/dom/XMLHttpRequest.h" 96 #include "mozilla/dom/WebSocketBinding.h" 97 #include "mozilla/dom/WindowBinding.h" 98 #include "mozilla/dom/XMLSerializerBinding.h" 99 #include "mozilla/dom/FormDataBinding.h" 100 #include "mozilla/dom/nsCSPContext.h" 101 #include "mozilla/dom/PolicyContainer.h" 102 #include "mozilla/ipc/BackgroundUtils.h" 103 #include "mozilla/ipc/PBackgroundSharedTypes.h" 104 #include "mozilla/BasePrincipal.h" 105 #include "mozilla/DeferredFinalize.h" 106 #include "mozilla/ExtensionPolicyService.h" 107 #include "mozilla/Maybe.h" 108 #include "mozilla/NullPrincipal.h" 109 #include "mozilla/ResultExtensions.h" 110 #include "mozilla/StaticPrefs_extensions.h" 111 112 using namespace mozilla; 113 using namespace mozilla::dom; 114 using namespace JS; 115 using namespace JS::loader; 116 using namespace xpc; 117 118 using mozilla::dom::DestroyProtoAndIfaceCache; 119 using mozilla::dom::IndexedDatabaseManager; 120 121 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(SandboxPrivate) 122 123 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SandboxPrivate) 124 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 125 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE 126 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR 127 NS_IMPL_CYCLE_COLLECTION_UNLINK(mModuleLoader) 128 tmp->UnlinkObjectsInGlobal(); 129 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 130 131 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SandboxPrivate) 132 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mModuleLoader) 133 tmp->TraverseObjectsInGlobal(cb); 134 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 135 136 NS_IMPL_CYCLE_COLLECTING_ADDREF(SandboxPrivate) 137 NS_IMPL_CYCLE_COLLECTING_RELEASE(SandboxPrivate) 138 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SandboxPrivate) 139 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 140 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptObjectPrincipal) 141 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) 142 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) 143 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 144 NS_INTERFACE_MAP_END 145 146 class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox, 147 public nsIXPCScriptable { 148 public: 149 // Aren't macros nice? 150 NS_DECL_ISUPPORTS 151 NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX 152 NS_DECL_NSIXPCSCRIPTABLE 153 154 public: 155 nsXPCComponents_utils_Sandbox(); 156 157 private: 158 virtual ~nsXPCComponents_utils_Sandbox(); 159 160 static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper, 161 JSContext* cx, HandleObject obj, 162 const CallArgs& args, bool* _retval); 163 }; 164 165 already_AddRefed<nsIXPCComponents_utils_Sandbox> xpc::NewSandboxConstructor() { 166 nsCOMPtr<nsIXPCComponents_utils_Sandbox> sbConstructor = 167 new nsXPCComponents_utils_Sandbox(); 168 return sbConstructor.forget(); 169 } 170 171 static bool SandboxDump(JSContext* cx, unsigned argc, Value* vp) { 172 if (!nsJSUtils::DumpEnabled()) { 173 return true; 174 } 175 176 CallArgs args = CallArgsFromVp(argc, vp); 177 178 if (args.length() == 0) { 179 return true; 180 } 181 182 RootedString str(cx, ToString(cx, args[0])); 183 if (!str) { 184 return false; 185 } 186 187 JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str); 188 char* cstr = utf8str.get(); 189 if (!cstr) { 190 return false; 191 } 192 193 #if defined(XP_MACOSX) 194 // Be nice and convert all \r to \n. 195 char* c = cstr; 196 char* cEnd = cstr + strlen(cstr); 197 while (c < cEnd) { 198 if (*c == '\r') { 199 *c = '\n'; 200 } 201 c++; 202 } 203 #endif 204 MOZ_LOG(nsContentUtils::DOMDumpLog(), mozilla::LogLevel::Debug, 205 ("[Sandbox.Dump] %s", cstr)); 206 #ifdef ANDROID 207 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr); 208 #endif 209 fputs(cstr, stdout); 210 fflush(stdout); 211 args.rval().setBoolean(true); 212 return true; 213 } 214 215 static bool SandboxDebug(JSContext* cx, unsigned argc, Value* vp) { 216 #ifdef DEBUG 217 return SandboxDump(cx, argc, vp); 218 #else 219 return true; 220 #endif 221 } 222 223 static bool SandboxImport(JSContext* cx, unsigned argc, Value* vp) { 224 CallArgs args = CallArgsFromVp(argc, vp); 225 226 if (args.length() < 1 || args[0].isPrimitive()) { 227 XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); 228 return false; 229 } 230 231 RootedString funname(cx); 232 if (args.length() > 1) { 233 // Use the second parameter as the function name. 234 funname = ToString(cx, args[1]); 235 if (!funname) { 236 return false; 237 } 238 } else { 239 // NB: funobj must only be used to get the JSFunction out. 240 RootedObject funobj(cx, &args[0].toObject()); 241 if (js::IsProxy(funobj)) { 242 funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj); 243 } 244 245 JSAutoRealm ar(cx, funobj); 246 247 RootedValue funval(cx, ObjectValue(*funobj)); 248 JS::Rooted<JSFunction*> fun(cx, JS_ValueToFunction(cx, funval)); 249 if (!fun) { 250 XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); 251 return false; 252 } 253 254 // Use the actual function name as the name. 255 if (!JS_GetFunctionId(cx, fun, &funname)) { 256 return false; 257 } 258 if (!funname) { 259 XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx); 260 return false; 261 } 262 } 263 JS_MarkCrossZoneIdValue(cx, StringValue(funname)); 264 265 RootedId id(cx); 266 if (!JS_StringToId(cx, funname, &id)) { 267 return false; 268 } 269 270 // We need to resolve the this object, because this function is used 271 // unbound and should still work and act on the original sandbox. 272 273 RootedObject thisObject(cx); 274 if (!args.computeThis(cx, &thisObject)) { 275 return false; 276 } 277 278 if (!JS_SetPropertyById(cx, thisObject, id, args[0])) { 279 return false; 280 } 281 282 args.rval().setUndefined(); 283 return true; 284 } 285 286 bool xpc::SandboxCreateCrypto(JSContext* cx, JS::Handle<JSObject*> obj) { 287 MOZ_ASSERT(JS_IsGlobalObject(obj)); 288 289 nsIGlobalObject* native = xpc::NativeGlobal(obj); 290 MOZ_ASSERT(native); 291 292 dom::Crypto* crypto = new dom::Crypto(native); 293 JS::RootedObject wrapped(cx, crypto->WrapObject(cx, nullptr)); 294 return JS_DefineProperty(cx, obj, "crypto", wrapped, JSPROP_ENUMERATE); 295 } 296 297 #ifdef MOZ_WEBRTC 298 static bool SandboxCreateRTCIdentityProvider(JSContext* cx, 299 JS::HandleObject obj) { 300 MOZ_ASSERT(JS_IsGlobalObject(obj)); 301 302 nsCOMPtr<nsIGlobalObject> nativeGlobal = xpc::NativeGlobal(obj); 303 MOZ_ASSERT(nativeGlobal); 304 305 dom::RTCIdentityProviderRegistrar* registrar = 306 new dom::RTCIdentityProviderRegistrar(nativeGlobal); 307 JS::RootedObject wrapped(cx, registrar->WrapObject(cx, nullptr)); 308 return JS_DefineProperty(cx, obj, "rtcIdentityProvider", wrapped, 309 JSPROP_ENUMERATE); 310 } 311 #endif 312 313 static bool SandboxFetch(JSContext* cx, JS::HandleObject scope, 314 const CallArgs& args) { 315 if (args.length() < 1) { 316 JS_ReportErrorASCII(cx, "fetch requires at least 1 argument"); 317 return false; 318 } 319 320 BindingCallContext callCx(cx, "fetch"); 321 RequestOrUTF8String request; 322 if (!request.Init(callCx, args[0], "Argument 1")) { 323 return false; 324 } 325 RootedDictionary<dom::RequestInit> options(cx); 326 if (!options.Init(callCx, args.hasDefined(1) ? args[1] : JS::NullHandleValue, 327 "Argument 2", false)) { 328 return false; 329 } 330 nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(scope); 331 if (!global) { 332 return false; 333 } 334 dom::CallerType callerType = nsContentUtils::IsSystemCaller(cx) 335 ? dom::CallerType::System 336 : dom::CallerType::NonSystem; 337 ErrorResult rv; 338 RefPtr<dom::Promise> response = FetchRequest( 339 global, Constify(request), Constify(options), callerType, rv); 340 if (rv.MaybeSetPendingException(cx)) { 341 return false; 342 } 343 344 args.rval().setObject(*response->PromiseObj()); 345 return true; 346 } 347 348 static bool SandboxFetchPromise(JSContext* cx, unsigned argc, Value* vp) { 349 CallArgs args = CallArgsFromVp(argc, vp); 350 RootedObject scope(cx, JS::CurrentGlobalOrNull(cx)); 351 if (SandboxFetch(cx, scope, args)) { 352 return true; 353 } 354 return ConvertExceptionToPromise(cx, args.rval()); 355 } 356 357 bool xpc::SandboxCreateFetch(JSContext* cx, JS::Handle<JSObject*> obj) { 358 MOZ_ASSERT(JS_IsGlobalObject(obj)); 359 360 return JS_DefineFunction(cx, obj, "fetch", SandboxFetchPromise, 2, 0) && 361 Request_Binding::CreateAndDefineOnGlobal(cx) && 362 Response_Binding::CreateAndDefineOnGlobal(cx) && 363 Headers_Binding::CreateAndDefineOnGlobal(cx); 364 } 365 366 static bool SandboxCreateStorage(JSContext* cx, JS::HandleObject obj) { 367 MOZ_ASSERT(JS_IsGlobalObject(obj)); 368 369 nsIGlobalObject* native = xpc::NativeGlobal(obj); 370 MOZ_ASSERT(native); 371 372 if (!StorageManager_Binding::CreateAndDefineOnGlobal(cx)) { 373 return false; 374 } 375 376 dom::StorageManager* storageManager = new dom::StorageManager(native); 377 JS::RootedObject wrapped(cx, storageManager->WrapObject(cx, nullptr)); 378 return JS_DefineProperty(cx, obj, "storage", wrapped, JSPROP_ENUMERATE); 379 } 380 381 static bool SandboxStructuredClone(JSContext* cx, unsigned argc, Value* vp) { 382 CallArgs args = CallArgsFromVp(argc, vp); 383 384 if (!args.requireAtLeast(cx, "structuredClone", 1)) { 385 return false; 386 } 387 388 RootedDictionary<dom::StructuredSerializeOptions> options(cx); 389 BindingCallContext callCx(cx, "structuredClone"); 390 if (!options.Init(cx, args.hasDefined(1) ? args[1] : JS::NullHandleValue, 391 "Argument 2", false)) { 392 return false; 393 } 394 395 nsIGlobalObject* global = CurrentNativeGlobal(cx); 396 if (!global) { 397 JS_ReportErrorASCII(cx, "structuredClone: Missing global"); 398 return false; 399 } 400 401 JS::Rooted<JS::Value> result(cx); 402 ErrorResult rv; 403 nsContentUtils::StructuredClone(cx, global, args[0], options, &result, rv); 404 if (rv.MaybeSetPendingException(cx)) { 405 return false; 406 } 407 408 MOZ_ASSERT_IF(result.isGCThing(), 409 !JS::GCThingIsMarkedGray(result.toGCCellPtr())); 410 args.rval().set(result); 411 return true; 412 } 413 414 bool xpc::SandboxCreateStructuredClone(JSContext* cx, HandleObject obj) { 415 MOZ_ASSERT(JS_IsGlobalObject(obj)); 416 417 return JS_DefineFunction(cx, obj, "structuredClone", SandboxStructuredClone, 418 1, 0); 419 } 420 421 bool xpc::SandboxCreateLocks(JSContext* cx, JS::Handle<JSObject*> obj) { 422 MOZ_ASSERT(JS_IsGlobalObject(obj)); 423 424 nsIGlobalObject* native = xpc::NativeGlobal(obj); 425 MOZ_ASSERT(native); 426 427 RefPtr<dom::LockManager> lockManager = dom::LockManager::Create(*native); 428 JS::RootedObject wrapped(cx, lockManager->WrapObject(cx, nullptr)); 429 return JS_DefineProperty(cx, obj, "locks", wrapped, JSPROP_ENUMERATE); 430 } 431 432 static bool SandboxIsProxy(JSContext* cx, unsigned argc, Value* vp) { 433 CallArgs args = CallArgsFromVp(argc, vp); 434 if (args.length() < 1) { 435 JS_ReportErrorASCII(cx, "Function requires at least 1 argument"); 436 return false; 437 } 438 if (!args[0].isObject()) { 439 args.rval().setBoolean(false); 440 return true; 441 } 442 443 RootedObject obj(cx, &args[0].toObject()); 444 // CheckedUnwrapStatic is OK here, since we only care about whether 445 // it's a scripted proxy and the things CheckedUnwrapStatic fails on 446 // are not. 447 obj = js::CheckedUnwrapStatic(obj); 448 if (!obj) { 449 args.rval().setBoolean(false); 450 return true; 451 } 452 453 args.rval().setBoolean(js::IsScriptedProxy(obj)); 454 return true; 455 } 456 457 /* 458 * Expected type of the arguments and the return value: 459 * function exportFunction(function funToExport, 460 * object targetScope, 461 * [optional] object options) 462 */ 463 static bool SandboxExportFunction(JSContext* cx, unsigned argc, Value* vp) { 464 CallArgs args = CallArgsFromVp(argc, vp); 465 if (args.length() < 2) { 466 JS_ReportErrorASCII(cx, "Function requires at least 2 arguments"); 467 return false; 468 } 469 470 RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue()); 471 return ExportFunction(cx, args[0], args[1], options, args.rval()); 472 } 473 474 static bool SandboxCreateObjectIn(JSContext* cx, unsigned argc, Value* vp) { 475 CallArgs args = CallArgsFromVp(argc, vp); 476 if (args.length() < 1) { 477 JS_ReportErrorASCII(cx, "Function requires at least 1 argument"); 478 return false; 479 } 480 481 RootedObject optionsObj(cx); 482 bool calledWithOptions = args.length() > 1; 483 if (calledWithOptions) { 484 if (!args[1].isObject()) { 485 JS_ReportErrorASCII( 486 cx, "Expected the 2nd argument (options) to be an object"); 487 return false; 488 } 489 optionsObj = &args[1].toObject(); 490 } 491 492 CreateObjectInOptions options(cx, optionsObj); 493 if (calledWithOptions && !options.Parse()) { 494 return false; 495 } 496 497 return xpc::CreateObjectIn(cx, args[0], options, args.rval()); 498 } 499 500 static bool SandboxCloneInto(JSContext* cx, unsigned argc, Value* vp) { 501 CallArgs args = CallArgsFromVp(argc, vp); 502 if (args.length() < 2) { 503 JS_ReportErrorASCII(cx, "Function requires at least 2 arguments"); 504 return false; 505 } 506 507 RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue()); 508 return xpc::CloneInto(cx, args[0], args[1], options, args.rval()); 509 } 510 511 static void sandbox_finalize(JS::GCContext* gcx, JSObject* obj) { 512 SandboxPrivate* priv = SandboxPrivate::GetPrivate(obj); 513 if (!priv) { 514 // priv can be null if CreateSandboxObject fails in the middle. 515 return; 516 } 517 518 priv->ForgetGlobalObject(obj); 519 DestroyProtoAndIfaceCache(obj); 520 DeferredFinalize(static_cast<nsIScriptObjectPrincipal*>(priv)); 521 } 522 523 static size_t sandbox_moved(JSObject* obj, JSObject* old) { 524 // Note that this hook can be called before the private pointer is set. In 525 // this case the SandboxPrivate will not exist yet, so there is nothing to 526 // do. 527 SandboxPrivate* priv = SandboxPrivate::GetPrivate(obj); 528 if (!priv) { 529 return 0; 530 } 531 532 return priv->ObjectMoved(obj, old); 533 } 534 535 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT \ 536 (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET) 537 538 static const JSClassOps SandboxClassOps = { 539 nullptr, // addProperty 540 nullptr, // delProperty 541 nullptr, // enumerate 542 JS_NewEnumerateStandardClasses, // newEnumerate 543 JS_ResolveStandardClass, // resolve 544 JS_MayResolveStandardClass, // mayResolve 545 sandbox_finalize, // finalize 546 nullptr, // call 547 nullptr, // construct 548 JS_GlobalObjectTraceHook, // trace 549 }; 550 551 static const js::ClassExtension SandboxClassExtension = { 552 sandbox_moved, // objectMovedOp 553 }; 554 555 static const JSClass SandboxClass = { 556 "Sandbox", 557 XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) | JSCLASS_FOREGROUND_FINALIZE, 558 &SandboxClassOps, 559 JS_NULL_CLASS_SPEC, 560 &SandboxClassExtension, 561 JS_NULL_OBJECT_OPS}; 562 563 static const JSFunctionSpec SandboxFunctions[] = { 564 JS_FN("dump", SandboxDump, 1, 0), JS_FN("debug", SandboxDebug, 1, 0), 565 JS_FN("importFunction", SandboxImport, 1, 0), JS_FS_END}; 566 567 bool xpc::IsSandbox(JSObject* obj) { 568 const JSClass* clasp = JS::GetClass(obj); 569 return clasp == &SandboxClass; 570 } 571 572 /***************************************************************************/ 573 nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox() = default; 574 575 nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox() = default; 576 577 NS_IMPL_QUERY_INTERFACE(nsXPCComponents_utils_Sandbox, 578 nsIXPCComponents_utils_Sandbox, nsIXPCScriptable) 579 580 NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox) 581 NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox) 582 583 // We use the nsIXPScriptable macros to generate lots of stuff for us. 584 #define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox 585 #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox" 586 #define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | XPC_SCRIPTABLE_WANT_CONSTRUCT) 587 #include "xpc_map_end.h" /* This #undef's the above. */ 588 589 class SandboxProxyHandler : public js::Wrapper { 590 public: 591 constexpr SandboxProxyHandler() : js::Wrapper(0) {} 592 593 virtual bool getOwnPropertyDescriptor( 594 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 595 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const override; 596 597 // We just forward the high-level methods to the BaseProxyHandler versions 598 // which implement them in terms of lower-level methods. 599 virtual bool has(JSContext* cx, JS::Handle<JSObject*> proxy, 600 JS::Handle<jsid> id, bool* bp) const override; 601 virtual bool get(JSContext* cx, JS::Handle<JSObject*> proxy, 602 JS::HandleValue receiver, JS::Handle<jsid> id, 603 JS::MutableHandle<JS::Value> vp) const override; 604 virtual bool set(JSContext* cx, JS::Handle<JSObject*> proxy, 605 JS::Handle<jsid> id, JS::Handle<JS::Value> v, 606 JS::Handle<JS::Value> receiver, 607 JS::ObjectOpResult& result) const override; 608 609 virtual bool hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, 610 JS::Handle<jsid> id, bool* bp) const override; 611 virtual bool getOwnEnumerablePropertyKeys( 612 JSContext* cx, JS::Handle<JSObject*> proxy, 613 JS::MutableHandleIdVector props) const override; 614 virtual bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, 615 JS::MutableHandleIdVector props) const override; 616 617 private: 618 // Implements the custom getPropertyDescriptor behavior. If the getOwn 619 // argument is true we only look for "own" properties. 620 bool getPropertyDescriptorImpl( 621 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 622 bool getOwn, JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const; 623 }; 624 625 static const SandboxProxyHandler sandboxProxyHandler; 626 627 namespace xpc { 628 629 bool IsSandboxPrototypeProxy(JSObject* obj) { 630 return js::IsProxy(obj) && js::GetProxyHandler(obj) == &sandboxProxyHandler; 631 } 632 633 bool IsWebExtensionContentScriptSandbox(JSObject* obj) { 634 return IsSandbox(obj) && 635 CompartmentPrivate::Get(obj)->isWebExtensionContentScript; 636 } 637 638 } // namespace xpc 639 640 // A proxy handler that lets us wrap callables and invoke them with 641 // the correct this object, while forwarding all other operations down 642 // to them directly. 643 class SandboxCallableProxyHandler : public js::Wrapper { 644 public: 645 constexpr SandboxCallableProxyHandler() : js::Wrapper(0) {} 646 647 virtual bool call(JSContext* cx, JS::Handle<JSObject*> proxy, 648 const JS::CallArgs& args) const override; 649 650 static const size_t SandboxProxySlot = 0; 651 652 static inline JSObject* getSandboxProxy(JS::Handle<JSObject*> proxy) { 653 return &js::GetProxyReservedSlot(proxy, SandboxProxySlot).toObject(); 654 } 655 }; 656 657 static const SandboxCallableProxyHandler sandboxCallableProxyHandler; 658 659 bool SandboxCallableProxyHandler::call(JSContext* cx, 660 JS::Handle<JSObject*> proxy, 661 const JS::CallArgs& args) const { 662 // We forward the call to our underlying callable. 663 664 // Get our SandboxProxyHandler proxy. 665 RootedObject sandboxProxy(cx, getSandboxProxy(proxy)); 666 MOZ_ASSERT(js::IsProxy(sandboxProxy) && 667 js::GetProxyHandler(sandboxProxy) == &sandboxProxyHandler); 668 669 // The global of the sandboxProxy is the sandbox global, and the 670 // target object is the original proto. 671 RootedObject sandboxGlobal(cx, JS::GetNonCCWObjectGlobal(sandboxProxy)); 672 MOZ_ASSERT(IsSandbox(sandboxGlobal)); 673 674 // If our this object is the sandbox global, we call with this set to the 675 // original proto instead. 676 // 677 // There are two different ways we can compute |this|. If we use 678 // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the 679 // caller, which may be undefined if a global function was invoked without 680 // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this| 681 // in |vp| will be coerced to the global, which is not the correct 682 // behavior in ES5 strict mode. And we have no way to compute strictness 683 // here. 684 // 685 // The naive approach is simply to use JS_THIS_VALUE here. If |this| was 686 // explicit, we can remap it appropriately. If it was implicit, then we 687 // leave it as undefined, and let the callee sort it out. Since the callee 688 // is generally in the same compartment as its global (eg the Window's 689 // compartment, not the Sandbox's), the callee will generally compute the 690 // correct |this|. 691 // 692 // However, this breaks down in the Xray case. If the sandboxPrototype 693 // is an Xray wrapper, then we'll end up reifying the native methods in 694 // the Sandbox's scope, which means that they'll compute |this| to be the 695 // Sandbox, breaking old-style XPC_WN_CallMethod methods. 696 // 697 // Luckily, the intent of Xrays is to provide a vanilla view of a foreign 698 // DOM interface, which means that we don't care about script-enacted 699 // strictness in the prototype's home compartment. Indeed, since DOM 700 // methods are always non-strict, we can just assume non-strict semantics 701 // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately 702 // remap |this|. 703 bool isXray = WrapperFactory::IsXrayWrapper(sandboxProxy); 704 RootedValue thisVal(cx, args.thisv()); 705 if (isXray) { 706 RootedObject thisObject(cx); 707 if (!args.computeThis(cx, &thisObject)) { 708 return false; 709 } 710 thisVal.setObject(*thisObject); 711 } 712 713 if (thisVal == ObjectValue(*sandboxGlobal)) { 714 thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy)); 715 } 716 717 RootedValue func(cx, js::GetProxyPrivate(proxy)); 718 return JS::Call(cx, thisVal, func, args, args.rval()); 719 } 720 721 /* 722 * Wrap a callable such that if we're called with oldThisObj as the 723 * "this" we will instead call it with newThisObj as the this. 724 */ 725 static JSObject* WrapCallable(JSContext* cx, HandleObject callable, 726 HandleObject sandboxProtoProxy) { 727 MOZ_ASSERT(JS::IsCallable(callable)); 728 // Our proxy is wrapping the callable. So we need to use the 729 // callable as the private. We put the given sandboxProtoProxy in 730 // an extra slot, and our call() hook depends on that. 731 MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) && 732 js::GetProxyHandler(sandboxProtoProxy) == &sandboxProxyHandler); 733 734 RootedValue priv(cx, ObjectValue(*callable)); 735 // We want to claim to have the same proto as our wrapped callable, so set 736 // ourselves up with a lazy proto. 737 js::ProxyOptions options; 738 options.setLazyProto(true); 739 JSObject* obj = js::NewProxyObject(cx, &sandboxCallableProxyHandler, priv, 740 nullptr, options); 741 if (obj) { 742 js::SetProxyReservedSlot(obj, SandboxCallableProxyHandler::SandboxProxySlot, 743 ObjectValue(*sandboxProtoProxy)); 744 } 745 746 return obj; 747 } 748 749 bool WrapAccessorFunction(JSContext* cx, MutableHandleObject accessor, 750 HandleObject sandboxProtoProxy) { 751 if (!accessor) { 752 return true; 753 } 754 755 accessor.set(WrapCallable(cx, accessor, sandboxProtoProxy)); 756 return !!accessor; 757 } 758 759 static bool IsMaybeWrappedDOMConstructor(JSObject* obj) { 760 // We really care about the underlying object here, which might be wrapped in 761 // cross-compartment wrappers. CheckedUnwrapStatic is fine, since we just 762 // care whether it's a DOM constructor. 763 obj = js::CheckedUnwrapStatic(obj); 764 if (!obj) { 765 return false; 766 } 767 768 return dom::IsDOMConstructor(obj); 769 } 770 771 bool SandboxProxyHandler::getPropertyDescriptorImpl( 772 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 773 bool getOwn, MutableHandle<Maybe<PropertyDescriptor>> desc_) const { 774 JS::RootedObject obj(cx, wrappedObject(proxy)); 775 776 MOZ_ASSERT(JS::GetCompartment(obj) == JS::GetCompartment(proxy)); 777 778 if (getOwn) { 779 if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, desc_)) { 780 return false; 781 } 782 } else { 783 Rooted<JSObject*> holder(cx); 784 if (!JS_GetPropertyDescriptorById(cx, obj, id, desc_, &holder)) { 785 return false; 786 } 787 } 788 789 if (desc_.isNothing()) { 790 return true; 791 } 792 793 Rooted<PropertyDescriptor> desc(cx, *desc_); 794 795 // Now fix up the getter/setter/value as needed. 796 if (desc.hasGetter() && !WrapAccessorFunction(cx, desc.getter(), proxy)) { 797 return false; 798 } 799 if (desc.hasSetter() && !WrapAccessorFunction(cx, desc.setter(), proxy)) { 800 return false; 801 } 802 if (desc.hasValue() && desc.value().isObject()) { 803 RootedObject val(cx, &desc.value().toObject()); 804 if (JS::IsCallable(val) && 805 // Don't wrap DOM constructors: they don't care about the "this" 806 // they're invoked with anyway, being constructors. And if we wrap 807 // them here we break invariants like Node == Node and whatnot. 808 !IsMaybeWrappedDOMConstructor(val)) { 809 val = WrapCallable(cx, val, proxy); 810 if (!val) { 811 return false; 812 } 813 desc.value().setObject(*val); 814 } 815 } 816 817 desc_.set(Some(desc.get())); 818 return true; 819 } 820 821 bool SandboxProxyHandler::getOwnPropertyDescriptor( 822 JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, 823 MutableHandle<Maybe<PropertyDescriptor>> desc) const { 824 return getPropertyDescriptorImpl(cx, proxy, id, /* getOwn = */ true, desc); 825 } 826 827 /* 828 * Reuse the BaseProxyHandler versions of the derived traps that are implemented 829 * in terms of the fundamental traps. 830 */ 831 832 bool SandboxProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, 833 JS::Handle<jsid> id, bool* bp) const { 834 // This uses JS_GetPropertyDescriptorById for backward compatibility. 835 Rooted<Maybe<PropertyDescriptor>> desc(cx); 836 if (!getPropertyDescriptorImpl(cx, proxy, id, /* getOwn = */ false, &desc)) { 837 return false; 838 } 839 840 *bp = desc.isSome(); 841 return true; 842 } 843 bool SandboxProxyHandler::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, 844 JS::Handle<jsid> id, bool* bp) const { 845 return BaseProxyHandler::hasOwn(cx, proxy, id, bp); 846 } 847 848 bool SandboxProxyHandler::get(JSContext* cx, JS::Handle<JSObject*> proxy, 849 JS::Handle<JS::Value> receiver, 850 JS::Handle<jsid> id, 851 JS::MutableHandle<Value> vp) const { 852 // This uses JS_GetPropertyDescriptorById for backward compatibility. 853 Rooted<Maybe<PropertyDescriptor>> desc(cx); 854 if (!getPropertyDescriptorImpl(cx, proxy, id, /* getOwn = */ false, &desc)) { 855 return false; 856 } 857 858 if (desc.isNothing()) { 859 vp.setUndefined(); 860 return true; 861 } else { 862 desc->assertComplete(); 863 } 864 865 // Everything after here follows [[Get]] for ordinary objects. 866 if (desc->isDataDescriptor()) { 867 vp.set(desc->value()); 868 return true; 869 } 870 871 MOZ_ASSERT(desc->isAccessorDescriptor()); 872 RootedObject getter(cx, desc->getter()); 873 874 if (!getter) { 875 vp.setUndefined(); 876 return true; 877 } 878 879 return Call(cx, receiver, getter, HandleValueArray::empty(), vp); 880 } 881 882 bool SandboxProxyHandler::set(JSContext* cx, JS::Handle<JSObject*> proxy, 883 JS::Handle<jsid> id, JS::Handle<Value> v, 884 JS::Handle<Value> receiver, 885 JS::ObjectOpResult& result) const { 886 return BaseProxyHandler::set(cx, proxy, id, v, receiver, result); 887 } 888 889 bool SandboxProxyHandler::getOwnEnumerablePropertyKeys( 890 JSContext* cx, JS::Handle<JSObject*> proxy, 891 MutableHandleIdVector props) const { 892 return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props); 893 } 894 895 bool SandboxProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, 896 JS::MutableHandleIdVector props) const { 897 return BaseProxyHandler::enumerate(cx, proxy, props); 898 } 899 900 bool xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj) { 901 uint32_t length; 902 bool ok = JS::GetArrayLength(cx, obj, &length); 903 NS_ENSURE_TRUE(ok, false); 904 for (uint32_t i = 0; i < length; i++) { 905 RootedValue nameValue(cx); 906 ok = JS_GetElement(cx, obj, i, &nameValue); 907 NS_ENSURE_TRUE(ok, false); 908 if (!nameValue.isString()) { 909 JS_ReportErrorASCII(cx, "Property names must be strings"); 910 return false; 911 } 912 JSLinearString* nameStr = JS_EnsureLinearString(cx, nameValue.toString()); 913 if (!nameStr) { 914 return false; 915 } 916 917 if (JS_LinearStringEqualsLiteral(nameStr, "AbortController")) { 918 AbortController = true; 919 } else if (JS_LinearStringEqualsLiteral(nameStr, "Blob")) { 920 Blob = true; 921 } else if (JS_LinearStringEqualsLiteral(nameStr, "ChromeUtils")) { 922 ChromeUtils = true; 923 } else if (JS_LinearStringEqualsLiteral(nameStr, "CSS")) { 924 CSS = true; 925 } else if (JS_LinearStringEqualsLiteral(nameStr, 926 "CSSPositionTryDescriptors")) { 927 CSSPositionTryDescriptors = true; 928 } else if (JS_LinearStringEqualsLiteral(nameStr, "CSSRule")) { 929 CSSRule = true; 930 } else if (JS_LinearStringEqualsLiteral(nameStr, "CustomStateSet")) { 931 CustomStateSet = true; 932 } else if (JS_LinearStringEqualsLiteral(nameStr, "Document")) { 933 Document = true; 934 } else if (JS_LinearStringEqualsLiteral(nameStr, "Directory")) { 935 Directory = true; 936 } else if (JS_LinearStringEqualsLiteral(nameStr, "DOMException")) { 937 DOMException = true; 938 } else if (JS_LinearStringEqualsLiteral(nameStr, "DOMParser")) { 939 DOMParser = true; 940 } else if (JS_LinearStringEqualsLiteral(nameStr, "DOMTokenList")) { 941 DOMTokenList = true; 942 } else if (JS_LinearStringEqualsLiteral(nameStr, "Element")) { 943 Element = true; 944 } else if (JS_LinearStringEqualsLiteral(nameStr, "Event")) { 945 Event = true; 946 } else if (JS_LinearStringEqualsLiteral(nameStr, "File")) { 947 File = true; 948 } else if (JS_LinearStringEqualsLiteral(nameStr, "FileReader")) { 949 FileReader = true; 950 } else if (JS_LinearStringEqualsLiteral(nameStr, "FormData")) { 951 FormData = true; 952 } else if (JS_LinearStringEqualsLiteral(nameStr, "Headers")) { 953 Headers = true; 954 } else if (JS_LinearStringEqualsLiteral(nameStr, "IOUtils")) { 955 IOUtils = true; 956 } else if (JS_LinearStringEqualsLiteral(nameStr, "InspectorCSSParser")) { 957 InspectorCSSParser = true; 958 } else if (JS_LinearStringEqualsLiteral(nameStr, "InspectorUtils")) { 959 InspectorUtils = true; 960 } else if (JS_LinearStringEqualsLiteral(nameStr, "MessageChannel")) { 961 MessageChannel = true; 962 } else if (JS_LinearStringEqualsLiteral(nameStr, "MIDIInputMap")) { 963 MIDIInputMap = true; 964 } else if (JS_LinearStringEqualsLiteral(nameStr, "MIDIOutputMap")) { 965 MIDIOutputMap = true; 966 } else if (JS_LinearStringEqualsLiteral(nameStr, "Node")) { 967 Node = true; 968 } else if (JS_LinearStringEqualsLiteral(nameStr, "NodeFilter")) { 969 NodeFilter = true; 970 } else if (JS_LinearStringEqualsLiteral(nameStr, "PathUtils")) { 971 PathUtils = true; 972 } else if (JS_LinearStringEqualsLiteral(nameStr, "Performance")) { 973 Performance = true; 974 } else if (JS_LinearStringEqualsLiteral(nameStr, "PromiseDebugging")) { 975 PromiseDebugging = true; 976 } else if (JS_LinearStringEqualsLiteral(nameStr, "Range")) { 977 Range = true; 978 } else if (JS_LinearStringEqualsLiteral(nameStr, "Selection")) { 979 Selection = true; 980 } else if (JS_LinearStringEqualsLiteral(nameStr, "TextDecoder")) { 981 TextDecoder = true; 982 } else if (JS_LinearStringEqualsLiteral(nameStr, "TextEncoder")) { 983 TextEncoder = true; 984 } else if (JS_LinearStringEqualsLiteral(nameStr, "TrustedHTML")) { 985 TrustedHTML = true; 986 } else if (JS_LinearStringEqualsLiteral(nameStr, "TrustedScript")) { 987 TrustedScript = true; 988 } else if (JS_LinearStringEqualsLiteral(nameStr, "TrustedScriptURL")) { 989 TrustedScriptURL = true; 990 } else if (JS_LinearStringEqualsLiteral(nameStr, "URL")) { 991 URL = true; 992 } else if (JS_LinearStringEqualsLiteral(nameStr, "URLSearchParams")) { 993 URLSearchParams = true; 994 } else if (JS_LinearStringEqualsLiteral(nameStr, "XMLHttpRequest")) { 995 XMLHttpRequest = true; 996 } else if (JS_LinearStringEqualsLiteral(nameStr, "WebSocket")) { 997 WebSocket = true; 998 } else if (JS_LinearStringEqualsLiteral(nameStr, "Window")) { 999 Window = true; 1000 } else if (JS_LinearStringEqualsLiteral(nameStr, "XMLSerializer")) { 1001 XMLSerializer = true; 1002 } else if (JS_LinearStringEqualsLiteral(nameStr, "ReadableStream")) { 1003 ReadableStream = true; 1004 } else if (JS_LinearStringEqualsLiteral(nameStr, "atob")) { 1005 atob = true; 1006 } else if (JS_LinearStringEqualsLiteral(nameStr, "btoa")) { 1007 btoa = true; 1008 } else if (JS_LinearStringEqualsLiteral(nameStr, "caches")) { 1009 caches = true; 1010 } else if (JS_LinearStringEqualsLiteral(nameStr, "crypto")) { 1011 crypto = true; 1012 } else if (JS_LinearStringEqualsLiteral(nameStr, "fetch")) { 1013 fetch = true; 1014 } else if (JS_LinearStringEqualsLiteral(nameStr, "storage")) { 1015 storage = true; 1016 } else if (JS_LinearStringEqualsLiteral(nameStr, "structuredClone")) { 1017 structuredClone = true; 1018 } else if (JS_LinearStringEqualsLiteral(nameStr, "locks")) { 1019 locks = true; 1020 } else if (JS_LinearStringEqualsLiteral(nameStr, "indexedDB")) { 1021 indexedDB = true; 1022 } else if (JS_LinearStringEqualsLiteral(nameStr, "isSecureContext")) { 1023 isSecureContext = true; 1024 #ifdef MOZ_WEBRTC 1025 } else if (JS_LinearStringEqualsLiteral(nameStr, "rtcIdentityProvider")) { 1026 rtcIdentityProvider = true; 1027 #endif 1028 } else { 1029 RootedString nameStr(cx, nameValue.toString()); 1030 JS::UniqueChars name = JS_EncodeStringToUTF8(cx, nameStr); 1031 if (!name) { 1032 return false; 1033 } 1034 1035 JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.get()); 1036 return false; 1037 } 1038 } 1039 return true; 1040 } 1041 1042 bool xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj) { 1043 MOZ_ASSERT(js::GetContextCompartment(cx) == JS::GetCompartment(obj)); 1044 // Properties will be exposed to System automatically but not to Sandboxes 1045 // if |[Exposed=System]| is specified. 1046 // This function holds common properties not exposed automatically but able 1047 // to be requested either in |Cu.importGlobalProperties| or 1048 // |wantGlobalProperties| of a sandbox. 1049 1050 #define DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(_iface) \ 1051 if ((_iface) && !dom::_iface##_Binding::CreateAndDefineOnGlobal(cx)) { \ 1052 return false; \ 1053 } 1054 1055 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(AbortController) 1056 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(ChromeUtils) 1057 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Blob) 1058 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(CSS) 1059 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(CSSPositionTryDescriptors) 1060 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(CSSRule) 1061 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(CustomStateSet) 1062 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Directory) 1063 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Document) 1064 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(DOMException) 1065 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(DOMParser) 1066 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(DOMTokenList) 1067 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Element) 1068 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Event) 1069 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(File) 1070 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(FileReader) 1071 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(FormData) 1072 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Headers) 1073 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(IOUtils) 1074 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(InspectorCSSParser) 1075 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(InspectorUtils) 1076 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(MessageChannel) 1077 if (MessageChannel && !MessagePort_Binding::CreateAndDefineOnGlobal(cx)) { 1078 return false; 1079 } 1080 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(MIDIInputMap) 1081 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(MIDIOutputMap) 1082 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Node) 1083 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(NodeFilter) 1084 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(PathUtils) 1085 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Performance) 1086 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(PromiseDebugging) 1087 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Range) 1088 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(ReadableStream) 1089 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Selection) 1090 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TextDecoder) 1091 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TextEncoder) 1092 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TrustedHTML) 1093 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TrustedScript) 1094 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(TrustedScriptURL) 1095 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(URL) 1096 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(URLSearchParams) 1097 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(WebSocket) 1098 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(Window) 1099 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(XMLHttpRequest) 1100 DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE(XMLSerializer) 1101 1102 #undef DEFINE_WEBIDL_INTERFACE_OR_NAMESPACE 1103 1104 if (atob && !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0)) return false; 1105 1106 if (btoa && !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0)) return false; 1107 1108 if (caches && !dom::cache::CacheStorage::DefineCachesForSandbox(cx, obj)) { 1109 return false; 1110 } 1111 1112 if (crypto && !SandboxCreateCrypto(cx, obj)) { 1113 return false; 1114 } 1115 1116 if (fetch && !SandboxCreateFetch(cx, obj)) { 1117 return false; 1118 } 1119 1120 if (storage && !SandboxCreateStorage(cx, obj)) { 1121 return false; 1122 } 1123 1124 if (structuredClone && !SandboxCreateStructuredClone(cx, obj)) { 1125 return false; 1126 } 1127 1128 if (locks && !SandboxCreateLocks(cx, obj)) { 1129 return false; 1130 } 1131 1132 // Note that isSecureContext here doesn't mean the context is actually secure 1133 // - just that the caller wants the property defined 1134 if (isSecureContext) { 1135 bool hasSecureContext = IsSecureContextOrObjectIsFromSecureContext(cx, obj); 1136 JS::Rooted<JS::Value> secureJsValue(cx, JS::BooleanValue(hasSecureContext)); 1137 return JS_DefineProperty(cx, obj, "isSecureContext", secureJsValue, 1138 JSPROP_ENUMERATE); 1139 } 1140 1141 #ifdef MOZ_WEBRTC 1142 if (rtcIdentityProvider && !SandboxCreateRTCIdentityProvider(cx, obj)) { 1143 return false; 1144 } 1145 #endif 1146 1147 return true; 1148 } 1149 1150 bool xpc::GlobalProperties::DefineInXPCComponents(JSContext* cx, 1151 JS::HandleObject obj) { 1152 if (indexedDB && !IndexedDatabaseManager::DefineIndexedDB(cx, obj)) 1153 return false; 1154 1155 return Define(cx, obj); 1156 } 1157 1158 bool xpc::GlobalProperties::DefineInSandbox(JSContext* cx, 1159 JS::HandleObject obj) { 1160 MOZ_ASSERT(IsSandbox(obj)); 1161 MOZ_ASSERT(js::GetContextCompartment(cx) == JS::GetCompartment(obj)); 1162 1163 if (indexedDB && !(IndexedDatabaseManager::ResolveSandboxBinding(cx) && 1164 IndexedDatabaseManager::DefineIndexedDB(cx, obj))) 1165 return false; 1166 1167 return Define(cx, obj); 1168 } 1169 1170 nsresult SetSandboxCSP(nsISupports* prinOrSop, const nsAString& cspString) { 1171 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop); 1172 if (!principal) { 1173 return NS_ERROR_INVALID_ARG; 1174 } 1175 auto* basePrin = BasePrincipal::Cast(principal); 1176 if (!basePrin->Is<ExpandedPrincipal>()) { 1177 return NS_ERROR_INVALID_ARG; 1178 } 1179 auto* expanded = basePrin->As<ExpandedPrincipal>(); 1180 1181 nsCOMPtr<nsIContentSecurityPolicy> csp; 1182 1183 // The choice of self-uri (self-origin) to use in a Sandbox depends on the 1184 // use case. For now, we default to a non-existing URL because there is no 1185 // use case that requires 'self' to have a particular value. Consumers can 1186 // always specify the URL explicitly instead of 'self'. Besides, the CSP 1187 // enforcement in a Sandbox is barely implemented, except for eval()-like 1188 // execution. 1189 // 1190 // moz-extension:-resources are never blocked by CSP because the protocol is 1191 // registered with URI_IS_LOCAL_RESOURCE and subjectToCSP in nsCSPService.cpp 1192 // therefore allows the load. This matches the CSP spec, which explicitly 1193 // states that CSP should not interfere with addons. Because of this, we do 1194 // not need to set selfURI to the real moz-extension:-URL, even in cases 1195 // where we want to restrict all scripts except for moz-extension:-URLs. 1196 nsCOMPtr<nsIURI> selfURI; 1197 MOZ_TRY(NS_NewURI(getter_AddRefs(selfURI), "moz-extension://dummy"_ns)); 1198 1199 #ifdef MOZ_DEBUG 1200 // Bug 1548468: Move CSP off ExpandedPrincipal 1201 expanded->GetCsp(getter_AddRefs(csp)); 1202 if (csp) { 1203 uint32_t count = 0; 1204 csp->GetPolicyCount(&count); 1205 if (count > 0) { 1206 // Ensure that the policy was not already added. 1207 nsAutoString parsedPolicyStr; 1208 for (uint32_t i = 0; i < count; i++) { 1209 csp->GetPolicyString(i, parsedPolicyStr); 1210 MOZ_ASSERT(!parsedPolicyStr.Equals(cspString)); 1211 } 1212 } 1213 } 1214 #endif 1215 1216 // Create a clone of the expanded principal to be used for the call to 1217 // SetRequestContextWithPrincipal (to prevent the CSP and expanded 1218 // principal instances to keep each other alive indefinitely, see 1219 // Bug 1741600). 1220 // 1221 // This may not be necessary anymore once Bug 1548468 will move CSP 1222 // off ExpandedPrincipal. 1223 RefPtr<ExpandedPrincipal> clonedPrincipal = ExpandedPrincipal::Create( 1224 expanded->AllowList(), expanded->OriginAttributesRef()); 1225 MOZ_ASSERT(clonedPrincipal); 1226 1227 csp = new nsCSPContext(); 1228 MOZ_TRY( 1229 csp->SetRequestContextWithPrincipal(clonedPrincipal, selfURI, ""_ns, 0)); 1230 1231 MOZ_TRY(csp->AppendPolicy(cspString, false, false)); 1232 1233 expanded->SetCsp(csp); 1234 return NS_OK; 1235 } 1236 1237 nsresult xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, 1238 nsISupports* prinOrSop, 1239 SandboxOptions& options) { 1240 // Create the sandbox global object 1241 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop); 1242 nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(prinOrSop); 1243 if (obj) { 1244 nsGlobalWindowInner* window = 1245 WindowOrNull(js::UncheckedUnwrap(obj->GetGlobalJSObject(), false)); 1246 // If we have a secure context window inherit from it's parent 1247 if (window && window->IsSecureContext()) { 1248 options.forceSecureContext = true; 1249 } 1250 } 1251 if (!principal) { 1252 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop); 1253 if (sop) { 1254 principal = sop->GetPrincipal(); 1255 } else { 1256 RefPtr<NullPrincipal> nullPrin = 1257 NullPrincipal::CreateWithoutOriginAttributes(); 1258 principal = nullPrin; 1259 } 1260 } 1261 MOZ_ASSERT(principal); 1262 1263 JS::RealmOptions realmOptions; 1264 1265 auto& creationOptions = realmOptions.creationOptions(); 1266 1267 bool isSystemPrincipal = principal->IsSystemPrincipal(); 1268 1269 if (isSystemPrincipal) { 1270 options.forceSecureContext = true; 1271 } 1272 1273 // If we are able to see [SecureContext] API code 1274 if (options.forceSecureContext) { 1275 creationOptions.setSecureContext(true); 1276 } 1277 1278 xpc::SetPrefableRealmOptions(realmOptions); 1279 if (options.sameZoneAs) { 1280 creationOptions.setNewCompartmentInExistingZone( 1281 js::UncheckedUnwrap(options.sameZoneAs)); 1282 } else if (options.freshZone) { 1283 creationOptions.setNewCompartmentAndZone(); 1284 } else if (isSystemPrincipal && !options.invisibleToDebugger && 1285 !options.freshCompartment) { 1286 // Use a shared system compartment for system-principal sandboxes that don't 1287 // require invisibleToDebugger (this is a compartment property, see bug 1288 // 1482215). 1289 creationOptions.setExistingCompartment(xpc::PrivilegedJunkScope()); 1290 } else { 1291 creationOptions.setNewCompartmentInSystemZone(); 1292 } 1293 1294 if (options.alwaysUseFdlibm) { 1295 creationOptions.setAlwaysUseFdlibm(true); 1296 } 1297 1298 creationOptions.setInvisibleToDebugger(options.invisibleToDebugger) 1299 .setTrace(TraceXPCGlobal); 1300 1301 realmOptions.behaviors().setDiscardSource(options.discardSource); 1302 1303 if (isSystemPrincipal) { 1304 realmOptions.behaviors().setClampAndJitterTime(false); 1305 } 1306 1307 if (obj) { 1308 nsGlobalWindowInner* window = 1309 WindowOrNull(js::UncheckedUnwrap(obj->GetGlobalJSObject(), false)); 1310 if (window) { 1311 const nsCString& localeOverride = 1312 window->GetBrowsingContext()->Top()->GetLanguageOverride(); 1313 if (!localeOverride.IsEmpty()) { 1314 realmOptions.behaviors().setLocaleOverride( 1315 PromiseFlatCString(localeOverride).get()); 1316 } 1317 1318 const nsAString& timezoneOverride = 1319 window->GetBrowsingContext()->Top()->GetTimezoneOverride(); 1320 if (!timezoneOverride.IsEmpty()) { 1321 realmOptions.behaviors().setTimeZoneOverride( 1322 NS_ConvertUTF16toUTF8(timezoneOverride).get()); 1323 } 1324 } 1325 } 1326 1327 const JSClass* clasp = &SandboxClass; 1328 1329 RootedObject sandbox( 1330 cx, xpc::CreateGlobalObject(cx, clasp, principal, realmOptions)); 1331 if (!sandbox) { 1332 return NS_ERROR_FAILURE; 1333 } 1334 1335 // Use exclusive expandos for non-system-principal sandboxes. 1336 bool hasExclusiveExpandos = !isSystemPrincipal; 1337 1338 // Set up the wantXrays flag, which indicates whether xrays are desired even 1339 // for same-origin access. 1340 // 1341 // This flag has historically been ignored for chrome sandboxes due to 1342 // quirks in the wrapping implementation that have now been removed. Indeed, 1343 // same-origin Xrays for chrome->chrome access seems a bit superfluous. 1344 // Arguably we should just flip the default for chrome and still honor the 1345 // flag, but such a change would break code in subtle ways for minimal 1346 // benefit. So we just switch it off here. 1347 bool wantXrays = AccessCheck::isChrome(sandbox) ? false : options.wantXrays; 1348 1349 if (creationOptions.compartmentSpecifier() == 1350 JS::CompartmentSpecifier::ExistingCompartment) { 1351 // Make sure the compartment we're reusing has flags that match what we 1352 // would set on a new compartment. 1353 CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox); 1354 MOZ_RELEASE_ASSERT(priv->allowWaivers == options.allowWaivers); 1355 MOZ_RELEASE_ASSERT(priv->isWebExtensionContentScript == 1356 options.isWebExtensionContentScript); 1357 MOZ_RELEASE_ASSERT(priv->isUAWidgetCompartment == options.isUAWidgetScope); 1358 MOZ_RELEASE_ASSERT(priv->hasExclusiveExpandos == hasExclusiveExpandos); 1359 MOZ_RELEASE_ASSERT(priv->wantXrays == wantXrays); 1360 } else { 1361 CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox); 1362 priv->allowWaivers = options.allowWaivers; 1363 priv->isWebExtensionContentScript = options.isWebExtensionContentScript; 1364 priv->isUAWidgetCompartment = options.isUAWidgetScope; 1365 priv->hasExclusiveExpandos = hasExclusiveExpandos; 1366 priv->wantXrays = wantXrays; 1367 } 1368 1369 { 1370 JSAutoRealm ar(cx, sandbox); 1371 1372 // This creates a SandboxPrivate and passes ownership of it to |sandbox|. 1373 SandboxPrivate::Create(principal, sandbox); 1374 1375 // Ensure |Object.prototype| is instantiated before prototype- 1376 // splicing below. 1377 if (!JS::GetRealmObjectPrototype(cx)) { 1378 return NS_ERROR_XPC_UNEXPECTED; 1379 } 1380 1381 if (options.proto) { 1382 bool ok = JS_WrapObject(cx, &options.proto); 1383 if (!ok) { 1384 return NS_ERROR_XPC_UNEXPECTED; 1385 } 1386 1387 // Now check what sort of thing we've got in |proto|, and figure out 1388 // if we need a SandboxProxyHandler. 1389 // 1390 // Note that, in the case of a window, we can't require that the 1391 // Sandbox subsumes the prototype, because we have to hold our 1392 // reference to it via an outer window, and the window may navigate 1393 // at any time. So we have to handle that case separately. 1394 bool useSandboxProxy = 1395 !!WindowOrNull(js::UncheckedUnwrap(options.proto, false)); 1396 if (!useSandboxProxy) { 1397 // We just wrapped options.proto into the compartment of whatever Realm 1398 // is on the cx, so use that same realm for the CheckedUnwrapDynamic 1399 // call. 1400 JSObject* unwrappedProto = 1401 js::CheckedUnwrapDynamic(options.proto, cx, false); 1402 if (!unwrappedProto) { 1403 JS_ReportErrorASCII(cx, "Sandbox must subsume sandboxPrototype"); 1404 return NS_ERROR_INVALID_ARG; 1405 } 1406 const JSClass* unwrappedClass = JS::GetClass(unwrappedProto); 1407 useSandboxProxy = unwrappedClass->isWrappedNative() || 1408 mozilla::dom::IsDOMClass(unwrappedClass); 1409 } 1410 1411 if (useSandboxProxy) { 1412 // Wrap it up in a proxy that will do the right thing in terms 1413 // of this-binding for methods. 1414 RootedValue priv(cx, ObjectValue(*options.proto)); 1415 options.proto = 1416 js::NewProxyObject(cx, &sandboxProxyHandler, priv, nullptr); 1417 if (!options.proto) { 1418 return NS_ERROR_OUT_OF_MEMORY; 1419 } 1420 } 1421 1422 ok = JS_SetPrototype(cx, sandbox, options.proto); 1423 if (!ok) { 1424 return NS_ERROR_XPC_UNEXPECTED; 1425 } 1426 } 1427 1428 bool allowComponents = principal->IsSystemPrincipal(); 1429 if (options.wantComponents && allowComponents) { 1430 if (!ObjectScope(sandbox)->AttachComponentsObject(cx)) { 1431 return NS_ERROR_XPC_UNEXPECTED; 1432 } 1433 1434 if (!ObjectScope(sandbox)->AttachJSServices(cx)) { 1435 return NS_ERROR_XPC_UNEXPECTED; 1436 } 1437 } 1438 1439 if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox)) { 1440 return NS_ERROR_XPC_UNEXPECTED; 1441 } 1442 1443 if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions)) { 1444 return NS_ERROR_XPC_UNEXPECTED; 1445 } 1446 1447 if (options.wantExportHelpers && 1448 (!JS_DefineFunction(cx, sandbox, "exportFunction", 1449 SandboxExportFunction, 3, 0) || 1450 !JS_DefineFunction(cx, sandbox, "createObjectIn", 1451 SandboxCreateObjectIn, 2, 0) || 1452 !JS_DefineFunction(cx, sandbox, "cloneInto", SandboxCloneInto, 3, 0) || 1453 !JS_DefineFunction(cx, sandbox, "isProxy", SandboxIsProxy, 1, 0))) 1454 return NS_ERROR_XPC_UNEXPECTED; 1455 1456 if (!options.globalProperties.DefineInSandbox(cx, sandbox)) { 1457 return NS_ERROR_XPC_UNEXPECTED; 1458 } 1459 } 1460 1461 // We handle the case where the context isn't in a compartment for the 1462 // benefit of UnprivilegedJunkScope(). 1463 vp.setObject(*sandbox); 1464 if (js::GetContextCompartment(cx) && !JS_WrapValue(cx, vp)) { 1465 return NS_ERROR_UNEXPECTED; 1466 } 1467 1468 // Set the location information for the new global, so that tools like 1469 // about:memory may use that information 1470 xpc::SetLocationForGlobal(sandbox, options.sandboxName); 1471 1472 nsresult rv = xpc::SetSandboxMetadata(cx, sandbox, options.metadata); 1473 if (NS_WARN_IF(NS_FAILED(rv))) { 1474 return rv; 1475 } 1476 1477 JSAutoRealm ar(cx, sandbox); 1478 JS_FireOnNewGlobalObject(cx, sandbox); 1479 1480 return NS_OK; 1481 } 1482 1483 NS_IMETHODIMP 1484 nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative* wrapper, 1485 JSContext* cx, JSObject* objArg, 1486 const CallArgs& args, bool* _retval) { 1487 RootedObject obj(cx, objArg); 1488 return CallOrConstruct(wrapper, cx, obj, args, _retval); 1489 } 1490 1491 NS_IMETHODIMP 1492 nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative* wrapper, 1493 JSContext* cx, JSObject* objArg, 1494 const CallArgs& args, bool* _retval) { 1495 RootedObject obj(cx, objArg); 1496 return CallOrConstruct(wrapper, cx, obj, args, _retval); 1497 } 1498 1499 /* 1500 * For sandbox constructor the first argument can be a URI string in which case 1501 * we use the related Content Principal for the sandbox. 1502 */ 1503 bool ParsePrincipal(JSContext* cx, HandleString contentUrl, 1504 const OriginAttributes& aAttrs, nsIPrincipal** principal) { 1505 MOZ_ASSERT(principal); 1506 MOZ_ASSERT(contentUrl); 1507 nsCOMPtr<nsIURI> uri; 1508 nsAutoJSString contentStr; 1509 NS_ENSURE_TRUE(contentStr.init(cx, contentUrl), false); 1510 nsresult rv = NS_NewURI(getter_AddRefs(uri), contentStr); 1511 if (NS_FAILED(rv)) { 1512 JS_ReportErrorASCII(cx, "Creating URI from string failed"); 1513 return false; 1514 } 1515 1516 // We could allow passing in the app-id and browser-element info to the 1517 // sandbox constructor. But creating a sandbox based on a string is a 1518 // deprecated API so no need to add features to it. 1519 nsCOMPtr<nsIPrincipal> prin = 1520 BasePrincipal::CreateContentPrincipal(uri, aAttrs); 1521 prin.forget(principal); 1522 1523 if (!*principal) { 1524 JS_ReportErrorASCII(cx, "Creating Principal from URI failed"); 1525 return false; 1526 } 1527 return true; 1528 } 1529 1530 /* 1531 * For sandbox constructor the first argument can be a principal object or 1532 * a script object principal (Document, Window). 1533 */ 1534 static bool GetPrincipalOrSOP(JSContext* cx, HandleObject from, 1535 nsISupports** out) { 1536 MOZ_ASSERT(out); 1537 *out = nullptr; 1538 1539 // We might have a Window here, so need ReflectorToISupportsDynamic 1540 nsCOMPtr<nsISupports> native = ReflectorToISupportsDynamic(from, cx); 1541 1542 if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native)) { 1543 sop.forget(out); 1544 return true; 1545 } 1546 1547 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(native); 1548 principal.forget(out); 1549 NS_ENSURE_TRUE(*out, false); 1550 1551 return true; 1552 } 1553 1554 /* 1555 * The first parameter of the sandbox constructor might be an array of 1556 * principals, either in string format or actual objects (see GetPrincipalOrSOP) 1557 */ 1558 static bool GetExpandedPrincipal(JSContext* cx, HandleObject arrayObj, 1559 const SandboxOptions& options, 1560 nsIExpandedPrincipal** out) { 1561 MOZ_ASSERT(out); 1562 uint32_t length; 1563 1564 if (!JS::GetArrayLength(cx, arrayObj, &length)) { 1565 return false; 1566 } 1567 if (!length) { 1568 // We need a whitelist of principals or uri strings to create an 1569 // expanded principal, if we got an empty array or something else 1570 // report error. 1571 JS_ReportErrorASCII(cx, "Expected an array of URI strings"); 1572 return false; 1573 } 1574 1575 nsTArray<nsCOMPtr<nsIPrincipal>> allowedDomains(length); 1576 allowedDomains.SetLength(length); 1577 1578 // If an originAttributes option has been specified, we will use that as the 1579 // OriginAttribute of all of the string arguments passed to this function. 1580 // Otherwise, we will use the OriginAttributes of a principal or SOP object 1581 // in the array, if any. If no such object is present, and all we have are 1582 // strings, then we will use a default OriginAttribute. 1583 // Otherwise, we will use the origin attributes of the passed object(s). If 1584 // more than one object is specified, we ensure that the OAs match. 1585 Maybe<OriginAttributes> attrs; 1586 if (options.originAttributes) { 1587 attrs.emplace(); 1588 JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes)); 1589 if (!attrs->Init(cx, val)) { 1590 // The originAttributes option, if specified, must be valid! 1591 JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object"); 1592 return false; 1593 } 1594 } 1595 1596 // Now we go over the array in two passes. In the first pass, we ignore 1597 // strings, and only process objects. Assuming that no originAttributes 1598 // option has been passed, if we encounter a principal or SOP object, we 1599 // grab its OA and save it if it's the first OA encountered, otherwise 1600 // check to make sure that it is the same as the OA found before. 1601 // In the second pass, we ignore objects, and use the OA found in pass 0 1602 // (or the previously computed OA if we have obtained it from the options) 1603 // to construct content principals. 1604 // 1605 // The effective OA selected above will also be set as the OA of the 1606 // expanded principal object. 1607 1608 // First pass: 1609 for (uint32_t i = 0; i < length; ++i) { 1610 RootedValue allowed(cx); 1611 if (!JS_GetElement(cx, arrayObj, i, &allowed)) { 1612 return false; 1613 } 1614 1615 nsCOMPtr<nsIPrincipal> principal; 1616 if (allowed.isObject()) { 1617 // In case of object let's see if it's a Principal or a 1618 // ScriptObjectPrincipal. 1619 nsCOMPtr<nsISupports> prinOrSop; 1620 RootedObject obj(cx, &allowed.toObject()); 1621 if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop))) { 1622 return false; 1623 } 1624 1625 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop)); 1626 principal = do_QueryInterface(prinOrSop); 1627 if (sop) { 1628 principal = sop->GetPrincipal(); 1629 } 1630 NS_ENSURE_TRUE(principal, false); 1631 1632 if (!options.originAttributes) { 1633 const OriginAttributes prinAttrs = principal->OriginAttributesRef(); 1634 if (attrs.isNothing()) { 1635 attrs.emplace(prinAttrs); 1636 } else if (prinAttrs != attrs.ref()) { 1637 // If attrs is from a previously encountered principal in the 1638 // array, we need to ensure that it matches the OA of the 1639 // principal we have here. 1640 // If attrs comes from OriginAttributes, we don't need 1641 // this check. 1642 return false; 1643 } 1644 } 1645 1646 // We do not allow ExpandedPrincipals to contain any system principals. 1647 bool isSystem = principal->IsSystemPrincipal(); 1648 if (isSystem) { 1649 JS_ReportErrorASCII( 1650 cx, "System principal is not allowed in an expanded principal"); 1651 return false; 1652 } 1653 allowedDomains[i] = principal; 1654 } else if (allowed.isString()) { 1655 // Skip any string arguments - we handle them in the next pass. 1656 } else { 1657 // Don't know what this is. 1658 return false; 1659 } 1660 } 1661 1662 if (attrs.isNothing()) { 1663 // If no OriginAttributes was found in the first pass, fall back to a 1664 // default one. 1665 attrs.emplace(); 1666 } 1667 1668 // Second pass: 1669 for (uint32_t i = 0; i < length; ++i) { 1670 RootedValue allowed(cx); 1671 if (!JS_GetElement(cx, arrayObj, i, &allowed)) { 1672 return false; 1673 } 1674 1675 nsCOMPtr<nsIPrincipal> principal; 1676 if (allowed.isString()) { 1677 // In case of string let's try to fetch a content principal from it. 1678 RootedString str(cx, allowed.toString()); 1679 1680 // attrs here is either a default OriginAttributes in case the 1681 // originAttributes option isn't specified, and no object in the array 1682 // provides a principal. Otherwise it's either the forced principal, or 1683 // the principal found before, so we can use it here. 1684 if (!ParsePrincipal(cx, str, attrs.ref(), getter_AddRefs(principal))) { 1685 return false; 1686 } 1687 NS_ENSURE_TRUE(principal, false); 1688 allowedDomains[i] = principal; 1689 } else { 1690 MOZ_ASSERT(allowed.isObject()); 1691 } 1692 } 1693 1694 RefPtr<ExpandedPrincipal> result = 1695 ExpandedPrincipal::Create(allowedDomains, attrs.ref()); 1696 result.forget(out); 1697 return true; 1698 } 1699 1700 /* 1701 * Helper that tries to get a property from the options object. 1702 */ 1703 bool OptionsBase::ParseValue(const char* name, MutableHandleValue prop, 1704 bool* aFound) { 1705 bool found; 1706 bool ok = JS_HasProperty(mCx, mObject, name, &found); 1707 NS_ENSURE_TRUE(ok, false); 1708 1709 if (aFound) { 1710 *aFound = found; 1711 } 1712 1713 if (!found) { 1714 return true; 1715 } 1716 1717 return JS_GetProperty(mCx, mObject, name, prop); 1718 } 1719 1720 /* 1721 * Helper that tries to get a boolean property from the options object. 1722 */ 1723 bool OptionsBase::ParseBoolean(const char* name, bool* prop) { 1724 MOZ_ASSERT(prop); 1725 RootedValue value(mCx); 1726 bool found; 1727 bool ok = ParseValue(name, &value, &found); 1728 NS_ENSURE_TRUE(ok, false); 1729 1730 if (!found) { 1731 return true; 1732 } 1733 1734 if (!value.isBoolean()) { 1735 JS_ReportErrorASCII(mCx, "Expected a boolean value for property %s", name); 1736 return false; 1737 } 1738 1739 *prop = value.toBoolean(); 1740 return true; 1741 } 1742 1743 /* 1744 * Helper that tries to get an object property from the options object. 1745 */ 1746 bool OptionsBase::ParseObject(const char* name, MutableHandleObject prop) { 1747 RootedValue value(mCx); 1748 bool found; 1749 bool ok = ParseValue(name, &value, &found); 1750 NS_ENSURE_TRUE(ok, false); 1751 1752 if (!found) { 1753 return true; 1754 } 1755 1756 if (!value.isObject()) { 1757 JS_ReportErrorASCII(mCx, "Expected an object value for property %s", name); 1758 return false; 1759 } 1760 prop.set(&value.toObject()); 1761 return true; 1762 } 1763 1764 /* 1765 * Helper that tries to get an object property from the options object. 1766 */ 1767 bool OptionsBase::ParseJSString(const char* name, MutableHandleString prop) { 1768 RootedValue value(mCx); 1769 bool found; 1770 bool ok = ParseValue(name, &value, &found); 1771 NS_ENSURE_TRUE(ok, false); 1772 1773 if (!found) { 1774 return true; 1775 } 1776 1777 if (!value.isString()) { 1778 JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name); 1779 return false; 1780 } 1781 prop.set(value.toString()); 1782 return true; 1783 } 1784 1785 /* 1786 * Helper that tries to get a string property from the options object. 1787 */ 1788 bool OptionsBase::ParseString(const char* name, nsCString& prop) { 1789 RootedValue value(mCx); 1790 bool found; 1791 bool ok = ParseValue(name, &value, &found); 1792 NS_ENSURE_TRUE(ok, false); 1793 1794 if (!found) { 1795 return true; 1796 } 1797 1798 if (!value.isString()) { 1799 JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name); 1800 return false; 1801 } 1802 1803 JS::UniqueChars tmp = JS_EncodeStringToLatin1(mCx, value.toString()); 1804 NS_ENSURE_TRUE(tmp, false); 1805 prop.Assign(tmp.get(), strlen(tmp.get())); 1806 return true; 1807 } 1808 1809 /* 1810 * Helper that tries to get a string property from the options object. 1811 */ 1812 bool OptionsBase::ParseString(const char* name, nsString& prop) { 1813 RootedValue value(mCx); 1814 bool found; 1815 bool ok = ParseValue(name, &value, &found); 1816 NS_ENSURE_TRUE(ok, false); 1817 1818 if (!found) { 1819 return true; 1820 } 1821 1822 if (!value.isString()) { 1823 JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name); 1824 return false; 1825 } 1826 1827 nsAutoJSString strVal; 1828 if (!strVal.init(mCx, value.toString())) { 1829 return false; 1830 } 1831 1832 prop = strVal; 1833 return true; 1834 } 1835 1836 /* 1837 * Helper that tries to get a string property from the options object. 1838 */ 1839 bool OptionsBase::ParseOptionalString(const char* name, Maybe<nsString>& prop) { 1840 RootedValue value(mCx); 1841 bool found; 1842 bool ok = ParseValue(name, &value, &found); 1843 NS_ENSURE_TRUE(ok, false); 1844 1845 if (!found || value.isUndefined()) { 1846 return true; 1847 } 1848 1849 if (!value.isString()) { 1850 JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name); 1851 return false; 1852 } 1853 1854 nsAutoJSString strVal; 1855 if (!strVal.init(mCx, value.toString())) { 1856 return false; 1857 } 1858 1859 prop = Some(strVal); 1860 return true; 1861 } 1862 1863 /* 1864 * Helper that tries to get jsid property from the options object. 1865 */ 1866 bool OptionsBase::ParseId(const char* name, MutableHandleId prop) { 1867 RootedValue value(mCx); 1868 bool found; 1869 bool ok = ParseValue(name, &value, &found); 1870 NS_ENSURE_TRUE(ok, false); 1871 1872 if (!found) { 1873 return true; 1874 } 1875 1876 return JS_ValueToId(mCx, value, prop); 1877 } 1878 1879 /* 1880 * Helper that tries to get a uint32_t property from the options object. 1881 */ 1882 bool OptionsBase::ParseUInt32(const char* name, uint32_t* prop) { 1883 MOZ_ASSERT(prop); 1884 RootedValue value(mCx); 1885 bool found; 1886 bool ok = ParseValue(name, &value, &found); 1887 NS_ENSURE_TRUE(ok, false); 1888 1889 if (!found) { 1890 return true; 1891 } 1892 1893 if (!JS::ToUint32(mCx, value, prop)) { 1894 JS_ReportErrorASCII(mCx, "Expected a uint32_t value for property %s", name); 1895 return false; 1896 } 1897 1898 return true; 1899 } 1900 1901 /* 1902 * Helper that tries to get a list of DOM constructors and other helpers from 1903 * the options object. 1904 */ 1905 bool SandboxOptions::ParseGlobalProperties() { 1906 RootedValue value(mCx); 1907 bool found; 1908 bool ok = ParseValue("wantGlobalProperties", &value, &found); 1909 NS_ENSURE_TRUE(ok, false); 1910 if (!found) { 1911 return true; 1912 } 1913 1914 if (!value.isObject()) { 1915 JS_ReportErrorASCII(mCx, 1916 "Expected an array value for wantGlobalProperties"); 1917 return false; 1918 } 1919 1920 RootedObject ctors(mCx, &value.toObject()); 1921 bool isArray; 1922 if (!JS::IsArrayObject(mCx, ctors, &isArray)) { 1923 return false; 1924 } 1925 if (!isArray) { 1926 JS_ReportErrorASCII(mCx, 1927 "Expected an array value for wantGlobalProperties"); 1928 return false; 1929 } 1930 1931 return globalProperties.Parse(mCx, ctors); 1932 } 1933 1934 /* 1935 * Helper that parsing the sandbox options object (from) and sets the fields of 1936 * the incoming options struct (options). 1937 */ 1938 bool SandboxOptions::Parse() { 1939 /* All option names must be ASCII-only. */ 1940 bool ok = ParseObject("sandboxPrototype", &proto) && 1941 ParseBoolean("wantXrays", &wantXrays) && 1942 ParseBoolean("allowWaivers", &allowWaivers) && 1943 ParseBoolean("wantComponents", &wantComponents) && 1944 ParseBoolean("wantExportHelpers", &wantExportHelpers) && 1945 ParseBoolean("isWebExtensionContentScript", 1946 &isWebExtensionContentScript) && 1947 ParseBoolean("forceSecureContext", &forceSecureContext) && 1948 ParseOptionalString("sandboxContentSecurityPolicy", 1949 sandboxContentSecurityPolicy) && 1950 ParseString("sandboxName", sandboxName) && 1951 ParseObject("sameZoneAs", &sameZoneAs) && 1952 ParseBoolean("freshCompartment", &freshCompartment) && 1953 ParseBoolean("freshZone", &freshZone) && 1954 ParseBoolean("invisibleToDebugger", &invisibleToDebugger) && 1955 ParseBoolean("discardSource", &discardSource) && 1956 ParseGlobalProperties() && ParseValue("metadata", &metadata) && 1957 ParseUInt32("userContextId", &userContextId) && 1958 ParseObject("originAttributes", &originAttributes) && 1959 ParseBoolean("alwaysUseFdlibm", &alwaysUseFdlibm); 1960 if (!ok) { 1961 return false; 1962 } 1963 1964 if (freshZone && sameZoneAs) { 1965 JS_ReportErrorASCII(mCx, "Cannot use both sameZoneAs and freshZone"); 1966 return false; 1967 } 1968 1969 return true; 1970 } 1971 1972 static nsresult AssembleSandboxMemoryReporterName(JSContext* cx, 1973 nsCString& sandboxName) { 1974 // Use a default name when the caller did not provide a sandboxName. 1975 if (sandboxName.IsEmpty()) { 1976 sandboxName = "[anonymous sandbox]"_ns; 1977 } else { 1978 #ifndef DEBUG 1979 // Adding the caller location is fairly expensive, so in non-debug 1980 // builds, only add it if we don't have an explicit sandbox name. 1981 return NS_OK; 1982 #endif 1983 } 1984 1985 // Get the xpconnect native call context. 1986 XPCCallContext* cc = XPCJSContext::Get()->GetCallContext(); 1987 NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG); 1988 1989 // Get the current source info from xpc. 1990 nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack(); 1991 1992 // Append the caller's location information. 1993 if (frame) { 1994 nsAutoCString location; 1995 frame->GetFilename(cx, location); 1996 int32_t lineNumber = frame->GetLineNumber(cx); 1997 1998 sandboxName.AppendLiteral(" (from: "); 1999 sandboxName.Append(location); 2000 sandboxName.Append(':'); 2001 sandboxName.AppendInt(lineNumber); 2002 sandboxName.Append(')'); 2003 } 2004 2005 return NS_OK; 2006 } 2007 2008 // static 2009 nsresult nsXPCComponents_utils_Sandbox::CallOrConstruct( 2010 nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, 2011 const CallArgs& args, bool* _retval) { 2012 if (args.length() < 1) { 2013 return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); 2014 } 2015 2016 nsresult rv; 2017 bool ok = false; 2018 bool calledWithOptions = args.length() > 1; 2019 if (calledWithOptions && !args[1].isObject()) { 2020 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); 2021 } 2022 2023 RootedObject optionsObject(cx, 2024 calledWithOptions ? &args[1].toObject() : nullptr); 2025 2026 SandboxOptions options(cx, optionsObject); 2027 if (calledWithOptions && !options.Parse()) { 2028 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); 2029 } 2030 2031 // Make sure to set up principals on the sandbox before initing classes. 2032 nsCOMPtr<nsIPrincipal> principal; 2033 nsCOMPtr<nsIExpandedPrincipal> expanded; 2034 nsCOMPtr<nsISupports> prinOrSop; 2035 2036 if (args[0].isString()) { 2037 RootedString str(cx, args[0].toString()); 2038 OriginAttributes attrs; 2039 if (options.originAttributes) { 2040 JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes)); 2041 if (!attrs.Init(cx, val)) { 2042 // The originAttributes option, if specified, must be valid! 2043 JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object"); 2044 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); 2045 } 2046 } 2047 attrs.mUserContextId = options.userContextId; 2048 ok = ParsePrincipal(cx, str, attrs, getter_AddRefs(principal)); 2049 prinOrSop = principal; 2050 } else if (args[0].isObject()) { 2051 RootedObject obj(cx, &args[0].toObject()); 2052 bool isArray; 2053 if (!JS::IsArrayObject(cx, obj, &isArray)) { 2054 ok = false; 2055 } else if (isArray) { 2056 if (options.userContextId != 0) { 2057 // We don't support passing a userContextId with an array. 2058 ok = false; 2059 } else { 2060 ok = GetExpandedPrincipal(cx, obj, options, getter_AddRefs(expanded)); 2061 prinOrSop = expanded; 2062 } 2063 } else { 2064 ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)); 2065 } 2066 } else if (args[0].isNull()) { 2067 // Null means that we just pass prinOrSop = nullptr, and get an 2068 // NullPrincipal. 2069 ok = true; 2070 } 2071 2072 if (!ok) { 2073 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); 2074 } 2075 2076 if (options.sandboxContentSecurityPolicy.isSome()) { 2077 if (!expanded) { 2078 // CSP is currently stored on ExpandedPrincipal. If CSP moves off 2079 // ExpandedPrincipal (bug 1548468), then we can drop/relax this check. 2080 JS_ReportErrorASCII(cx, 2081 "sandboxContentSecurityPolicy is currently only " 2082 "supported with ExpandedPrincipals"); 2083 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); 2084 } 2085 rv = SetSandboxCSP(prinOrSop, options.sandboxContentSecurityPolicy.value()); 2086 if (NS_WARN_IF(NS_FAILED(rv))) { 2087 return rv; 2088 } 2089 } 2090 2091 if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName))) { 2092 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval); 2093 } 2094 2095 if (options.metadata.isNullOrUndefined()) { 2096 // If the caller is running in a sandbox, inherit. 2097 RootedObject callerGlobal(cx, JS::GetScriptedCallerGlobal(cx)); 2098 if (IsSandbox(callerGlobal)) { 2099 rv = GetSandboxMetadata(cx, callerGlobal, &options.metadata); 2100 if (NS_WARN_IF(NS_FAILED(rv))) { 2101 return rv; 2102 } 2103 } 2104 } 2105 2106 rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options); 2107 2108 if (NS_FAILED(rv)) { 2109 return ThrowAndFail(rv, cx, _retval); 2110 } 2111 2112 *_retval = true; 2113 return NS_OK; 2114 } 2115 2116 nsresult xpc::EvalInSandbox(JSContext* cx, HandleObject sandboxArg, 2117 const nsAString& source, const nsACString& filename, 2118 int32_t lineNo, bool enforceFilenameRestrictions, 2119 MutableHandleValue rval) { 2120 JS_AbortIfWrongThread(cx); 2121 rval.set(UndefinedValue()); 2122 2123 bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg); 2124 // CheckedUnwrapStatic is fine here, since we're checking for "is it a 2125 // sandbox". 2126 RootedObject sandbox(cx, js::CheckedUnwrapStatic(sandboxArg)); 2127 if (!sandbox || !IsSandbox(sandbox)) { 2128 return NS_ERROR_INVALID_ARG; 2129 } 2130 2131 SandboxPrivate* priv = SandboxPrivate::GetPrivate(sandbox); 2132 nsIScriptObjectPrincipal* sop = priv; 2133 MOZ_ASSERT(sop, "Invalid sandbox passed"); 2134 nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal(); 2135 NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); 2136 2137 nsAutoCString filenameBuf; 2138 if (!filename.IsVoid() && filename.Length() != 0) { 2139 filenameBuf.Assign(filename); 2140 } else { 2141 // Default to the spec of the principal. 2142 nsresult rv = nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf); 2143 NS_ENSURE_SUCCESS(rv, rv); 2144 lineNo = 1; 2145 } 2146 2147 // We create a separate cx to do the sandbox evaluation. Scope it. 2148 RootedValue v(cx, UndefinedValue()); 2149 RootedValue exn(cx, UndefinedValue()); 2150 bool ok = true; 2151 { 2152 // We're about to evaluate script, so make an AutoEntryScript. 2153 // This is clearly Gecko-specific and not in any spec. 2154 mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation"); 2155 JSContext* sandcx = aes.cx(); 2156 JSAutoRealm ar(sandcx, sandbox); 2157 2158 JS::CompileOptions options(sandcx); 2159 options.setFileAndLine(filenameBuf.get(), lineNo); 2160 options.setSkipFilenameValidation(!enforceFilenameRestrictions); 2161 MOZ_ASSERT(JS_IsGlobalObject(sandbox)); 2162 2163 const nsPromiseFlatString& flat = PromiseFlatString(source); 2164 2165 JS::SourceText<char16_t> buffer; 2166 ok = buffer.init(sandcx, flat.get(), flat.Length(), 2167 JS::SourceOwnership::Borrowed) && 2168 JS::Evaluate(sandcx, options, buffer, &v); 2169 2170 // If the sandbox threw an exception, grab it off the context. 2171 if (aes.HasException()) { 2172 if (!aes.StealException(&exn)) { 2173 return NS_ERROR_OUT_OF_MEMORY; 2174 } 2175 } 2176 } 2177 2178 // 2179 // Alright, we're back on the caller's cx. If an error occured, try to 2180 // wrap and set the exception. Otherwise, wrap the return value. 2181 // 2182 2183 if (!ok) { 2184 // If we end up without an exception, it was probably due to OOM along 2185 // the way, in which case we thow. Otherwise, wrap it. 2186 if (exn.isUndefined() || !JS_WrapValue(cx, &exn)) { 2187 return NS_ERROR_OUT_OF_MEMORY; 2188 } 2189 2190 // Set the exception on our caller's cx. 2191 JS_SetPendingException(cx, exn); 2192 return NS_ERROR_FAILURE; 2193 } 2194 2195 // Transitively apply Xray waivers if |sb| was waived. 2196 if (waiveXray) { 2197 ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v); 2198 } else { 2199 ok = JS_WrapValue(cx, &v); 2200 } 2201 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); 2202 2203 // Whew! 2204 rval.set(v); 2205 return NS_OK; 2206 } 2207 2208 nsresult xpc::GetSandboxMetadata(JSContext* cx, HandleObject sandbox, 2209 MutableHandleValue rval) { 2210 MOZ_ASSERT(NS_IsMainThread()); 2211 MOZ_ASSERT(IsSandbox(sandbox)); 2212 2213 RootedValue metadata(cx); 2214 { 2215 JSAutoRealm ar(cx, sandbox); 2216 metadata = 2217 JS::GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT); 2218 } 2219 2220 if (!JS_WrapValue(cx, &metadata)) { 2221 return NS_ERROR_UNEXPECTED; 2222 } 2223 2224 rval.set(metadata); 2225 return NS_OK; 2226 } 2227 2228 nsresult xpc::SetSandboxLocaleOverride(JSContext* cx, HandleObject sandbox, 2229 const char* locale) { 2230 MOZ_ASSERT(NS_IsMainThread()); 2231 MOZ_ASSERT(IsSandbox(sandbox)); 2232 2233 JS::SetRealmLocaleOverride(JS::GetObjectRealmOrNull(sandbox), locale); 2234 2235 return NS_OK; 2236 } 2237 2238 nsresult xpc::SetSandboxTimezoneOverride(JSContext* cx, HandleObject sandbox, 2239 const char* timezone) { 2240 MOZ_ASSERT(NS_IsMainThread()); 2241 MOZ_ASSERT(IsSandbox(sandbox)); 2242 2243 JS::SetRealmTimezoneOverride(JS::GetObjectRealmOrNull(sandbox), timezone); 2244 2245 return NS_OK; 2246 } 2247 2248 nsresult xpc::SetSandboxMetadata(JSContext* cx, HandleObject sandbox, 2249 HandleValue metadataArg) { 2250 MOZ_ASSERT(NS_IsMainThread()); 2251 MOZ_ASSERT(IsSandbox(sandbox)); 2252 2253 RootedValue metadata(cx); 2254 2255 JSAutoRealm ar(cx, sandbox); 2256 if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr)) { 2257 return NS_ERROR_UNEXPECTED; 2258 } 2259 2260 JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata); 2261 2262 return NS_OK; 2263 } 2264 2265 ModuleLoaderBase* SandboxPrivate::GetModuleLoader(JSContext* aCx) { 2266 if (mModuleLoader) { 2267 return mModuleLoader; 2268 } 2269 2270 JSObject* object = GetGlobalJSObject(); 2271 nsGlobalWindowInner* sandboxWindow = xpc::SandboxWindowOrNull(object, aCx); 2272 if (!sandboxWindow) { 2273 return nullptr; 2274 } 2275 2276 ModuleLoader* mainModuleLoader = 2277 static_cast<ModuleLoader*>(sandboxWindow->GetModuleLoader(aCx)); 2278 2279 ScriptLoader* scriptLoader = mainModuleLoader->GetScriptLoader(); 2280 2281 ModuleLoader* moduleLoader = 2282 new ModuleLoader(scriptLoader, this, ModuleLoader::WebExtension); 2283 scriptLoader->RegisterContentScriptModuleLoader(moduleLoader); 2284 mModuleLoader = moduleLoader; 2285 2286 return moduleLoader; 2287 } 2288 2289 mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult> 2290 SandboxPrivate::GetStorageKey() { 2291 MOZ_ASSERT(NS_IsMainThread()); 2292 2293 mozilla::ipc::PrincipalInfo principalInfo; 2294 nsresult rv = PrincipalToPrincipalInfo(mPrincipal, &principalInfo); 2295 if (NS_WARN_IF(NS_FAILED(rv))) { 2296 return mozilla::Err(rv); 2297 } 2298 2299 // Block expanded and null principals, let content and system through. 2300 if (principalInfo.type() != 2301 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo && 2302 principalInfo.type() != 2303 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) { 2304 return Err(NS_ERROR_DOM_SECURITY_ERR); 2305 } 2306 2307 return std::move(principalInfo); 2308 }