diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 1d2796f0b9b0..e09f8600e430 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -397,7 +397,7 @@ bool DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const { - assertEnteredPolicy(cx, proxy, id, GET | SET); + assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR); JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. RootedObject target(cx, proxy->as().target()); return JS_GetPropertyDescriptorById(cx, target, id, desc); @@ -407,7 +407,7 @@ bool DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle desc) const { - assertEnteredPolicy(cx, proxy, id, GET | SET); + assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR); RootedObject target(cx, proxy->as().target()); return js::GetOwnPropertyDescriptor(cx, target, id, desc); } @@ -2195,7 +2195,7 @@ Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, JS_CHECK_RECURSION(cx, return false); const BaseProxyHandler *handler = proxy->as().handler(); desc.object().set(nullptr); // default result if we refuse to perform this action - AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true); if (!policy.allowed()) return policy.returnValue(); if (!handler->hasPrototype()) @@ -2226,7 +2226,7 @@ Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, const BaseProxyHandler *handler = proxy->as().handler(); desc.object().set(nullptr); // default result if we refuse to perform this action - AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); + AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true); if (!policy.allowed()) return policy.returnValue(); return handler->getOwnPropertyDescriptor(cx, proxy, id, desc); diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index b4588c4c2af5..cac5730de3e5 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -155,9 +155,9 @@ class JS_FRIEND_API(BaseProxyHandler) * may still decide to squelch the error). * * We make these OR-able so that assertEnteredPolicy can pass a union of them. - * For example, get{,Own}PropertyDescriptor is invoked by both calls to ::get() - * and ::set() (since we need to look up the accessor), so its - * assertEnteredPolicy would pass GET | SET. + * For example, get{,Own}PropertyDescriptor is invoked by calls to ::get() + * ::set(), in addition to being invoked on its own, so there are several + * valid Actions that could have been entered. */ typedef uint32_t Action; enum { @@ -165,7 +165,8 @@ class JS_FRIEND_API(BaseProxyHandler) GET = 0x01, SET = 0x02, CALL = 0x04, - ENUMERATE = 0x08 + ENUMERATE = 0x08, + GET_PROPERTY_DESCRIPTOR = 0x10 }; virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act, diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index aaf39b2bb1d3..6d58a7c369af 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -157,6 +157,13 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, JSObject *wrapperArg, j if (act == Wrapper::ENUMERATE) return true; + // For the case of getting a property descriptor, we allow if either GET or SET + // is allowed, and rely on FilteringWrapper to filter out any disallowed accessors. + if (act == Wrapper::GET_PROPERTY_DESCRIPTOR) { + return isCrossOriginAccessPermitted(cx, wrapperArg, idArg, Wrapper::GET) || + isCrossOriginAccessPermitted(cx, wrapperArg, idArg, Wrapper::SET); + } + RootedId id(cx, idArg); RootedObject wrapper(cx, wrapperArg); RootedObject obj(cx, Wrapper::wrappedObject(wrapper)); @@ -212,6 +219,14 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapperArg, jsid idArg, Wr if (act == Wrapper::CALL) return true; + + // For the case of getting a property descriptor, we allow if either GET or SET + // is allowed, and rely on FilteringWrapper to filter out any disallowed accessors. + if (act == Wrapper::GET_PROPERTY_DESCRIPTOR) { + return check(cx, wrapperArg, idArg, Wrapper::GET) || + check(cx, wrapperArg, idArg, Wrapper::SET); + } + RootedId exposedPropsId(cx, GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS)); // We need to enter the wrappee's compartment to look at __exposedProps__, diff --git a/js/xpconnect/wrappers/AccessCheck.h b/js/xpconnect/wrappers/AccessCheck.h index b8e6df2d3adb..ada5a762ed78 100644 --- a/js/xpconnect/wrappers/AccessCheck.h +++ b/js/xpconnect/wrappers/AccessCheck.h @@ -79,8 +79,9 @@ struct ExposedPropertiesOnly : public Policy { static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act); static bool deny(js::Wrapper::Action act, JS::HandleId id) { - // Fail silently for GETs and ENUMERATEs. - return act == js::Wrapper::GET || act == js::Wrapper::ENUMERATE; + // Fail silently for GET ENUMERATE, and GET_PROPERTY_DESCRIPTOR. + return act == js::Wrapper::GET || act == js::Wrapper::ENUMERATE || + act == js::Wrapper::GET_PROPERTY_DESCRIPTOR; } static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) { return false; diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp index a8aa3c0e4083..6b2d3a312b93 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp @@ -77,7 +77,7 @@ ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx, HandleId id, JS::MutableHandle desc) const { - assertEnteredPolicy(cx, wrapper, id, GET | SET); + assertEnteredPolicy(cx, wrapper, id, GET | SET | GET_PROPERTY_DESCRIPTOR); // First, try a lookup on the base wrapper if permitted. desc.object().set(nullptr); if (AllowedByBase(cx, wrapper, id, Wrapper::GET) && @@ -274,7 +274,8 @@ ChromeObjectWrapper::enter(JSContext *cx, HandleObject wrapper, return true; // COWs fail silently for GETs, and that also happens to be the only case // where we might want to redirect the lookup to the home prototype chain. - *bp = act == Wrapper::GET || act == Wrapper::ENUMERATE; + *bp = act == Wrapper::GET || act == Wrapper::ENUMERATE || + act == Wrapper::GET_PROPERTY_DESCRIPTOR; if (!*bp || id == JSID_VOID) return false; diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index 7f82a4706384..b32b650c868a 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -80,7 +80,8 @@ FilteringWrapper::getPropertyDescriptor(JSContext *cx, HandleObjec HandleId id, JS::MutableHandle desc) const { - assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET | + BaseProxyHandler::GET_PROPERTY_DESCRIPTOR); if (!Base::getPropertyDescriptor(cx, wrapper, id, desc)) return false; return FilterPropertyDescriptor(cx, wrapper, id, desc); @@ -92,7 +93,8 @@ FilteringWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleOb HandleId id, JS::MutableHandle desc) const { - assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET | + BaseProxyHandler::GET_PROPERTY_DESCRIPTOR); if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc)) return false; return FilterPropertyDescriptor(cx, wrapper, id, desc); diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index b76e2a705b12..f20143bffe3a 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -2364,7 +2364,8 @@ XrayWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wra JS::MutableHandle desc) const { - assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET | + BaseProxyHandler::GET_PROPERTY_DESCRIPTOR); RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); if (Traits::isResolving(cx, holder, id)) { desc.object().set(nullptr); @@ -2496,7 +2497,8 @@ XrayWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject JS::MutableHandle desc) const { - assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); + assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET | + BaseProxyHandler::GET_PROPERTY_DESCRIPTOR); RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); if (Traits::isResolving(cx, holder, id)) { desc.object().set(nullptr);