bool AccessCheck::isCrossOriginAccessPermitted(JSContext* cx, HandleObject wrapper, HandleId id, Wrapper::Action act) { if (act == Wrapper::CALL) return false; 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, wrapper, id, Wrapper::GET) || isCrossOriginAccessPermitted(cx, wrapper, id, Wrapper::SET); } RootedObject obj(cx, js::UncheckedUnwrap(wrapper, /* stopAtOuter = */ false)); CrossOriginObjectType type = IdentifyCrossOriginObject(obj); if (JSID_IS_STRING(id)) { if (IsPermitted(type, JSID_TO_FLAT_STRING(id), act == Wrapper::SET)) return true; } if (act != Wrapper::GET) return false; // Check for frame IDs. If we're resolving named frames, make sure to only // resolve ones that don't shadow native properties. See bug 860494. if (type == CrossOriginWindow) { if (JSID_IS_STRING(id)) { bool wouldShadow = false; if (!XrayUtils::HasNativeProperty(cx, wrapper, id, &wouldShadow) || wouldShadow) { // If the named subframe matches the name of a DOM constructor, // the global resolve triggered by the HasNativeProperty call // above will try to perform a CheckedUnwrap on |wrapper|, and // throw a security error if it fails. That exception isn't // really useful for our callers, so we silence it and just // deny access to the property (since it matched a builtin). // // Note that this would be a problem if the resolve code ever // tried to CheckedUnwrap the wrapper _before_ concluding that // the name corresponds to a builtin global property, since it // would mean that we'd never permit cross-origin named subframe // access (something we regrettably need to support). JS_ClearPendingException(cx); return false; } } return IsFrameId(cx, obj, id); } return false; }
static const Wrapper* SelectWrapper(bool securityWrapper, bool wantXrays, XrayType xrayType, bool waiveXrays, bool originIsXBLScope, JSObject* obj) { // Waived Xray uses a modified CCW that has transparent behavior but // transitively waives Xrays on arguments. if (waiveXrays) { MOZ_ASSERT(!securityWrapper); return &WaiveXrayWrapper::singleton; } // If we don't want or can't use Xrays, select a wrapper that's either // entirely transparent or entirely opaque. if (!wantXrays || xrayType == NotXray) { if (!securityWrapper) return &CrossCompartmentWrapper::singleton; return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } // Ok, we're using Xray. If this isn't a security wrapper, use the permissive // version and skip the filter. if (!securityWrapper) { if (xrayType == XrayForWrappedNative) return &PermissiveXrayXPCWN::singleton; else if (xrayType == XrayForDOMObject) return &PermissiveXrayDOM::singleton; else if (xrayType == XrayForJSObject) return &PermissiveXrayJS::singleton; MOZ_ASSERT(xrayType == XrayForOpaqueObject); return &PermissiveXrayOpaque::singleton; } // This is a security wrapper. Use the security versions and filter. if (xrayType == XrayForDOMObject && IdentifyCrossOriginObject(obj) != CrossOriginOpaque) return &FilteringWrapper<CrossOriginXrayWrapper, CrossOriginAccessiblePropertiesOnly>::singleton; // There's never any reason to expose other objects to non-subsuming actors. // Just use an opaque wrapper in these cases. // // In general, we don't want opaque function wrappers to be callable. // But in the case of XBL, we rely on content being able to invoke // functions exposed from the XBL scope. We could remove this exception, // if needed, by using ExportFunction to generate the content-side // representations of XBL methods. if (xrayType == XrayForJSObject && originIsXBLScope) return &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton; return &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; }