mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 580128 - Always wrap Location objects in wrappers, even for same origin. r=mrbkap/gal
This commit is contained in:
parent
2ec85d518c
commit
e036314b75
@ -2465,10 +2465,19 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj
|
||||
jsClass = aObj->getClass();
|
||||
} while (1);
|
||||
|
||||
NS_ASSERTION(!aAllowShortCircuit ||
|
||||
result == doGetObjectPrincipal(origObj, PR_FALSE),
|
||||
"Principal mismatch. Not good");
|
||||
|
||||
#ifdef DEBUG
|
||||
if (aAllowShortCircuit) {
|
||||
nsIPrincipal *principal = doGetObjectPrincipal(origObj, PR_FALSE);
|
||||
|
||||
// Location is always wrapped (even for same-compartment), so we can
|
||||
// loosen the check to same-origin instead of same-principal.
|
||||
NS_ASSERTION(strcmp(jsClass->name, "Location") == 0 ?
|
||||
NS_SUCCEEDED(CheckSameOriginPrincipal(result, principal)) :
|
||||
result == principal,
|
||||
"Principal mismatch. Not good");
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,7 @@ private:
|
||||
class nsOuterWindowProxy : public JSWrapper
|
||||
{
|
||||
public:
|
||||
nsOuterWindowProxy() : JSWrapper(0) {}
|
||||
nsOuterWindowProxy() : JSWrapper((uintN)0) {}
|
||||
|
||||
virtual bool isOuterWindow() {
|
||||
return true;
|
||||
|
@ -35,26 +35,35 @@ function runTest() {
|
||||
is(lastlocation.iframeprop, 42, 'can read the new prop');
|
||||
ok(firstlocation !== lastlocation, 'got a new location object');
|
||||
// firstlocation should still work.
|
||||
ok(firstlocation.href.indexOf('file_location.html'), 'can read location.href');
|
||||
firstlocation.href = 'http://example.com/tests/dom/tests/mochitest/dom-level0/file_location.html';
|
||||
return;
|
||||
}
|
||||
|
||||
if (count == 3) {
|
||||
var permissionDenied = false;
|
||||
try {
|
||||
isnot($('ifr').contentWindow.location.iframeprop, 42, "shouldn't see this");
|
||||
var foo = $('ifr').contentWindow.location.href == '';
|
||||
} catch (e) {
|
||||
ok(/Permission denied/.test(e), "correctly threw a security error");
|
||||
permissionDenied = /Permission denied/.test(e.message);
|
||||
}
|
||||
ok(permissionDenied, 'correctly threw a permission denied security error when reading location.href');
|
||||
|
||||
// XXX this doesn't work - file a bug on me!
|
||||
//firstlocation.href = 'http://mochi.test:8888/tests/dom/tests/mochitest/dom-level0/file_location.html';
|
||||
todo(false, 'can use old same-origin location object to set cross-origin frame');
|
||||
$('ifr').contentWindow.location = 'file_location.html';
|
||||
permissionDenied = false;
|
||||
try {
|
||||
var foo = $('ifr').contentWindow.location.iframeprop == 42;
|
||||
} catch (e) {
|
||||
permissionDenied = /Permission denied/.test(e.message);
|
||||
}
|
||||
ok(permissionDenied, 'correctly threw a permission denied security error an expando on location');
|
||||
|
||||
firstlocation.href = 'http://mochi.test:8888/tests/dom/tests/mochitest/dom-level0/file_location.html';
|
||||
return;
|
||||
}
|
||||
|
||||
is(lastlocation.iframeprop, 42, 'can still read old values of the location object');
|
||||
ok(lastlocation !== $('ifr').contentWindow.location, 'location objects are distinct');
|
||||
ok(firstlocation.href.indexOf('file_location.html'), 'can read location.href');
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
@ -85,6 +85,10 @@ JSObject::unwrap(uintN *flagsp)
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
JSWrapper::JSWrapper(void *family) : JSProxyHandler(&sWrapperFamily), mFlags(0)
|
||||
{
|
||||
}
|
||||
|
||||
JSWrapper::JSWrapper(uintN flags) : JSProxyHandler(&sWrapperFamily), mFlags(flags)
|
||||
{
|
||||
}
|
||||
@ -278,7 +282,7 @@ JSWrapper::leave(JSContext *cx, JSObject *wrapper)
|
||||
{
|
||||
}
|
||||
|
||||
JSWrapper JSWrapper::singleton(0);
|
||||
JSWrapper JSWrapper::singleton((uintN)0);
|
||||
|
||||
JSObject *
|
||||
JSWrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
|
||||
@ -360,7 +364,7 @@ AutoCompartment::leave()
|
||||
|
||||
/* Cross compartment wrappers. */
|
||||
|
||||
JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(void *family) : JSWrapper(0)
|
||||
JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(void *family) : JSWrapper((uintN)0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,10 @@ JS_BEGIN_EXTERN_C
|
||||
/* No-op wrapper handler base class. */
|
||||
class JSWrapper : public js::JSProxyHandler {
|
||||
uintN mFlags;
|
||||
protected:
|
||||
// XXX Hack to let wrappers derive from either cross compartment
|
||||
// wrappers or JSProxyHandlers.
|
||||
JS_FRIEND_API(JSWrapper(void *family));
|
||||
public:
|
||||
uintN flags() const { return mFlags; }
|
||||
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "XPCWrapper.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "WrapperFactory.h"
|
||||
|
||||
//#define STRICT_CHECK_OF_UNICODE
|
||||
#ifdef STRICT_CHECK_OF_UNICODE
|
||||
@ -1345,6 +1346,30 @@ XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx,
|
||||
JSObject *original = flat;
|
||||
if(!JS_WrapObject(ccx, &flat))
|
||||
return JS_FALSE;
|
||||
|
||||
// If the object was not wrapped, we are same compartment and don't need
|
||||
// to enforce any cross origin policies, except in case of the location
|
||||
// object, which always needs a wrapper in between.
|
||||
if(original == flat)
|
||||
{
|
||||
if(xpc::WrapperFactory::IsLocationObject(flat))
|
||||
{
|
||||
JSObject *locationWrapper = wrapper->GetWrapper();
|
||||
if(!locationWrapper)
|
||||
{
|
||||
locationWrapper = xpc::WrapperFactory::WrapLocationObject(cx, flat);
|
||||
if(!locationWrapper)
|
||||
return JS_FALSE;
|
||||
|
||||
// Cache the location wrapper to ensure that we maintain
|
||||
// the identity of window/document.location.
|
||||
wrapper->SetWrapper(locationWrapper);
|
||||
}
|
||||
|
||||
flat = locationWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
*d = OBJECT_TO_JSVAL(flat);
|
||||
|
||||
if(dest)
|
||||
|
@ -63,6 +63,22 @@ AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b)
|
||||
cond;
|
||||
}
|
||||
|
||||
bool
|
||||
AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSCompartment *compartment = obj->compartment();
|
||||
|
||||
obj = obj->unwrap()->getParent();
|
||||
if (!obj->getClass()->ext.innerObject) {
|
||||
obj = obj->unwrap();
|
||||
JS_ASSERT(obj->getClass()->ext.innerObject);
|
||||
}
|
||||
OBJ_TO_INNER_OBJECT(cx, obj);
|
||||
if (!obj)
|
||||
return false;
|
||||
return isSameOrigin(compartment, obj->compartment());
|
||||
}
|
||||
|
||||
bool
|
||||
AccessCheck::isChrome(JSCompartment *compartment)
|
||||
{
|
||||
|
@ -52,6 +52,7 @@ class AccessCheck {
|
||||
static bool isCrossOriginAccessPermitted(JSContext *cx, JSObject *obj, jsid id,
|
||||
JSWrapper::Action act);
|
||||
static bool isSystemOnlyAccessPermitted(JSContext *cx);
|
||||
static bool isLocationObjectSameOrigin(JSContext *cx, JSObject *obj);
|
||||
|
||||
static bool needsSystemOnlyWrapper(JSObject *obj);
|
||||
|
||||
@ -99,6 +100,20 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy {
|
||||
}
|
||||
};
|
||||
|
||||
// This policy only permits access to properties that are safe to be used
|
||||
// across origins.
|
||||
struct SameOriginOrCrossOriginAccessiblePropertiesOnly : public Policy {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, JSWrapper::Action act,
|
||||
Permission &perm) {
|
||||
perm = DenyAccess;
|
||||
if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) ||
|
||||
AccessCheck::isLocationObjectSameOrigin(cx, wrapper)) {
|
||||
perm = PermitPropertyAccess;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// This policy only permits access to properties if they appear in the
|
||||
// objects exposed properties list.
|
||||
struct ExposedPropertiesOnly : public Policy {
|
||||
|
@ -153,15 +153,23 @@ FilteringWrapper<Base, Policy>::enter(JSContext *cx, JSObject *wrapper, jsid id,
|
||||
#define XOW FilteringWrapper<XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray>, \
|
||||
CrossOriginAccessiblePropertiesOnly>
|
||||
#define NNXOW FilteringWrapper<JSCrossCompartmentWrapper, CrossOriginAccessiblePropertiesOnly>
|
||||
#define LW FilteringWrapper<XrayWrapper<JSWrapper, SameCompartmentXray>, \
|
||||
SameOriginOrCrossOriginAccessiblePropertiesOnly>
|
||||
#define XLW FilteringWrapper<XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray>, \
|
||||
SameOriginOrCrossOriginAccessiblePropertiesOnly>
|
||||
|
||||
template<> SOW SOW::singleton(0);
|
||||
template<> COW COW::singleton(0);
|
||||
template<> XOW XOW::singleton(0);
|
||||
template<> NNXOW NNXOW::singleton(0);
|
||||
template<> LW LW::singleton(0);
|
||||
template<> XLW XLW::singleton(0);
|
||||
|
||||
template class SOW;
|
||||
template class COW;
|
||||
template class XOW;
|
||||
template class NNXOW;
|
||||
template class LW;
|
||||
template class XLW;
|
||||
|
||||
}
|
||||
|
@ -126,8 +126,17 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
|
||||
CrossOriginAccessiblePropertiesOnly>::singleton;
|
||||
} else {
|
||||
typedef XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray> Xray;
|
||||
wrapper = &FilteringWrapper<Xray,
|
||||
CrossOriginAccessiblePropertiesOnly>::singleton;
|
||||
|
||||
// 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;
|
||||
@ -142,4 +151,28 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
|
||||
return wrapperObj;
|
||||
}
|
||||
|
||||
typedef FilteringWrapper<XrayWrapper<JSWrapper, SameCompartmentXray>,
|
||||
SameOriginOrCrossOriginAccessiblePropertiesOnly> LW;
|
||||
|
||||
bool
|
||||
WrapperFactory::IsLocationObject(JSObject *obj)
|
||||
{
|
||||
const char *name = obj->getClass()->name;
|
||||
return name[0] == 'L' && !strcmp(name, "Location");
|
||||
}
|
||||
|
||||
JSObject *
|
||||
WrapperFactory::WrapLocationObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *xrayHolder = LW::createHolder(cx, obj, obj->getParent());
|
||||
if (!xrayHolder)
|
||||
return NULL;
|
||||
JSObject *wrapperObj = JSWrapper::New(cx, obj, obj->getProto(), NULL, &LW::singleton);
|
||||
if (!wrapperObj)
|
||||
return NULL;
|
||||
wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder));
|
||||
xrayHolder->setSlot(XrayUtils::JSSLOT_PROXY_OBJ, js::ObjectValue(*wrapperObj));
|
||||
return wrapperObj;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,6 +59,12 @@ class WrapperFactory {
|
||||
JSObject *wrappedProto,
|
||||
JSObject *parent,
|
||||
uintN flags);
|
||||
|
||||
// Return true if this is a location object.
|
||||
static bool IsLocationObject(JSObject *obj);
|
||||
|
||||
// Wrap a location object.
|
||||
static JSObject *WrapLocationObject(JSContext *cx, JSObject *obj);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -354,11 +354,35 @@ XrayWrapper<Base, Policy>::~XrayWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
class AutoLeaveHelper
|
||||
{
|
||||
public:
|
||||
AutoLeaveHelper(XrayWrapper<Base, Policy> &xray, JSContext *cx, JSObject *wrapper)
|
||||
: xray(xray), cx(cx), wrapper(wrapper)
|
||||
{
|
||||
}
|
||||
~AutoLeaveHelper()
|
||||
{
|
||||
xray.leave(cx, wrapper);
|
||||
}
|
||||
|
||||
private:
|
||||
XrayWrapper<Base, Policy> &xray;
|
||||
JSContext *cx;
|
||||
JSObject *wrapper;
|
||||
};
|
||||
|
||||
template <typename Base, typename Policy>
|
||||
bool
|
||||
XrayWrapper<Base, Policy>::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
|
||||
bool set, PropertyDescriptor *desc_in)
|
||||
{
|
||||
if (!this->enter(cx, wrapper, id, set ? JSWrapper::SET : JSWrapper::GET))
|
||||
return false;
|
||||
|
||||
AutoLeaveHelper<Base, Policy> helper(*this, cx, wrapper);
|
||||
|
||||
JSPropertyDescriptor *desc = Jsvalify(desc_in);
|
||||
|
||||
if (id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) {
|
||||
@ -543,7 +567,7 @@ CrossCompartmentXray::leave(JSContext *cx, JSObject *wrapper, void *priv)
|
||||
}
|
||||
|
||||
#define XPCNW XrayWrapper<JSCrossCompartmentWrapper, CrossCompartmentXray>
|
||||
#define SCNW XrayWrapper<JSProxyHandler, SameCompartmentXray>
|
||||
#define SCNW XrayWrapper<JSWrapper, SameCompartmentXray>
|
||||
|
||||
template <> XPCNW XPCNW::singleton(0);
|
||||
template <> SCNW SCNW::singleton(0);
|
||||
|
Loading…
Reference in New Issue
Block a user