JSObject * WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, unsigned flags) { NS_ASSERTION(!IsWrapper(obj) || GetProxyHandler(obj) == &WaiveXrayWrapperWrapper || js::GetObjectClass(obj)->ext.innerObject, "wrapped object passed to rewrap"); NS_ASSERTION(JS_GetClass(obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); JSCompartment *origin = js::GetObjectCompartment(obj); JSCompartment *target = js::GetContextCompartment(cx); bool usingXray = false; Wrapper *wrapper; CompartmentPrivate *targetdata = static_cast<CompartmentPrivate *>(JS_GetCompartmentPrivate(target)); if (AccessCheck::isChrome(target)) { if (AccessCheck::isChrome(origin)) { wrapper = &CrossCompartmentWrapper::singleton; } else { bool isSystem; { JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return nsnull; JSObject *globalObj = JS_GetGlobalForObject(cx, obj); JS_ASSERT(globalObj); isSystem = JS_IsSystemObject(cx, globalObj); } if (isSystem) { wrapper = &CrossCompartmentWrapper::singleton; } else if (flags & WAIVE_XRAY_WRAPPER_FLAG) { // If we waived the X-ray wrapper for this object, wrap it into a // special wrapper to transitively maintain the X-ray waiver. wrapper = &CrossOriginWrapper::singleton; } else { // Native objects must be wrapped into an X-ray wrapper. bool proxy; if (CanXray(obj, &proxy)) { if (proxy) { wrapper = &XrayProxy::singleton; } else { typedef XrayWrapper<CrossCompartmentWrapper> Xray; usingXray = true; wrapper = &Xray::singleton; } } else { wrapper = &NoWaiverWrapper::singleton; } } } } else if (AccessCheck::isChrome(origin)) { JSFunction *fun = JS_GetObjectFunction(obj); if (fun) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { JS_ReportError(cx, "Not allowed to access chrome eval or Function from content"); return nsnull; } } XPCWrappedNative *wn; if (targetdata && (wn = GetWrappedNative(cx, obj)) && wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) { typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray; usingXray = true; if (IsLocationObject(obj)) wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton; else wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton; } else { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, ExposedPropertiesOnly>::singleton; } } else if (AccessCheck::isSameOrigin(origin, target)) { // For the same-origin case we use a transparent wrapper, unless one // of the following is true: // * The wrapper is a Location object. // * The wrapper is flagged as needing a SOW. // * The context compartment specifically requested Xray vision into // same-origin compartments. // // The first two cases always require a security wrapper for non-chrome // access, regardless of the origin of the object. bool proxy; if (AccessCheck::needsSystemOnlyWrapper(obj)) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OnlyIfSubjectIsSystem>::singleton; } else if (IsLocationObject(obj)) { typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray; usingXray = true; wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton; } else if (targetdata && targetdata->wantXrays && CanXray(obj, &proxy)) { if (proxy) { wrapper = &XrayProxy::singleton; } else { typedef XrayWrapper<CrossCompartmentWrapper> Xray; usingXray = true; wrapper = &Xray::singleton; } } else { wrapper = &CrossCompartmentWrapper::singleton; } } else { NS_ASSERTION(!AccessCheck::needsSystemOnlyWrapper(obj), "bad object exposed across origins"); // Cross origin we want to disallow scripting and limit access to // a predefined set of properties. XrayWrapper adds a property // (.wrappedJSObject) which allows bypassing the XrayWrapper, but // we filter out access to that property. bool proxy; if (!CanXray(obj, &proxy)) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, CrossOriginAccessiblePropertiesOnly>::singleton; } else { if (proxy) { wrapper = &FilteringWrapper<XrayProxy, CrossOriginAccessiblePropertiesOnly>::singleton; } else { typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray; usingXray = true; // Location objects can become same origin after navigation, so we might // have to grant transparent access later on. if (IsLocationObject(obj)) { wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton; } else { wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton; } } } } JSObject *wrapperObj = Wrapper::New(cx, obj, wrappedProto, parent, wrapper); if (!wrapperObj || !usingXray) return wrapperObj; JSObject *xrayHolder = XrayUtils::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; js::SetProxyExtra(wrapperObj, 0, js::ObjectValue(*xrayHolder)); return wrapperObj; }
JSObject * WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, HandleObject wrappedProto, HandleObject parent, unsigned flags) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::GetObjectClass(obj)->ext.innerObject, "wrapped object passed to rewrap"); MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder"); MOZ_ASSERT(!js::IsInnerObject(obj)); // We sometimes end up here after nsContentUtils has been shut down but before // XPConnect has been shut down, so check the context stack the roundabout way. MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx); // Compute the information we need to select the right wrapper. JSCompartment *origin = js::GetObjectCompartment(obj); JSCompartment *target = js::GetContextCompartment(cx); bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; XrayType xrayType = GetXrayType(obj); bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG; Wrapper *wrapper; CompartmentPrivate *targetdata = EnsureCompartmentPrivate(target); // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. if (xpc::IsUniversalXPConnectEnabled(target)) { wrapper = &CrossCompartmentWrapper::singleton; // If this is a chrome object being exposed to content without Xrays, use // a COW. } else if (originIsChrome && !targetIsChrome && xrayType == NotXray) { wrapper = &ChromeObjectWrapper::singleton; // If content is accessing NAC, we need a special filter, even if the // object is same origin. Note that we allow access to NAC for remote-XUL // whitelisted domains, since they don't have XBL scopes. } else if (AccessCheck::needsSystemOnlyWrapper(obj) && xpc::AllowXBLScope(target) && !(targetIsChrome || (targetSubsumesOrigin && nsContentUtils::IsCallerXBL()))) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } // Normally, a non-xrayable non-waived content object that finds itself in // a privileged scope is wrapped with a CrossCompartmentWrapper, even though // the lack of a waiver _really_ should give it an opaque wrapper. This is // a bit too entrenched to change for content-chrome, but we can at least fix // it for XBL scopes. // // See bug 843829. else if (targetSubsumesOrigin && !originSubsumesTarget && !waiveXrayFlag && xrayType == NotXray && IsXBLScope(target)) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton; } // // Now, handle the regular cases. // // These are wrappers we can compute using a rule-based approach. In order // to do so, we need to compute some parameters. // else { // The wrapper is a security wrapper (protecting the wrappee) if and // only if the target does not subsume the origin. bool securityWrapper = !targetSubsumesOrigin; // Xrays are warranted if either the target or the origin don't trust // each other. This is generally the case, unless the two are same-origin // and the caller has not requested same-origin Xrays. // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool wantXrays = !(sameOrigin && !targetdata->wantXrays); // If Xrays are warranted, the caller may waive them for non-security // wrappers. bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag; wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays); } if (!targetSubsumesOrigin) { // Do a belt-and-suspenders check against exposing eval()/Function() to // non-subsuming content. JSFunction *fun = JS_GetObjectFunction(obj); if (fun) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { JS_ReportError(cx, "Permission denied to expose eval or Function to non-subsuming content"); return nullptr; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing) return Wrapper::Renew(cx, existing, obj, wrapper); return Wrapper::New(cx, obj, parent, wrapper); }
JSObject * WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, HandleObject wrappedProto, HandleObject parent, unsigned flags) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::GetObjectClass(obj)->ext.innerObject, "wrapped object passed to rewrap"); MOZ_ASSERT(JS_GetClass(obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); MOZ_ASSERT(!js::IsInnerObject(obj)); // We sometimes end up here after nsContentUtils has been shut down but before // XPConnect has been shut down, so check the context stack the roundabout way. MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx); // Compute the information we need to select the right wrapper. JSCompartment *origin = js::GetObjectCompartment(obj); JSCompartment *target = js::GetContextCompartment(cx); bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumes(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumes(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; XrayType xrayType = GetXrayType(obj); bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG; // By default we use the wrapped proto of the underlying object as the // prototype for our wrapper, but we may select something different below. RootedObject proxyProto(cx, wrappedProto); Wrapper *wrapper; CompartmentPrivate *targetdata = EnsureCompartmentPrivate(target); // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. if (xpc::IsUniversalXPConnectEnabled(target)) { wrapper = &CrossCompartmentWrapper::singleton; // If this is a chrome object being exposed to content without Xrays, use // a COW. } else if (originIsChrome && !targetIsChrome && xrayType == NotXray) { wrapper = &ChromeObjectWrapper::singleton; // If content is accessing a Components object or NAC, we need a special filter, // even if the object is same origin. } else if (IsComponentsObject(obj) && !AccessCheck::isChrome(target)) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, ComponentsObjectPolicy>::singleton; } else if (AccessCheck::needsSystemOnlyWrapper(obj) && !(targetIsChrome || (targetSubsumesOrigin && nsContentUtils::IsCallerXBL()))) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OnlyIfSubjectIsSystem>::singleton; } // Normally, a non-xrayable non-waived content object that finds itself in // a privileged scope is wrapped with a CrossCompartmentWrapper, even though // the lack of a waiver _really_ should give it an opaque wrapper. This is // a bit too entrenched to change for content-chrome, but we can at least fix // it for XBL scopes. // // See bug 843829. else if (targetSubsumesOrigin && !originSubsumesTarget && !waiveXrayFlag && xrayType == NotXray && IsXBLScope(target)) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>::singleton; } // // Now, handle the regular cases. // // These are wrappers we can compute using a rule-based approach. In order // to do so, we need to compute some parameters. // else { // The wrapper is a security wrapper (protecting the wrappee) if and // only if the target does not subsume the origin. bool securityWrapper = !targetSubsumesOrigin; // Xrays are warranted if either the target or the origin don't trust // each other. This is generally the case, unless the two are same-origin // and the caller has not requested same-origin Xrays. // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool wantXrays = !(sameOrigin && !targetdata->wantXrays); // If Xrays are warranted, the caller may waive them for non-security // wrappers. bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag; wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays); } // If the prototype of a chrome object being wrapped in content is a prototype // for a standard class, use the one from the content compartment so // that we can safely take advantage of things like .forEach(). // // If the prototype chain of chrome object |obj| looks like this: // // obj => foo => bar => chromeWin.StandardClass.prototype // // The prototype chain of COW(obj) looks lke this: // // COW(obj) => COW(foo) => COW(bar) => contentWin.StandardClass.prototype if (wrapper == &ChromeObjectWrapper::singleton) { JSProtoKey key = JSProto_Null; { JSAutoCompartment ac(cx, obj); RootedObject unwrappedProto(cx); if (!js::GetObjectProto(cx, obj, unwrappedProto.address())) return NULL; if (unwrappedProto && IsCrossCompartmentWrapper(unwrappedProto)) unwrappedProto = Wrapper::wrappedObject(unwrappedProto); if (unwrappedProto) { JSAutoCompartment ac2(cx, unwrappedProto); key = JS_IdentifyClassPrototype(cx, unwrappedProto); } } if (key != JSProto_Null) { RootedObject homeProto(cx); if (!JS_GetClassPrototype(cx, key, homeProto.address())) return NULL; MOZ_ASSERT(homeProto); proxyProto = homeProto; } // This shouldn't happen, but do a quick check to make some dumb addon // doesn't expose chrome eval or Function(). JSFunction *fun = JS_GetObjectFunction(obj); if (fun) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { JS_ReportError(cx, "Not allowed to access chrome eval or Function from content"); return nullptr; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing && proxyProto == wrappedProto) return Wrapper::Renew(cx, existing, obj, wrapper); return Wrapper::New(cx, obj, proxyProto, parent, wrapper); }
JSObject * WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj, HandleObject parent) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::GetObjectClass(obj)->ext.innerObject, "wrapped object passed to rewrap"); MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder"); MOZ_ASSERT(!js::IsInnerObject(obj)); // We sometimes end up here after nsContentUtils has been shut down but before // XPConnect has been shut down, so check the context stack the roundabout way. MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx); // Compute the information we need to select the right wrapper. JSCompartment *origin = js::GetObjectCompartment(obj); JSCompartment *target = js::GetContextCompartment(cx); bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; XrayType xrayType = GetXrayType(obj); const Wrapper *wrapper; // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. if (xpc::IsUniversalXPConnectEnabled(target)) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } // Let the SpecialPowers scope make its stuff easily accessible to content. else if (CompartmentPrivate::Get(origin)->forcePermissiveCOWs) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } // If this is a chrome object being exposed to content without Xrays, use // a COW. // // We make an exception for Object instances, because we still rely on COWs // for those in a lot of places in the tree. else if (originIsChrome && !targetIsChrome && (xrayType == NotXray || ForceCOWBehavior(obj))) { wrapper = &ChromeObjectWrapper::singleton; } // // Now, handle the regular cases. // // These are wrappers we can compute using a rule-based approach. In order // to do so, we need to compute some parameters. // else { // The wrapper is a security wrapper (protecting the wrappee) if and // only if the target does not subsume the origin. bool securityWrapper = !targetSubsumesOrigin; // Xrays are warranted if either the target or the origin don't trust // each other. This is generally the case, unless the two are same-origin // and the caller has not requested same-origin Xrays. // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool sameOriginXrays = CompartmentPrivate::Get(origin)->wantXrays || CompartmentPrivate::Get(target)->wantXrays; bool wantXrays = !sameOrigin || sameOriginXrays; // If Xrays are warranted, the caller may waive them for non-security // wrappers. bool waiveXrays = wantXrays && !securityWrapper && HasWaiveXrayFlag(obj); // We have slightly different behavior for the case when the object // being wrapped is in an XBL scope. bool originIsContentXBLScope = IsContentXBLScope(origin); wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays, originIsContentXBLScope); // If we want to apply add-on interposition in the target compartment, // then we try to "upgrade" the wrapper to an interposing one. if (CompartmentPrivate::Get(target)->scope->HasInterposition()) wrapper = SelectAddonWrapper(cx, obj, wrapper); } if (!targetSubsumesOrigin) { // Do a belt-and-suspenders check against exposing eval()/Function() to // non-subsuming content. if (JSFunction *fun = JS_GetObjectFunction(obj)) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { NS_WARNING("Trying to expose eval or Function to non-subsuming content!"); wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing) return Wrapper::Renew(cx, existing, obj, wrapper); return Wrapper::New(cx, obj, parent, wrapper); }
JSObject * WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, uintN flags) { NS_ASSERTION(!obj->isWrapper() || (obj->isWrapper() && obj->getProxyHandler() == &WaiveXrayWrapperWrapper) || obj->getClass()->ext.innerObject, "wrapped object passed to rewrap"); NS_ASSERTION(JS_GET_CLASS(cx, obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); JSCompartment *origin = obj->compartment(); JSCompartment *target = cx->compartment; JSObject *xrayHolder = nsnull; JSWrapper *wrapper; CompartmentPrivate *targetdata = static_cast<CompartmentPrivate *>(JS_GetCompartmentPrivate(cx, target)); if (AccessCheck::isChrome(target)) { if (AccessCheck::isChrome(origin)) { wrapper = &JSCrossCompartmentWrapper::singleton; } else { bool isSystem; { JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return nsnull; JSObject *globalObj = JS_GetGlobalForObject(cx, obj); JS_ASSERT(globalObj); isSystem = JS_IsSystemObject(cx, globalObj); } if (isSystem) { wrapper = &JSCrossCompartmentWrapper::singleton; } else if (flags & WAIVE_XRAY_WRAPPER_FLAG) { // If we waived the X-ray wrapper for this object, wrap it into a // special wrapper to transitively maintain the X-ray waiver. wrapper = &CrossOriginWrapper::singleton; } else { // Native objects must be wrapped into an X-ray wrapper. if (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &NoWaiverWrapper::singleton; } } } } else if (AccessCheck::isChrome(origin)) { if (obj->isFunction()) { JSFunction *fun = obj->getFunctionPrivate(); if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { JS_ReportError(cx, "Not allowed to access chrome eval or Function from content"); return nsnull; } } XPCWrappedNative *wn; if (targetdata && (wn = GetWrappedNative(cx, obj)) && wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, ExposedPropertiesOnly>::singleton; } } else if (AccessCheck::isSameOrigin(origin, target)) { // Same origin we use a transparent wrapper, unless the compartment asks // for an Xray or the wrapper needs a SOW. if (AccessCheck::needsSystemOnlyWrapper(obj)) { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, OnlyIfSubjectIsSystem>::singleton; } else if (targetdata && targetdata->wantXrays && (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject)) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &JSCrossCompartmentWrapper::singleton; } } else { NS_ASSERTION(!AccessCheck::needsSystemOnlyWrapper(obj), "bad object exposed across origins"); // Cross origin we want to disallow scripting and limit access to // a predefined set of properties. XrayWrapper adds a property // (.wrappedJSObject) which allows bypassing the XrayWrapper, but // we filter out access to that property. if (!IS_WN_WRAPPER(obj) && !obj->getClass()->ext.innerObject) { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, CrossOriginAccessiblePropertiesOnly>::singleton; } else { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; // Location objects can become same origin after navigation, so we might // have to grant transparent access later on. if (IsLocationObject(obj)) { wrapper = &FilteringWrapper<Xray, SameOriginOrCrossOriginAccessiblePropertiesOnly>::singleton; } else { wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton; } xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } } JSObject *wrapperObj = JSWrapper::New(cx, obj, wrappedProto, parent, wrapper); if (!wrapperObj || !xrayHolder) return wrapperObj; // NB: The fact that the only wrappers to use ProxyExtra are XrayWrappers // is relied on by XPCNativeWrapper.unwrap. wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); return wrapperObj; }
JSObject* WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj) { MOZ_ASSERT(!IsWrapper(obj) || GetProxyHandler(obj) == &XrayWaiver || js::IsWindowProxy(obj), "wrapped object passed to rewrap"); MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder"); MOZ_ASSERT(!js::IsWindow(obj)); MOZ_ASSERT(dom::IsJSAPIActive()); // Compute the information we need to select the right wrapper. JSCompartment* origin = js::GetObjectCompartment(obj); JSCompartment* target = js::GetContextCompartment(cx); bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; XrayType xrayType = GetXrayType(obj); const Wrapper* wrapper; // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use // a vanilla CCW. if (xpc::IsUniversalXPConnectEnabled(target)) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } // Let the SpecialPowers scope make its stuff easily accessible to content. else if (CompartmentPrivate::Get(origin)->forcePermissiveCOWs) { CrashIfNotInAutomation(); wrapper = &CrossCompartmentWrapper::singleton; } // Special handling for chrome objects being exposed to content. else if (originIsChrome && !targetIsChrome) { // If this is a chrome function being exposed to content, we need to allow // call (but nothing else). We allow CPOWs that purport to be function's // here, but only in the content process. if ((IdentifyStandardInstance(obj) == JSProto_Function || (jsipc::IsCPOW(obj) && JS::IsCallable(obj) && XRE_IsContentProcess()))) { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton; } // For Vanilla JSObjects exposed from chrome to content, we use a wrapper // that supports __exposedProps__. We'd like to get rid of these eventually, // but in their current form they don't cause much trouble. else if (IdentifyStandardInstance(obj) == JSProto_Object) { wrapper = &ChromeObjectWrapper::singleton; } // Otherwise we get an opaque wrapper. else { wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } } // // Now, handle the regular cases. // // These are wrappers we can compute using a rule-based approach. In order // to do so, we need to compute some parameters. // else { // The wrapper is a security wrapper (protecting the wrappee) if and // only if the target does not subsume the origin. bool securityWrapper = !targetSubsumesOrigin; // Xrays are warranted if either the target or the origin don't trust // each other. This is generally the case, unless the two are same-origin // and the caller has not requested same-origin Xrays. // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool sameOriginXrays = CompartmentPrivate::Get(origin)->wantXrays || CompartmentPrivate::Get(target)->wantXrays; bool wantXrays = !sameOrigin || sameOriginXrays; // If Xrays are warranted, the caller may waive them for non-security // wrappers (unless explicitly forbidden from doing so). bool waiveXrays = wantXrays && !securityWrapper && CompartmentPrivate::Get(target)->allowWaivers && HasWaiveXrayFlag(obj); // We have slightly different behavior for the case when the object // being wrapped is in an XBL scope. bool originIsContentXBLScope = IsContentXBLScope(origin); wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays, originIsContentXBLScope, obj); // If we want to apply add-on interposition in the target compartment, // then we try to "upgrade" the wrapper to an interposing one. if (CompartmentPrivate::Get(target)->scope->HasInterposition()) wrapper = SelectAddonWrapper(cx, obj, wrapper); } if (!targetSubsumesOrigin) { // Do a belt-and-suspenders check against exposing eval()/Function() to // non-subsuming content. if (JSFunction* fun = JS_GetObjectFunction(obj)) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { NS_WARNING("Trying to expose eval or Function to non-subsuming content!"); wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } } } DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target); if (existing) return Wrapper::Renew(cx, existing, obj, wrapper); return Wrapper::New(cx, obj, wrapper); }