зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1051224 - Add an opt-out for cross-origin argument checking. r=gabor
This commit is contained in:
Родитель
f31fdb8904
Коммит
2606e901b5
|
@ -83,8 +83,12 @@ StackScopedCloneRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t ta
|
|||
if (!JS_WrapObject(cx, &obj))
|
||||
return nullptr;
|
||||
|
||||
if (!xpc::NewFunctionForwarder(cx, JSID_VOIDHANDLE, obj, &functionValue))
|
||||
FunctionForwarderOptions forwarderOptions;
|
||||
if (!xpc::NewFunctionForwarder(cx, JSID_VOIDHANDLE, obj, forwarderOptions,
|
||||
&functionValue))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &functionValue.toObject();
|
||||
}
|
||||
|
@ -200,8 +204,13 @@ StackScopedClone(JSContext *cx, StackScopedCloneOptions &options,
|
|||
// Note - This function mirrors the logic of CheckPassToChrome in
|
||||
// ChromeObjectWrapper.cpp.
|
||||
static bool
|
||||
CheckSameOriginArg(JSContext *cx, HandleValue v)
|
||||
CheckSameOriginArg(JSContext *cx, FunctionForwarderOptions &options, HandleValue v)
|
||||
{
|
||||
// Consumers can explicitly opt out of this security check. This is used in
|
||||
// the web console to allow the utility functions to accept cross-origin Windows.
|
||||
if (options.allowCrossOriginArguments)
|
||||
return true;
|
||||
|
||||
// Primitives are fine.
|
||||
if (!v.isObject())
|
||||
return true;
|
||||
|
@ -232,6 +241,13 @@ FunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
|
|||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
// Grab the options from the reserved slot.
|
||||
RootedObject optionsObj(cx, &js::GetFunctionNativeReserved(&args.callee(), 1).toObject());
|
||||
FunctionForwarderOptions options(cx, optionsObj);
|
||||
if (!options.Parse())
|
||||
return false;
|
||||
|
||||
// Grab and unwrap the underlying callable.
|
||||
RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
|
||||
RootedObject unwrappedFun(cx, js::UncheckedUnwrap(&v.toObject()));
|
||||
|
||||
|
@ -247,11 +263,11 @@ FunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
|
|||
JSAutoCompartment ac(cx, unwrappedFun);
|
||||
|
||||
RootedValue thisVal(cx, ObjectValue(*thisObj));
|
||||
if (!CheckSameOriginArg(cx, thisVal) || !JS_WrapObject(cx, &thisObj))
|
||||
if (!CheckSameOriginArg(cx, options, thisVal) || !JS_WrapObject(cx, &thisObj))
|
||||
return false;
|
||||
|
||||
for (size_t n = 0; n < args.length(); ++n) {
|
||||
if (!CheckSameOriginArg(cx, args[n]) || !JS_WrapValue(cx, args[n]))
|
||||
if (!CheckSameOriginArg(cx, options, args[n]) || !JS_WrapValue(cx, args[n]))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -266,7 +282,7 @@ FunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
bool
|
||||
NewFunctionForwarder(JSContext *cx, HandleId idArg, HandleObject callable,
|
||||
MutableHandleValue vp)
|
||||
FunctionForwarderOptions &options, MutableHandleValue vp)
|
||||
{
|
||||
RootedId id(cx, idArg);
|
||||
if (id == JSID_VOIDHANDLE)
|
||||
|
@ -277,8 +293,17 @@ NewFunctionForwarder(JSContext *cx, HandleId idArg, HandleObject callable,
|
|||
if (!fun)
|
||||
return false;
|
||||
|
||||
JSObject *funobj = JS_GetFunctionObject(fun);
|
||||
// Stash the callable in slot 0.
|
||||
AssertSameCompartment(cx, callable);
|
||||
RootedObject funobj(cx, JS_GetFunctionObject(fun));
|
||||
js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable));
|
||||
|
||||
// Stash the options in slot 1.
|
||||
RootedObject optionsObj(cx, options.ToJSObject(cx));
|
||||
if (!optionsObj)
|
||||
return false;
|
||||
js::SetFunctionNativeReserved(funobj, 1, ObjectValue(*optionsObj));
|
||||
|
||||
vp.setObject(*funobj);
|
||||
return true;
|
||||
}
|
||||
|
@ -349,7 +374,9 @@ ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleV
|
|||
|
||||
// And now, let's create the forwarder function in the target compartment
|
||||
// for the function the be exported.
|
||||
if (!NewFunctionForwarder(cx, id, funObj, rval)) {
|
||||
FunctionForwarderOptions forwarderOptions;
|
||||
forwarderOptions.allowCrossOriginArguments = options.allowCrossOriginArguments;
|
||||
if (!NewFunctionForwarder(cx, id, funObj, forwarderOptions, rval)) {
|
||||
JS_ReportError(cx, "Exporting function failed");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -3071,7 +3071,8 @@ nsXPCComponents_Utils::MakeObjectPropsNormal(HandleValue vobj, JSContext *cx)
|
|||
if (!js::IsWrapper(propobj) || !JS_ObjectIsCallable(cx, propobj))
|
||||
continue;
|
||||
|
||||
if (!NewFunctionForwarder(cx, id, propobj, &v) ||
|
||||
FunctionForwarderOptions forwarderOptions;
|
||||
if (!NewFunctionForwarder(cx, id, propobj, forwarderOptions, &v) ||
|
||||
!JS_SetPropertyById(cx, obj, id, v))
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -3363,9 +3363,10 @@ Btoa(JSContext *cx, unsigned argc, jsval *vp);
|
|||
|
||||
// Helper function that creates a JSFunction that wraps a native function that
|
||||
// forwards the call to the original 'callable'.
|
||||
class FunctionForwarderOptions;
|
||||
bool
|
||||
NewFunctionForwarder(JSContext *cx, JS::HandleId id, JS::HandleObject callable,
|
||||
JS::MutableHandleValue vp);
|
||||
FunctionForwarderOptions &options, JS::MutableHandleValue vp);
|
||||
|
||||
// Old fashioned xpc error reporter. Try to use JS_ReportError instead.
|
||||
nsresult
|
||||
|
@ -3479,13 +3480,46 @@ public:
|
|||
JSObject* options = nullptr)
|
||||
: OptionsBase(cx, options)
|
||||
, defineAs(cx, JSID_VOID)
|
||||
, allowCrossOriginArguments(false)
|
||||
{ }
|
||||
|
||||
virtual bool Parse() {
|
||||
return ParseId("defineAs", &defineAs);
|
||||
return ParseId("defineAs", &defineAs) &&
|
||||
ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments);
|
||||
}
|
||||
|
||||
JS::RootedId defineAs;
|
||||
bool allowCrossOriginArguments;
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS FunctionForwarderOptions : public OptionsBase {
|
||||
public:
|
||||
explicit FunctionForwarderOptions(JSContext *cx = xpc_GetSafeJSContext(),
|
||||
JSObject* options = nullptr)
|
||||
: OptionsBase(cx, options)
|
||||
, allowCrossOriginArguments(false)
|
||||
{ }
|
||||
|
||||
JSObject *ToJSObject(JSContext *cx) {
|
||||
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
JS::RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), global));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
JS::RootedValue val(cx);
|
||||
unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
|
||||
val = JS::BooleanValue(allowCrossOriginArguments);
|
||||
if (!JS_DefineProperty(cx, obj, "allowCrossOriginArguments", val, attrs))
|
||||
return nullptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
virtual bool Parse() {
|
||||
return ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments);
|
||||
}
|
||||
|
||||
bool allowCrossOriginArguments;
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS StackScopedCloneOptions : public OptionsBase {
|
||||
|
|
|
@ -35,6 +35,7 @@ function run_test() {
|
|||
wasCalled = false;
|
||||
}
|
||||
exportFunction(funToExport, subsb, { defineAs: "imported", allowCallbacks: true });
|
||||
exportFunction((x) => x, subsb, { defineAs: "echoAllowXO", allowCallbacks: true, allowCrossOriginArguments: true });
|
||||
}.toSource() + ")()", epsb);
|
||||
|
||||
subsb.xrayed = Cu.evalInSandbox("(" + function () {
|
||||
|
@ -64,6 +65,15 @@ function run_test() {
|
|||
do_check_true(/denied|insecure/.test(e));
|
||||
}
|
||||
|
||||
// Callers can opt-out of the above.
|
||||
subsb.xoNative = Cu.evalInSandbox('new XMLHttpRequest()', xorigsb);
|
||||
try {
|
||||
do_check_eq(Cu.evalInSandbox('echoAllowXO(xoNative)', subsb), subsb.xoNative);
|
||||
do_check_true(true);
|
||||
} catch (e) {
|
||||
do_check_true(false);
|
||||
}
|
||||
|
||||
// Apply should work and |this| should carry over appropriately.
|
||||
Cu.evalInSandbox("(" + function() {
|
||||
var someThis = {};
|
||||
|
|
Загрузка…
Ссылка в новой задаче