LCOV - code coverage report
Current view: top level - js/xpconnect/wrappers - FilteringWrapper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 118 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 55 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "nsJSUtils.h"
      12             : #include "mozilla/ErrorResult.h"
      13             : 
      14             : #include "jsapi.h"
      15             : 
      16             : using namespace JS;
      17             : using namespace js;
      18             : 
      19             : namespace xpc {
      20             : 
      21             : static JS::SymbolCode sCrossOriginWhitelistedSymbolCodes[] = {
      22             :     JS::SymbolCode::toStringTag,
      23             :     JS::SymbolCode::hasInstance,
      24             :     JS::SymbolCode::isConcatSpreadable
      25             : };
      26             : 
      27             : bool
      28           0 : IsCrossOriginWhitelistedSymbol(JSContext* cx, JS::HandleId id)
      29             : {
      30           0 :     if (!JSID_IS_SYMBOL(id)) {
      31           0 :         return false;
      32             :     }
      33             : 
      34           0 :     JS::Symbol* symbol = JSID_TO_SYMBOL(id);
      35           0 :     for (auto code : sCrossOriginWhitelistedSymbolCodes) {
      36           0 :         if (symbol == JS::GetWellKnownSymbol(cx, code)) {
      37           0 :             return true;
      38             :         }
      39             :     }
      40             : 
      41           0 :     return false;
      42             : }
      43             : 
      44             : template <typename Policy>
      45             : static bool
      46           0 : Filter(JSContext* cx, HandleObject wrapper, AutoIdVector& props)
      47             : {
      48           0 :     size_t w = 0;
      49           0 :     RootedId id(cx);
      50           0 :     for (size_t n = 0; n < props.length(); ++n) {
      51           0 :         id = props[n];
      52           0 :         if (Policy::check(cx, wrapper, id, Wrapper::GET) || Policy::check(cx, wrapper, id, Wrapper::SET))
      53           0 :             props[w++].set(id);
      54           0 :         else if (JS_IsExceptionPending(cx))
      55           0 :             return false;
      56             :     }
      57           0 :     if (!props.resize(w))
      58           0 :         return false;
      59             : 
      60           0 :     return true;
      61             : }
      62             : 
      63             : template <typename Policy>
      64             : static bool
      65           0 : FilterPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle<PropertyDescriptor> desc)
      66             : {
      67           0 :     MOZ_ASSERT(!JS_IsExceptionPending(cx));
      68           0 :     bool getAllowed = Policy::check(cx, wrapper, id, Wrapper::GET);
      69           0 :     if (JS_IsExceptionPending(cx))
      70           0 :         return false;
      71           0 :     bool setAllowed = Policy::check(cx, wrapper, id, Wrapper::SET);
      72           0 :     if (JS_IsExceptionPending(cx))
      73           0 :         return false;
      74             : 
      75           0 :     MOZ_ASSERT(getAllowed || setAllowed,
      76             :                "Filtering policy should not allow GET_PROPERTY_DESCRIPTOR in this case");
      77             : 
      78           0 :     if (!desc.hasGetterOrSetter()) {
      79             :         // Handle value properties.
      80           0 :         if (!getAllowed)
      81           0 :             desc.value().setUndefined();
      82             :     } else {
      83             :         // Handle accessor properties.
      84           0 :         MOZ_ASSERT(desc.value().isUndefined());
      85           0 :         if (!getAllowed)
      86           0 :             desc.setGetter(nullptr);
      87           0 :         if (!setAllowed)
      88           0 :             desc.setSetter(nullptr);
      89             :     }
      90             : 
      91           0 :     return true;
      92             : }
      93             : 
      94             : template <typename Base, typename Policy>
      95             : bool
      96           0 : FilteringWrapper<Base, Policy>::getPropertyDescriptor(JSContext* cx, HandleObject wrapper,
      97             :                                                       HandleId id,
      98             :                                                       MutableHandle<PropertyDescriptor> desc) const
      99             : {
     100           0 :     assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
     101             :                                          BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
     102           0 :     if (!Base::getPropertyDescriptor(cx, wrapper, id, desc))
     103           0 :         return false;
     104           0 :     return FilterPropertyDescriptor<Policy>(cx, wrapper, id, desc);
     105             : }
     106             : 
     107             : template <typename Base, typename Policy>
     108             : bool
     109           0 : FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper,
     110             :                                                          HandleId id,
     111             :                                                          MutableHandle<PropertyDescriptor> desc) const
     112             : {
     113           0 :     assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
     114             :                                          BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
     115           0 :     if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc))
     116           0 :         return false;
     117           0 :     return FilterPropertyDescriptor<Policy>(cx, wrapper, id, desc);
     118             : }
     119             : 
     120             : template <typename Base, typename Policy>
     121             : bool
     122           0 : FilteringWrapper<Base, Policy>::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
     123             :                                                 AutoIdVector& props) const
     124             : {
     125           0 :     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
     126           0 :     return Base::ownPropertyKeys(cx, wrapper, props) &&
     127           0 :            Filter<Policy>(cx, wrapper, props);
     128             : }
     129             : 
     130             : template <typename Base, typename Policy>
     131             : bool
     132           0 : FilteringWrapper<Base, Policy>::getOwnEnumerablePropertyKeys(JSContext* cx,
     133             :                                                              HandleObject wrapper,
     134             :                                                              AutoIdVector& props) const
     135             : {
     136           0 :     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
     137           0 :     return Base::getOwnEnumerablePropertyKeys(cx, wrapper, props) &&
     138           0 :            Filter<Policy>(cx, wrapper, props);
     139             : }
     140             : 
     141             : template <typename Base, typename Policy>
     142             : JSObject*
     143           0 : FilteringWrapper<Base, Policy>::enumerate(JSContext* cx, HandleObject wrapper) const
     144             : {
     145           0 :     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
     146             :     // We refuse to trigger the enumerate hook across chrome wrappers because
     147             :     // we don't know how to censor custom iterator objects. Instead we trigger
     148             :     // the default proxy enumerate trap, which will use js::GetPropertyKeys
     149             :     // for the list of (censored) ids.
     150           0 :     return js::BaseProxyHandler::enumerate(cx, wrapper);
     151             : }
     152             : 
     153             : template <typename Base, typename Policy>
     154             : bool
     155           0 : FilteringWrapper<Base, Policy>::call(JSContext* cx, JS::Handle<JSObject*> wrapper,
     156             :                                     const JS::CallArgs& args) const
     157             : {
     158           0 :     if (!Policy::checkCall(cx, wrapper, args))
     159           0 :         return false;
     160           0 :     return Base::call(cx, wrapper, args);
     161             : }
     162             : 
     163             : template <typename Base, typename Policy>
     164             : bool
     165           0 : FilteringWrapper<Base, Policy>::construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
     166             :                                           const JS::CallArgs& args) const
     167             : {
     168           0 :     if (!Policy::checkCall(cx, wrapper, args))
     169           0 :         return false;
     170           0 :     return Base::construct(cx, wrapper, args);
     171             : }
     172             : 
     173             : template <typename Base, typename Policy>
     174             : bool
     175           0 : FilteringWrapper<Base, Policy>::nativeCall(JSContext* cx, JS::IsAcceptableThis test,
     176             :                                            JS::NativeImpl impl, const JS::CallArgs& args) const
     177             : {
     178           0 :     if (Policy::allowNativeCall(cx, test, impl))
     179           0 :         return Base::Permissive::nativeCall(cx, test, impl, args);
     180           0 :     return Base::Restrictive::nativeCall(cx, test, impl, args);
     181             : }
     182             : 
     183             : template <typename Base, typename Policy>
     184             : bool
     185           0 : FilteringWrapper<Base, Policy>::getPrototype(JSContext* cx, JS::HandleObject wrapper,
     186             :                                              JS::MutableHandleObject protop) const
     187             : {
     188             :     // Filtering wrappers do not allow access to the prototype.
     189           0 :     protop.set(nullptr);
     190           0 :     return true;
     191             : }
     192             : 
     193             : template <typename Base, typename Policy>
     194             : bool
     195           0 : FilteringWrapper<Base, Policy>::enter(JSContext* cx, HandleObject wrapper,
     196             :                                       HandleId id, Wrapper::Action act,
     197             :                                       bool mayThrow, bool* bp) const
     198             : {
     199           0 :     if (!Policy::check(cx, wrapper, id, act)) {
     200           0 :         *bp = JS_IsExceptionPending(cx) ?
     201           0 :             false : Policy::deny(cx, act, id, mayThrow);
     202           0 :         return false;
     203             :     }
     204           0 :     *bp = true;
     205           0 :     return true;
     206             : }
     207             : 
     208             : bool
     209           0 : CrossOriginXrayWrapper::getPropertyDescriptor(JSContext* cx,
     210             :                                               JS::Handle<JSObject*> wrapper,
     211             :                                               JS::Handle<jsid> id,
     212             :                                               JS::MutableHandle<PropertyDescriptor> desc) const
     213             : {
     214           0 :     if (!SecurityXrayDOM::getPropertyDescriptor(cx, wrapper, id, desc))
     215           0 :         return false;
     216           0 :     if (desc.object()) {
     217             :         // Cross-origin DOM objects do not have symbol-named properties apart
     218             :         // from the ones we add ourselves here.
     219           0 :         MOZ_ASSERT(!JSID_IS_SYMBOL(id),
     220             :                    "What's this symbol-named property that appeared on a "
     221             :                    "Window or Location instance?");
     222             : 
     223             :         // All properties on cross-origin DOM objects are |own|.
     224           0 :         desc.object().set(wrapper);
     225             : 
     226             :         // All properties on cross-origin DOM objects are non-enumerable and
     227             :         // "configurable". Any value attributes are read-only.
     228           0 :         desc.attributesRef() &= ~JSPROP_ENUMERATE;
     229           0 :         desc.attributesRef() &= ~JSPROP_PERMANENT;
     230           0 :         if (!desc.getter() && !desc.setter())
     231           0 :             desc.attributesRef() |= JSPROP_READONLY;
     232           0 :     } else if (IsCrossOriginWhitelistedSymbol(cx, id)) {
     233             :         // Spec says to return PropertyDescriptor {
     234             :         //   [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false,
     235             :         //   [[Configurable]]: true
     236             :         // }.
     237             :         //
     238           0 :         desc.setDataDescriptor(JS::UndefinedHandleValue, JSPROP_READONLY);
     239           0 :         desc.object().set(wrapper);
     240             :     }
     241             : 
     242           0 :     return true;
     243             : }
     244             : 
     245             : bool
     246           0 : CrossOriginXrayWrapper::getOwnPropertyDescriptor(JSContext* cx,
     247             :                                                  JS::Handle<JSObject*> wrapper,
     248             :                                                  JS::Handle<jsid> id,
     249             :                                                  JS::MutableHandle<PropertyDescriptor> desc) const
     250             : {
     251             :     // All properties on cross-origin DOM objects are |own|.
     252           0 :     return getPropertyDescriptor(cx, wrapper, id, desc);
     253             : }
     254             : 
     255             : bool
     256           0 : CrossOriginXrayWrapper::ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
     257             :                                         JS::AutoIdVector& props) const
     258             : {
     259             :     // All properties on cross-origin objects are supposed |own|, despite what
     260             :     // the underlying native object may report. Override the inherited trap to
     261             :     // avoid passing JSITER_OWNONLY as a flag.
     262           0 :     if (!SecurityXrayDOM::getPropertyKeys(cx, wrapper, JSITER_HIDDEN, props)) {
     263           0 :         return false;
     264             :     }
     265             : 
     266             :     // Now add the three symbol-named props cross-origin objects have.
     267             : #ifdef DEBUG
     268           0 :     for (size_t n = 0; n < props.length(); ++n) {
     269           0 :         MOZ_ASSERT(!JSID_IS_SYMBOL(props[n]),
     270             :                    "Unexpected existing symbol-name prop");
     271             :     }
     272             : #endif
     273           0 :     if (!props.reserve(props.length() +
     274           0 :                        ArrayLength(sCrossOriginWhitelistedSymbolCodes))) {
     275           0 :         return false;
     276             :     }
     277             : 
     278           0 :     for (auto code : sCrossOriginWhitelistedSymbolCodes) {
     279           0 :         props.infallibleAppend(SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, code)));
     280             :     }
     281             : 
     282           0 :     return true;
     283             : }
     284             : 
     285             : bool
     286           0 : CrossOriginXrayWrapper::defineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
     287             :                                        JS::Handle<jsid> id,
     288             :                                        JS::Handle<PropertyDescriptor> desc,
     289             :                                        JS::ObjectOpResult& result) const
     290             : {
     291           0 :     AccessCheck::reportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("define"));
     292           0 :     return false;
     293             : }
     294             : 
     295             : bool
     296           0 : CrossOriginXrayWrapper::delete_(JSContext* cx, JS::Handle<JSObject*> wrapper,
     297             :                                 JS::Handle<jsid> id, JS::ObjectOpResult& result) const
     298             : {
     299           0 :     AccessCheck::reportCrossOriginDenial(cx, id, NS_LITERAL_CSTRING("delete"));
     300           0 :     return false;
     301             : }
     302             : 
     303             : bool
     304           0 : CrossOriginXrayWrapper::setPrototype(JSContext* cx, JS::HandleObject wrapper,
     305             :                                      JS::HandleObject proto,
     306             :                                      JS::ObjectOpResult& result) const
     307             : {
     308             :     // https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-setprototypeof
     309             :     // and
     310             :     // https://html.spec.whatwg.org/multipage/browsers.html#location-setprototypeof
     311             :     // both say to call SetImmutablePrototype, which does nothing and just
     312             :     // returns whether the passed-in value equals the current prototype.  Our
     313             :     // current prototype is always null, so this just comes down to returning
     314             :     // whether null was passed in.
     315             :     //
     316             :     // In terms of ObjectOpResult that means calling one of the fail*() things
     317             :     // on it if non-null was passed, and it's got one that does just what we
     318             :     // want.
     319           0 :     if (!proto) {
     320           0 :         return result.succeed();
     321             :     }
     322           0 :     return result.failCantSetProto();
     323             : }
     324             : 
     325             : #define XOW FilteringWrapper<CrossOriginXrayWrapper, CrossOriginAccessiblePropertiesOnly>
     326             : #define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>
     327             : #define NNXOWC FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>
     328             : 
     329             : template<> const XOW XOW::singleton(0);
     330             : template<> const NNXOW NNXOW::singleton(0);
     331             : template<> const NNXOWC NNXOWC::singleton(0);
     332             : 
     333             : template class XOW;
     334             : template class NNXOW;
     335             : template class NNXOWC;
     336             : template class ChromeObjectWrapperBase;
     337             : } // namespace xpc

Generated by: LCOV version 1.13