/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* static */ bool MaybeCrossOriginObjectMixins::IsPlatformObjectSameOrigin(JSContext* cx,
JSObject* obj) {
MOZ_ASSERT(!js::IsCrossCompartmentWrapper(obj)); // WindowProxy and Window must always be same-Realm, so we can do // our IsPlatformObjectSameOrigin check against either one. But verify that // in case we have a WindowProxy the right things happen.
MOZ_ASSERT(js::GetNonCCWObjectRealm(obj) == // "true" for second arg means to unwrap WindowProxy to // get at the Window.
js::GetNonCCWObjectRealm(js::UncheckedUnwrap(obj, true)), "WindowProxy not same-Realm as Window?");
// The spec effectively has an EqualsConsideringDomain check here, // because the spec has no concept of asymmetric security // relationships. But we shouldn't ever end up here in the // asymmetric case anyway: That case should end up with Xrays, which // don't call into this code. // // Let's assert that EqualsConsideringDomain and // SubsumesConsideringDomain give the same results and use // EqualsConsideringDomain for the check we actually do, since it's // stricter and more closely matches the spec. // // That said, if the (not very well named) // OriginAttributes::IsRestrictOpenerAccessForFPI() method returns // false, we want to use FastSubsumesConsideringDomainIgnoringFPD // instead of FastEqualsConsideringDomain, because in that case we // still want to treat things which are in different first-party // contexts as same-origin.
MOZ_ASSERT(
subjectPrincipal->FastEqualsConsideringDomain(objectPrincipal) ==
subjectPrincipal->FastSubsumesConsideringDomain(objectPrincipal), "Why are we in an asymmetric case here?"); if (OriginAttributes::IsRestrictOpenerAccessForFPI()) { return subjectPrincipal->FastEqualsConsideringDomain(objectPrincipal);
}
bool MaybeCrossOriginObjectMixins::CrossOriginGetOwnPropertyHelper(
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const {
MOZ_ASSERT(!IsPlatformObjectSameOrigin(cx, obj) || IsRemoteObjectProxy(obj), "Why did we get called?"); // First check for an IDL-defined cross-origin property with the given name. // This corresponds to // https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-) // step 2.
JS::Rooted<JSObject*> holder(cx); if (!EnsureHolder(cx, obj, &holder)) { returnfalse;
}
/* static */ bool MaybeCrossOriginObjectMixins::CrossOriginGet(
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<JS::Value> receiver,
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) { // This is fairly similar to BaseProxyHandler::get, but there are some // differences. Most importantly, we want to throw if we have a descriptor // with no getter, while BaseProxyHandler::get returns undefined. The other // big difference is that we don't have to worry about prototypes (ours is // always null).
// We want to invoke [[GetOwnProperty]] on "obj", but _without_ entering its // compartment, because for the proxies we have here [[GetOwnProperty]] will // do security checks based on the current Realm. Unfortunately, // JS_GetPropertyDescriptorById asserts that compartments match. Luckily, we // know that "obj" is a proxy here, so we can directly call its // getOwnPropertyDescriptor() hook. // // It looks like Proxy::getOwnPropertyDescriptor is not public, so just grab // the handler and call its getOwnPropertyDescriptor hook directly.
MOZ_ASSERT(js::IsProxy(obj), "How did we get a bogus object here?");
MOZ_ASSERT(
js::IsWindowProxy(obj) || IsLocation(obj) || IsRemoteObjectProxy(obj), "Unexpected proxy");
MOZ_ASSERT(!IsPlatformObjectSameOrigin(cx, obj) || IsRemoteObjectProxy(obj), "Why did we get called?");
js::AssertSameCompartment(cx, receiver);
/* static */ bool MaybeCrossOriginObjectMixins::CrossOriginSet(
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
JS::ObjectOpResult& result) { // We want to invoke [[GetOwnProperty]] on "obj", but _without_ entering its // compartment, because for the proxies we have here [[GetOwnProperty]] will // do security checks based on the current Realm. Unfortunately, // JS_GetPropertyDescriptorById asserts that compartments match. Luckily, we // know that "obj" is a proxy here, so we can directly call its // getOwnPropertyDescriptor() hook. // // It looks like Proxy::getOwnPropertyDescriptor is not public, so just grab // the handler and call its getOwnPropertyDescriptor hook directly.
MOZ_ASSERT(js::IsProxy(obj), "How did we get a bogus object here?");
MOZ_ASSERT(
js::IsWindowProxy(obj) || IsLocation(obj) || IsRemoteObjectProxy(obj), "Unexpected proxy");
MOZ_ASSERT(!IsPlatformObjectSameOrigin(cx, obj) || IsRemoteObjectProxy(obj), "Why did we get called?");
js::AssertSameCompartment(cx, receiver);
js::AssertSameCompartment(cx, v);
/* static */ bool MaybeCrossOriginObjectMixins::EnsureHolder(
JSContext* cx, JS::Handle<JSObject*> obj, size_t slot, const CrossOriginProperties& properties,
JS::MutableHandle<JSObject*> holder) {
MOZ_ASSERT(!IsPlatformObjectSameOrigin(cx, obj) || IsRemoteObjectProxy(obj), "Why are we calling this at all in same-origin cases?"); // We store the holders in a weakmap stored in obj's slot. Our object is // always a proxy, so we can just go ahead and use GetProxyReservedSlot here.
JS::Rooted<JS::Value> weakMapVal(cx, js::GetProxyReservedSlot(obj, slot)); if (weakMapVal.isUndefined()) { // Enter the Realm of "obj" when we allocate the WeakMap, since we are going // to store it in a slot on "obj" and in general we may not be // same-compartment with "obj" here.
JSAutoRealm ar(cx, obj);
JSObject* newMap = JS::NewWeakMapObject(cx); if (!newMap) { returnfalse;
}
weakMapVal.setObject(*newMap);
js::SetProxyReservedSlot(obj, slot, weakMapVal);
}
MOZ_ASSERT(weakMapVal.isObject(), "How did a non-object else end up in this slot?");
JS::Rooted<JSObject*> map(cx, &weakMapVal.toObject());
MOZ_ASSERT(JS::IsWeakMapObject(map), "How did something else end up in this slot?");
// We need to be in "map"'s compartment to work with it. Per spec, the key // for this map is supposed to be the pair (current settings, relevant // settings). The current settings corresponds to the current Realm of cx. // The relevant settings corresponds to the Realm of "obj", but since all of // our objects are per-Realm singletons, we are basically using "obj" itself // as part of the key. // // To represent the current settings, we use a dedicated key object of the // current-Realm. // // We can't use the current global, because we can't get a useful // cross-compartment wrapper for it; such wrappers would always go // through a WindowProxy and would not be guarantee to keep pointing to a // single Realm when unwrapped. We want to grab this key before we start // changing Realms. // // Also we can't use arbitrary object (e.g.: Object.prototype), because at // this point those compartments are not same-origin, and don't have access to // each other, and the object retrieved here will be wrapped by a security // wrapper below, and the wrapper will be stored into the cache // (see Compartment::wrap). Those compartments can get access later by // modifying `document.domain`, and wrapping objects after that point // shouldn't result in a security wrapper. Wrap operation looks up the // existing wrapper in the cache, that contains the security wrapper created // here. We should use unique/private object here, so that this doesn't // affect later wrap operation.
JS::Rooted<JSObject*> key(cx, JS::GetRealmKeyObject(cx)); if (!key) { returnfalse;
}
JS::Rooted<JS::Value> holderVal(cx);
{ // Scope for working with the map
JSAutoRealm ar(cx, map); if (!MaybeWrapObject(cx, &key)) { returnfalse;
}
if (holderVal.isObject()) { // We want to do an unchecked unwrap, because the holder (and the current // caller) may actually be more privileged than our map.
holder.set(js::UncheckedUnwrap(&holderVal.toObject()));
// holder might be a dead object proxy if things got nuked. if (!JS_IsDeadWrapper(holder)) {
MOZ_ASSERT(js::GetContextRealm(cx) == js::GetNonCCWObjectRealm(holder), "How did we end up with a key/value mismatch?"); returntrue;
}
}
// We didn't find a usable holder. Go ahead and allocate one. At this point // we have two options: we could allocate the holder in the current Realm and // store a cross-compartment wrapper for it in the map as needed, or we could // allocate the holder in the Realm of the map and have it hold // cross-compartment references to all the methods it holds, since those // methods need to be in our current Realm. It seems better to allocate the // holder in our current Realm. bool isChrome = xpc::AccessCheck::isChrome(js::GetContextRealm(cx));
holder.set(JS_NewObjectWithGivenProto(cx, nullptr, nullptr)); if (!holder || !JS_DefineProperties(cx, holder, properties.mAttributes) ||
!JS_DefineFunctions(cx, holder, properties.mMethods) ||
(isChrome && properties.mChromeOnlyAttributes &&
!JS_DefineProperties(cx, holder, properties.mChromeOnlyAttributes)) ||
(isChrome && properties.mChromeOnlyMethods &&
!JS_DefineFunctions(cx, holder, properties.mChromeOnlyMethods))) { returnfalse;
}
holderVal.setObject(*holder);
{ // Scope for working with the map
JSAutoRealm ar(cx, map);
// Key is already in the right Realm, but we need to wrap the value. if (!MaybeWrapValue(cx, &holderVal)) { returnfalse;
}
{ // Scope for JSAutoRealm
JSAutoRealm ar(cx, proxy);
protop.set(getSameOriginPrototype(cx)); if (!protop) { returnfalse;
}
}
return MaybeWrapObject(cx, protop);
}
template <typename Base> bool MaybeCrossOriginObject<Base>::setPrototype(
JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<JSObject*> proto,
JS::ObjectOpResult& result) const { // Inlined version of // https://tc39.github.io/ecma262/#sec-set-immutable-prototype
js::AssertSameCompartment(cx, proto);
// We have to be careful how we get the prototype. In particular, we do _NOT_ // want to enter the Realm of "proxy" to do that, in case we're not // same-origin with it here.
JS::Rooted<JSObject*> wrappedProxy(cx, proxy); if (!MaybeWrapObject(cx, &wrappedProxy)) { returnfalse;
}
JS::Rooted<JSObject*> currentProto(cx); if (!js::GetObjectProto(cx, wrappedProxy, ¤tProto)) { returnfalse;
}
if (currentProto != proto) { return result.failCantSetProto();
}
return result.succeed();
}
template <typename Base> bool MaybeCrossOriginObject<Base>::getPrototypeIfOrdinary(
JSContext* cx, JS::Handle<JSObject*> proxy, bool* isOrdinary,
JS::MutableHandle<JSObject*> protop) const { // We have a custom [[GetPrototypeOf]]
*isOrdinary = false; returntrue;
}
template <typename Base> bool MaybeCrossOriginObject<Base>::setImmutablePrototype(
JSContext* cx, JS::Handle<JSObject*> proxy, bool* succeeded) const { // We just want to disallow this.
*succeeded = false; returntrue;
}
template <typename Base> bool MaybeCrossOriginObject<Base>::isExtensible(JSContext* cx,
JS::Handle<JSObject*> proxy, bool* extensible) const { // We never allow [[PreventExtensions]] to succeed.
*extensible = true; returntrue;
}
// Enter the Realm of proxy and do the remaining work in there.
JSAutoRealm ar(cx, proxy);
JS::Rooted<JS::PropertyDescriptor> descCopy(cx, desc); if (!JS_WrapPropertyDescriptor(cx, &descCopy)) { returnfalse;
}
template <typename Base> bool MaybeCrossOriginObject<Base>::enumerate(
JSContext* cx, JS::Handle<JSObject*> proxy,
JS::MutableHandleVector<jsid> props) const { // Just get the property keys from ourselves, in whatever Realm we happen to // be in. It's important to not enter the Realm of "proxy" here, because that // would affect the list of keys we claim to have. We wrap the proxy in the // current compartment just to be safe; it doesn't affect behavior as far as // CrossOriginObjectWrapper and MaybeCrossOriginObject are concerned.
JS::Rooted<JSObject*> self(cx, proxy); if (!MaybeWrapObject(cx, &self)) { returnfalse;
}
return js::GetPropertyKeys(cx, self, 0, props);
}
// Force instantiations of the out-of-line template methods we need. templateclass MaybeCrossOriginObject<js::Wrapper>; templateclass MaybeCrossOriginObject<DOMProxyHandler>;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.