js/xpconnect/wrappers/FilteringWrapper.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,207 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "FilteringWrapper.h"
    1.11 +#include "AccessCheck.h"
    1.12 +#include "ChromeObjectWrapper.h"
    1.13 +#include "XrayWrapper.h"
    1.14 +
    1.15 +#include "jsapi.h"
    1.16 +
    1.17 +using namespace JS;
    1.18 +using namespace js;
    1.19 +
    1.20 +namespace xpc {
    1.21 +
    1.22 +template <typename Base, typename Policy>
    1.23 +FilteringWrapper<Base, Policy>::FilteringWrapper(unsigned flags) : Base(flags)
    1.24 +{
    1.25 +}
    1.26 +
    1.27 +template <typename Base, typename Policy>
    1.28 +FilteringWrapper<Base, Policy>::~FilteringWrapper()
    1.29 +{
    1.30 +}
    1.31 +
    1.32 +template <typename Policy>
    1.33 +static bool
    1.34 +Filter(JSContext *cx, HandleObject wrapper, AutoIdVector &props)
    1.35 +{
    1.36 +    size_t w = 0;
    1.37 +    RootedId id(cx);
    1.38 +    for (size_t n = 0; n < props.length(); ++n) {
    1.39 +        id = props[n];
    1.40 +        if (Policy::check(cx, wrapper, id, Wrapper::GET))
    1.41 +            props[w++] = id;
    1.42 +        else if (JS_IsExceptionPending(cx))
    1.43 +            return false;
    1.44 +    }
    1.45 +    props.resize(w);
    1.46 +    return true;
    1.47 +}
    1.48 +
    1.49 +template <typename Policy>
    1.50 +static bool
    1.51 +FilterSetter(JSContext *cx, JSObject *wrapper, jsid id, JS::MutableHandle<JSPropertyDescriptor> desc)
    1.52 +{
    1.53 +    bool setAllowed = Policy::check(cx, wrapper, id, Wrapper::SET);
    1.54 +    if (!setAllowed) {
    1.55 +        if (JS_IsExceptionPending(cx))
    1.56 +            return false;
    1.57 +        desc.setSetter(nullptr);
    1.58 +    }
    1.59 +    return true;
    1.60 +}
    1.61 +
    1.62 +template <typename Base, typename Policy>
    1.63 +bool
    1.64 +FilteringWrapper<Base, Policy>::getPropertyDescriptor(JSContext *cx, HandleObject wrapper,
    1.65 +                                                      HandleId id,
    1.66 +                                                      JS::MutableHandle<JSPropertyDescriptor> desc)
    1.67 +{
    1.68 +    assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET);
    1.69 +    if (!Base::getPropertyDescriptor(cx, wrapper, id, desc))
    1.70 +        return false;
    1.71 +    return FilterSetter<Policy>(cx, wrapper, id, desc);
    1.72 +}
    1.73 +
    1.74 +template <typename Base, typename Policy>
    1.75 +bool
    1.76 +FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper,
    1.77 +                                                         HandleId id,
    1.78 +                                                         JS::MutableHandle<JSPropertyDescriptor> desc)
    1.79 +{
    1.80 +    assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET);
    1.81 +    if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc))
    1.82 +        return false;
    1.83 +    return FilterSetter<Policy>(cx, wrapper, id, desc);
    1.84 +}
    1.85 +
    1.86 +template <typename Base, typename Policy>
    1.87 +bool
    1.88 +FilteringWrapper<Base, Policy>::getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
    1.89 +                                                    AutoIdVector &props)
    1.90 +{
    1.91 +    assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
    1.92 +    return Base::getOwnPropertyNames(cx, wrapper, props) &&
    1.93 +           Filter<Policy>(cx, wrapper, props);
    1.94 +}
    1.95 +
    1.96 +template <typename Base, typename Policy>
    1.97 +bool
    1.98 +FilteringWrapper<Base, Policy>::enumerate(JSContext *cx, HandleObject wrapper,
    1.99 +                                          AutoIdVector &props)
   1.100 +{
   1.101 +    assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
   1.102 +    return Base::enumerate(cx, wrapper, props) &&
   1.103 +           Filter<Policy>(cx, wrapper, props);
   1.104 +}
   1.105 +
   1.106 +template <typename Base, typename Policy>
   1.107 +bool
   1.108 +FilteringWrapper<Base, Policy>::keys(JSContext *cx, HandleObject wrapper,
   1.109 +                                     AutoIdVector &props)
   1.110 +{
   1.111 +    assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
   1.112 +    return Base::keys(cx, wrapper, props) &&
   1.113 +           Filter<Policy>(cx, wrapper, props);
   1.114 +}
   1.115 +
   1.116 +template <typename Base, typename Policy>
   1.117 +bool
   1.118 +FilteringWrapper<Base, Policy>::iterate(JSContext *cx, HandleObject wrapper,
   1.119 +                                        unsigned flags, MutableHandleValue vp)
   1.120 +{
   1.121 +    assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
   1.122 +    // We refuse to trigger the iterator hook across chrome wrappers because
   1.123 +    // we don't know how to censor custom iterator objects. Instead we trigger
   1.124 +    // the default proxy iterate trap, which will ask enumerate() for the list
   1.125 +    // of (censored) ids.
   1.126 +    return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp);
   1.127 +}
   1.128 +
   1.129 +template <typename Base, typename Policy>
   1.130 +bool
   1.131 +FilteringWrapper<Base, Policy>::nativeCall(JSContext *cx, JS::IsAcceptableThis test,
   1.132 +                                           JS::NativeImpl impl, JS::CallArgs args)
   1.133 +{
   1.134 +    if (Policy::allowNativeCall(cx, test, impl))
   1.135 +        return Base::Permissive::nativeCall(cx, test, impl, args);
   1.136 +    return Base::Restrictive::nativeCall(cx, test, impl, args);
   1.137 +}
   1.138 +
   1.139 +template <typename Base, typename Policy>
   1.140 +bool
   1.141 +FilteringWrapper<Base, Policy>::defaultValue(JSContext *cx, HandleObject obj,
   1.142 +                                             JSType hint, MutableHandleValue vp)
   1.143 +{
   1.144 +    return Base::defaultValue(cx, obj, hint, vp);
   1.145 +}
   1.146 +
   1.147 +// With our entirely-opaque wrapper, the DefaultValue algorithm throws,
   1.148 +// causing spurious exceptions. Manually implement something benign.
   1.149 +template<>
   1.150 +bool
   1.151 +FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>
   1.152 +                ::defaultValue(JSContext *cx, HandleObject obj,
   1.153 +                               JSType hint, MutableHandleValue vp)
   1.154 +{
   1.155 +    JSString *str = JS_NewStringCopyZ(cx, "[Opaque]");
   1.156 +    if (!str)
   1.157 +        return false;
   1.158 +    vp.set(JS::StringValue(str));
   1.159 +    return true;
   1.160 +}
   1.161 +
   1.162 +
   1.163 +template <typename Base, typename Policy>
   1.164 +bool
   1.165 +FilteringWrapper<Base, Policy>::enter(JSContext *cx, HandleObject wrapper,
   1.166 +                                      HandleId id, Wrapper::Action act, bool *bp)
   1.167 +{
   1.168 +    // This is a super ugly hacky to get around Xray Resolve wonkiness.
   1.169 +    //
   1.170 +    // Basically, XPCWN Xrays sometimes call into the Resolve hook of the
   1.171 +    // scriptable helper, and pass the wrapper itself as the object upon which
   1.172 +    // the resolve is happening. Then, special handling happens in
   1.173 +    // XrayWrapper::defineProperty to detect the resolve and redefine the
   1.174 +    // property on the holder. Really, we should just pass the holder itself to
   1.175 +    // NewResolve, but there's too much code in nsDOMClassInfo that assumes this
   1.176 +    // isn't the case (in particular, code expects to be able to look up
   1.177 +    // properties on the object, which doesn't work for the holder). Given that
   1.178 +    // these hooks are going away eventually with the new DOM bindings, let's
   1.179 +    // just hack around this for now.
   1.180 +    if (XrayUtils::IsXrayResolving(cx, wrapper, id)) {
   1.181 +        *bp = true;
   1.182 +        return true;
   1.183 +    }
   1.184 +    if (!Policy::check(cx, wrapper, id, act)) {
   1.185 +        *bp = JS_IsExceptionPending(cx) ? false : Policy::deny(act, id);
   1.186 +        return false;
   1.187 +    }
   1.188 +    *bp = true;
   1.189 +    return true;
   1.190 +}
   1.191 +
   1.192 +#define XOW FilteringWrapper<SecurityXrayXPCWN, CrossOriginAccessiblePropertiesOnly>
   1.193 +#define DXOW   FilteringWrapper<SecurityXrayDOM, CrossOriginAccessiblePropertiesOnly>
   1.194 +#define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>
   1.195 +#define NNXOWC FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>
   1.196 +#define GO FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque>
   1.197 +template<> XOW XOW::singleton(0);
   1.198 +template<> DXOW DXOW::singleton(0);
   1.199 +template<> NNXOW NNXOW::singleton(0);
   1.200 +template<> NNXOWC NNXOWC::singleton(0);
   1.201 +
   1.202 +template<> GO GO::singleton(0);
   1.203 +
   1.204 +template class XOW;
   1.205 +template class DXOW;
   1.206 +template class NNXOW;
   1.207 +template class NNXOWC;
   1.208 +template class ChromeObjectWrapperBase;
   1.209 +template class GO;
   1.210 +}

mercurial