|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "FilteringWrapper.h" |
|
8 #include "AccessCheck.h" |
|
9 #include "ChromeObjectWrapper.h" |
|
10 #include "XrayWrapper.h" |
|
11 |
|
12 #include "jsapi.h" |
|
13 |
|
14 using namespace JS; |
|
15 using namespace js; |
|
16 |
|
17 namespace xpc { |
|
18 |
|
19 template <typename Base, typename Policy> |
|
20 FilteringWrapper<Base, Policy>::FilteringWrapper(unsigned flags) : Base(flags) |
|
21 { |
|
22 } |
|
23 |
|
24 template <typename Base, typename Policy> |
|
25 FilteringWrapper<Base, Policy>::~FilteringWrapper() |
|
26 { |
|
27 } |
|
28 |
|
29 template <typename Policy> |
|
30 static bool |
|
31 Filter(JSContext *cx, HandleObject wrapper, AutoIdVector &props) |
|
32 { |
|
33 size_t w = 0; |
|
34 RootedId id(cx); |
|
35 for (size_t n = 0; n < props.length(); ++n) { |
|
36 id = props[n]; |
|
37 if (Policy::check(cx, wrapper, id, Wrapper::GET)) |
|
38 props[w++] = id; |
|
39 else if (JS_IsExceptionPending(cx)) |
|
40 return false; |
|
41 } |
|
42 props.resize(w); |
|
43 return true; |
|
44 } |
|
45 |
|
46 template <typename Policy> |
|
47 static bool |
|
48 FilterSetter(JSContext *cx, JSObject *wrapper, jsid id, JS::MutableHandle<JSPropertyDescriptor> desc) |
|
49 { |
|
50 bool setAllowed = Policy::check(cx, wrapper, id, Wrapper::SET); |
|
51 if (!setAllowed) { |
|
52 if (JS_IsExceptionPending(cx)) |
|
53 return false; |
|
54 desc.setSetter(nullptr); |
|
55 } |
|
56 return true; |
|
57 } |
|
58 |
|
59 template <typename Base, typename Policy> |
|
60 bool |
|
61 FilteringWrapper<Base, Policy>::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, |
|
62 HandleId id, |
|
63 JS::MutableHandle<JSPropertyDescriptor> desc) |
|
64 { |
|
65 assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); |
|
66 if (!Base::getPropertyDescriptor(cx, wrapper, id, desc)) |
|
67 return false; |
|
68 return FilterSetter<Policy>(cx, wrapper, id, desc); |
|
69 } |
|
70 |
|
71 template <typename Base, typename Policy> |
|
72 bool |
|
73 FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, |
|
74 HandleId id, |
|
75 JS::MutableHandle<JSPropertyDescriptor> desc) |
|
76 { |
|
77 assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); |
|
78 if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc)) |
|
79 return false; |
|
80 return FilterSetter<Policy>(cx, wrapper, id, desc); |
|
81 } |
|
82 |
|
83 template <typename Base, typename Policy> |
|
84 bool |
|
85 FilteringWrapper<Base, Policy>::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, |
|
86 AutoIdVector &props) |
|
87 { |
|
88 assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
|
89 return Base::getOwnPropertyNames(cx, wrapper, props) && |
|
90 Filter<Policy>(cx, wrapper, props); |
|
91 } |
|
92 |
|
93 template <typename Base, typename Policy> |
|
94 bool |
|
95 FilteringWrapper<Base, Policy>::enumerate(JSContext *cx, HandleObject wrapper, |
|
96 AutoIdVector &props) |
|
97 { |
|
98 assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
|
99 return Base::enumerate(cx, wrapper, props) && |
|
100 Filter<Policy>(cx, wrapper, props); |
|
101 } |
|
102 |
|
103 template <typename Base, typename Policy> |
|
104 bool |
|
105 FilteringWrapper<Base, Policy>::keys(JSContext *cx, HandleObject wrapper, |
|
106 AutoIdVector &props) |
|
107 { |
|
108 assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
|
109 return Base::keys(cx, wrapper, props) && |
|
110 Filter<Policy>(cx, wrapper, props); |
|
111 } |
|
112 |
|
113 template <typename Base, typename Policy> |
|
114 bool |
|
115 FilteringWrapper<Base, Policy>::iterate(JSContext *cx, HandleObject wrapper, |
|
116 unsigned flags, MutableHandleValue vp) |
|
117 { |
|
118 assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
|
119 // We refuse to trigger the iterator hook across chrome wrappers because |
|
120 // we don't know how to censor custom iterator objects. Instead we trigger |
|
121 // the default proxy iterate trap, which will ask enumerate() for the list |
|
122 // of (censored) ids. |
|
123 return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); |
|
124 } |
|
125 |
|
126 template <typename Base, typename Policy> |
|
127 bool |
|
128 FilteringWrapper<Base, Policy>::nativeCall(JSContext *cx, JS::IsAcceptableThis test, |
|
129 JS::NativeImpl impl, JS::CallArgs args) |
|
130 { |
|
131 if (Policy::allowNativeCall(cx, test, impl)) |
|
132 return Base::Permissive::nativeCall(cx, test, impl, args); |
|
133 return Base::Restrictive::nativeCall(cx, test, impl, args); |
|
134 } |
|
135 |
|
136 template <typename Base, typename Policy> |
|
137 bool |
|
138 FilteringWrapper<Base, Policy>::defaultValue(JSContext *cx, HandleObject obj, |
|
139 JSType hint, MutableHandleValue vp) |
|
140 { |
|
141 return Base::defaultValue(cx, obj, hint, vp); |
|
142 } |
|
143 |
|
144 // With our entirely-opaque wrapper, the DefaultValue algorithm throws, |
|
145 // causing spurious exceptions. Manually implement something benign. |
|
146 template<> |
|
147 bool |
|
148 FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque> |
|
149 ::defaultValue(JSContext *cx, HandleObject obj, |
|
150 JSType hint, MutableHandleValue vp) |
|
151 { |
|
152 JSString *str = JS_NewStringCopyZ(cx, "[Opaque]"); |
|
153 if (!str) |
|
154 return false; |
|
155 vp.set(JS::StringValue(str)); |
|
156 return true; |
|
157 } |
|
158 |
|
159 |
|
160 template <typename Base, typename Policy> |
|
161 bool |
|
162 FilteringWrapper<Base, Policy>::enter(JSContext *cx, HandleObject wrapper, |
|
163 HandleId id, Wrapper::Action act, bool *bp) |
|
164 { |
|
165 // This is a super ugly hacky to get around Xray Resolve wonkiness. |
|
166 // |
|
167 // Basically, XPCWN Xrays sometimes call into the Resolve hook of the |
|
168 // scriptable helper, and pass the wrapper itself as the object upon which |
|
169 // the resolve is happening. Then, special handling happens in |
|
170 // XrayWrapper::defineProperty to detect the resolve and redefine the |
|
171 // property on the holder. Really, we should just pass the holder itself to |
|
172 // NewResolve, but there's too much code in nsDOMClassInfo that assumes this |
|
173 // isn't the case (in particular, code expects to be able to look up |
|
174 // properties on the object, which doesn't work for the holder). Given that |
|
175 // these hooks are going away eventually with the new DOM bindings, let's |
|
176 // just hack around this for now. |
|
177 if (XrayUtils::IsXrayResolving(cx, wrapper, id)) { |
|
178 *bp = true; |
|
179 return true; |
|
180 } |
|
181 if (!Policy::check(cx, wrapper, id, act)) { |
|
182 *bp = JS_IsExceptionPending(cx) ? false : Policy::deny(act, id); |
|
183 return false; |
|
184 } |
|
185 *bp = true; |
|
186 return true; |
|
187 } |
|
188 |
|
189 #define XOW FilteringWrapper<SecurityXrayXPCWN, CrossOriginAccessiblePropertiesOnly> |
|
190 #define DXOW FilteringWrapper<SecurityXrayDOM, CrossOriginAccessiblePropertiesOnly> |
|
191 #define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque> |
|
192 #define NNXOWC FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall> |
|
193 #define GO FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque> |
|
194 template<> XOW XOW::singleton(0); |
|
195 template<> DXOW DXOW::singleton(0); |
|
196 template<> NNXOW NNXOW::singleton(0); |
|
197 template<> NNXOWC NNXOWC::singleton(0); |
|
198 |
|
199 template<> GO GO::singleton(0); |
|
200 |
|
201 template class XOW; |
|
202 template class DXOW; |
|
203 template class NNXOW; |
|
204 template class NNXOWC; |
|
205 template class ChromeObjectWrapperBase; |
|
206 template class GO; |
|
207 } |