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 +}