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 #ifndef jswrapper_h
8 #define jswrapper_h
9 
10 #include "mozilla/Attributes.h"
11 
12 #include "js/Proxy.h"
13 
14 namespace js {
15 
16 /*
17  * Helper for Wrapper::New default options.
18  *
19  * Callers of Wrapper::New() who wish to specify a prototype for the created
20  * Wrapper, *MUST* construct a WrapperOptions with a JSContext.
21  */
22 class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions {
23   public:
WrapperOptions()24     WrapperOptions() : ProxyOptions(false),
25                        proto_()
26     {}
27 
WrapperOptions(JSContext * cx)28     explicit WrapperOptions(JSContext* cx) : ProxyOptions(false),
29                                              proto_()
30     {
31         proto_.emplace(cx);
32     }
33 
34     inline JSObject* proto() const;
setProto(JSObject * protoArg)35     WrapperOptions& setProto(JSObject* protoArg) {
36         MOZ_ASSERT(proto_);
37         *proto_ = protoArg;
38         return *this;
39     }
40 
41   private:
42     mozilla::Maybe<JS::RootedObject> proto_;
43 };
44 
45 /*
46  * A wrapper is a proxy with a target object to which it generally forwards
47  * operations, but may restrict access to certain operations or instrument the
48  * methods in various ways. A wrapper is distinct from a Direct Proxy Handler
49  * in the sense that it can be "unwrapped" in C++, exposing the underlying
50  * object (Direct Proxy Handlers have an underlying target object, but don't
51  * expect to expose this object via any kind of unwrapping operation). Callers
52  * should be careful to avoid unwrapping security wrappers in the wrong
53  * context.
54  */
JS_FRIEND_API(Wrapper)55 class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
56 {
57     unsigned mFlags;
58 
59   public:
60     using BaseProxyHandler::Action;
61 
62     enum Flags {
63         CROSS_COMPARTMENT = 1 << 0,
64         LAST_USED_FLAG = CROSS_COMPARTMENT
65     };
66 
67     static JSObject* New(JSContext* cx, JSObject* obj, const Wrapper* handler,
68                          const WrapperOptions& options = WrapperOptions());
69 
70     static JSObject* Renew(JSContext* cx, JSObject* existing, JSObject* obj, const Wrapper* handler);
71 
72     static const Wrapper* wrapperHandler(JSObject* wrapper);
73 
74     static JSObject* wrappedObject(JSObject* wrapper);
75 
76     unsigned flags() const {
77         return mFlags;
78     }
79 
80     explicit MOZ_CONSTEXPR Wrapper(unsigned aFlags, bool aHasPrototype = false,
81                                    bool aHasSecurityPolicy = false)
82       : DirectProxyHandler(&family, aHasPrototype, aHasSecurityPolicy),
83         mFlags(aFlags)
84     { }
85 
86     virtual bool finalizeInBackground(Value priv) const override;
87     virtual bool isConstructor(JSObject* obj) const override;
88 
89     static const char family;
90     static const Wrapper singleton;
91     static const Wrapper singletonWithPrototype;
92 
93     static JSObject* defaultProto;
94 };
95 
96 inline JSObject*
proto()97 WrapperOptions::proto() const
98 {
99     return proto_ ? *proto_ : Wrapper::defaultProto;
100 }
101 
102 /* Base class for all cross compartment wrapper handlers. */
JS_FRIEND_API(CrossCompartmentWrapper)103 class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
104 {
105   public:
106     explicit MOZ_CONSTEXPR CrossCompartmentWrapper(unsigned aFlags, bool aHasPrototype = false,
107                                                    bool aHasSecurityPolicy = false)
108       : Wrapper(CROSS_COMPARTMENT | aFlags, aHasPrototype, aHasSecurityPolicy)
109     { }
110 
111     /* Standard internal methods. */
112     virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
113                                           MutableHandle<JSPropertyDescriptor> desc) const override;
114     virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
115                                 Handle<JSPropertyDescriptor> desc,
116                                 ObjectOpResult& result) const override;
117     virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper,
118                                  AutoIdVector& props) const override;
119     virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id,
120                          ObjectOpResult& result) const override;
121     virtual bool enumerate(JSContext* cx, HandleObject wrapper, MutableHandleObject objp) const override;
122     virtual bool getPrototype(JSContext* cx, HandleObject proxy,
123                               MutableHandleObject protop) const override;
124     virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
125                               ObjectOpResult& result) const override;
126 
127     virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
128                                        bool* succeeded) const override;
129     virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
130                                    ObjectOpResult& result) const override;
131     virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
132     virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
133     virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
134                      HandleId id, MutableHandleValue vp) const override;
135     virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
136                      HandleValue receiver, ObjectOpResult& result) const override;
137     virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
138     virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
139 
140     /* SpiderMonkey extensions. */
141     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
142                                        MutableHandle<JSPropertyDescriptor> desc) const override;
143     virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
144     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
145                                               AutoIdVector& props) const override;
146     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
147                             const CallArgs& args) const override;
148     virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
149                              bool* bp) const override;
150     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
151     virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
152                                    unsigned indent) const override;
153     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
154     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
155 
156     static const CrossCompartmentWrapper singleton;
157     static const CrossCompartmentWrapper singletonWithPrototype;
158 };
159 
JS_FRIEND_API(OpaqueCrossCompartmentWrapper)160 class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrapper
161 {
162   public:
163     explicit MOZ_CONSTEXPR OpaqueCrossCompartmentWrapper() : CrossCompartmentWrapper(0)
164     { }
165 
166     /* Standard internal methods. */
167     virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
168                                           MutableHandle<JSPropertyDescriptor> desc) const override;
169     virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
170                                 Handle<JSPropertyDescriptor> desc,
171                                 ObjectOpResult& result) const override;
172     virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper,
173                                  AutoIdVector& props) const override;
174     virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id,
175                          ObjectOpResult& result) const override;
176     virtual bool enumerate(JSContext* cx, HandleObject wrapper,
177                            MutableHandleObject objp) const override;
178     virtual bool getPrototype(JSContext* cx, HandleObject wrapper,
179                               MutableHandleObject protop) const override;
180     virtual bool setPrototype(JSContext* cx, HandleObject wrapper, HandleObject proto,
181                               ObjectOpResult& result) const override;
182     virtual bool setImmutablePrototype(JSContext* cx, HandleObject wrapper,
183                                        bool* succeeded) const override;
184     virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
185                                    ObjectOpResult& result) const override;
186     virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
187     virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id,
188                      bool* bp) const override;
189     virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
190                      HandleId id, MutableHandleValue vp) const override;
191     virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
192                      HandleValue receiver, ObjectOpResult& result) const override;
193     virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
194     virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
195 
196     /* SpiderMonkey extensions. */
197     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
198                                        MutableHandle<JSPropertyDescriptor> desc) const override;
199     virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id,
200                         bool* bp) const override;
201     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
202                                               AutoIdVector& props) const override;
203     virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper,
204                                  ESClassValue* classValue) const override;
205     virtual bool isArray(JSContext* cx, HandleObject obj,
206                          JS::IsArrayAnswer* answer) const override;
207     virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
208     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
209 
210     static const OpaqueCrossCompartmentWrapper singleton;
211 };
212 
213 /*
214  * Base class for security wrappers. A security wrapper is potentially hiding
215  * all or part of some wrapped object thus SecurityWrapper defaults to denying
216  * access to the wrappee. This is the opposite of Wrapper which tries to be
217  * completely transparent.
218  *
219  * NB: Currently, only a few ProxyHandler operations are overridden to deny
220  * access, relying on derived SecurityWrapper to block access when necessary.
221  */
222 template <class Base>
JS_FRIEND_API(SecurityWrapper)223 class JS_FRIEND_API(SecurityWrapper) : public Base
224 {
225   public:
226     explicit MOZ_CONSTEXPR SecurityWrapper(unsigned flags, bool hasPrototype = false)
227       : Base(flags, hasPrototype, /* hasSecurityPolicy = */ true)
228     { }
229 
230     virtual bool enter(JSContext* cx, HandleObject wrapper, HandleId id, Wrapper::Action act,
231                        bool* bp) const override;
232 
233     virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
234                                 Handle<JSPropertyDescriptor> desc,
235                                 ObjectOpResult& result) const override;
236     virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
237     virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
238                                    ObjectOpResult& result) const override;
239     virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
240                               ObjectOpResult& result) const override;
241     virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const override;
242 
243     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
244                             const CallArgs& args) const override;
245     virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper,
246                                  ESClassValue* classValue) const override;
247     virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
248     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
249     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
250 
251     // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
252     // against.
253 
254     virtual bool watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
255                        JS::HandleObject callable) const override;
256     virtual bool unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id) const override;
257 
258     /*
259      * Allow our subclasses to select the superclass behavior they want without
260      * needing to specify an exact superclass.
261      */
262     typedef Base Permissive;
263     typedef SecurityWrapper<Base> Restrictive;
264 };
265 
266 typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper;
267 typedef SecurityWrapper<CrossCompartmentWrapper> CrossCompartmentSecurityWrapper;
268 
269 extern JSObject*
270 TransparentObjectWrapper(JSContext* cx, HandleObject existing, HandleObject obj);
271 
272 inline bool
IsWrapper(JSObject * obj)273 IsWrapper(JSObject* obj)
274 {
275     return IsProxy(obj) && GetProxyHandler(obj)->family() == &Wrapper::family;
276 }
277 
278 // Given a JSObject, returns that object stripped of wrappers. If
279 // stopAtWindowProxy is true, then this returns the WindowProxy if it was
280 // previously wrapped. Otherwise, this returns the first object for
281 // which JSObject::isWrapper returns false.
282 JS_FRIEND_API(JSObject*)
283 UncheckedUnwrap(JSObject* obj, bool stopAtWindowProxy = true, unsigned* flagsp = nullptr);
284 
285 // Given a JSObject, returns that object stripped of wrappers. At each stage,
286 // the security wrapper has the opportunity to veto the unwrap. If
287 // stopAtWindowProxy is true, then this returns the WindowProxy if it was
288 // previously wrapped.
289 JS_FRIEND_API(JSObject*)
290 CheckedUnwrap(JSObject* obj, bool stopAtWindowProxy = true);
291 
292 // Unwrap only the outermost security wrapper, with the same semantics as
293 // above. This is the checked version of Wrapper::wrappedObject.
294 JS_FRIEND_API(JSObject*)
295 UnwrapOneChecked(JSObject* obj, bool stopAtWindowProxy = true);
296 
297 JS_FRIEND_API(bool)
298 IsCrossCompartmentWrapper(JSObject* obj);
299 
300 void
301 NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper);
302 
303 bool
304 RemapWrapper(JSContext* cx, JSObject* wobj, JSObject* newTarget);
305 
306 JS_FRIEND_API(bool)
307 RemapAllWrappersForObject(JSContext* cx, JSObject* oldTarget,
308                           JSObject* newTarget);
309 
310 // API to recompute all cross-compartment wrappers whose source and target
311 // match the given filters.
312 JS_FRIEND_API(bool)
313 RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
314                   const CompartmentFilter& targetFilter);
315 
316 } /* namespace js */
317 
318 #endif /* jswrapper_h */
319