FilteringWrapper.cpp (5696B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 #include "xpcpublic.h" 14 #include "xpcprivate.h" 15 16 #include "jsapi.h" 17 #include "js/Symbol.h" 18 19 using namespace JS; 20 using namespace js; 21 22 namespace xpc { 23 24 static JS::SymbolCode sCrossOriginWhitelistedSymbolCodes[] = { 25 JS::SymbolCode::toStringTag, JS::SymbolCode::hasInstance, 26 JS::SymbolCode::isConcatSpreadable}; 27 28 static bool IsCrossOriginWhitelistedSymbol(JSContext* cx, JS::HandleId id) { 29 if (!id.isSymbol()) { 30 return false; 31 } 32 33 JS::Symbol* symbol = id.toSymbol(); 34 for (auto code : sCrossOriginWhitelistedSymbolCodes) { 35 if (symbol == JS::GetWellKnownSymbol(cx, code)) { 36 return true; 37 } 38 } 39 40 return false; 41 } 42 43 bool IsCrossOriginWhitelistedProp(JSContext* cx, JS::HandleId id) { 44 return id == GetJSIDByIndex(cx, XPCJSContext::IDX_THEN) || 45 IsCrossOriginWhitelistedSymbol(cx, id); 46 } 47 48 bool AppendCrossOriginWhitelistedPropNames(JSContext* cx, 49 JS::MutableHandleIdVector props) { 50 // Add "then" if it's not already in the list. 51 RootedIdVector thenProp(cx); 52 if (!thenProp.append(GetJSIDByIndex(cx, XPCJSContext::IDX_THEN))) { 53 return false; 54 } 55 56 if (!AppendUnique(cx, props, thenProp)) { 57 return false; 58 } 59 60 // Now add the three symbol-named props cross-origin objects have. 61 #ifdef DEBUG 62 for (size_t n = 0; n < props.length(); ++n) { 63 MOZ_ASSERT(!props[n].isSymbol(), "Unexpected existing symbol-name prop"); 64 } 65 #endif 66 if (!props.reserve(props.length() + 67 std::size(sCrossOriginWhitelistedSymbolCodes))) { 68 return false; 69 } 70 71 for (auto code : sCrossOriginWhitelistedSymbolCodes) { 72 props.infallibleAppend(JS::GetWellKnownSymbolKey(cx, code)); 73 } 74 75 return true; 76 } 77 78 // Note: Previously, FilteringWrapper supported complex access policies where 79 // certain properties on an object were accessible and others weren't. Today, 80 // the only supported policies are Opaque and OpaqueWithCall, none of which need 81 // that. So we just stub out the unreachable paths. 82 template <typename Base, typename Policy> 83 bool FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor( 84 JSContext* cx, HandleObject wrapper, HandleId id, 85 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const { 86 MOZ_CRASH("FilteringWrappers are now always opaque"); 87 } 88 89 template <typename Base, typename Policy> 90 bool FilteringWrapper<Base, Policy>::ownPropertyKeys( 91 JSContext* cx, HandleObject wrapper, MutableHandleIdVector props) const { 92 MOZ_CRASH("FilteringWrappers are now always opaque"); 93 } 94 95 template <typename Base, typename Policy> 96 bool FilteringWrapper<Base, Policy>::getOwnEnumerablePropertyKeys( 97 JSContext* cx, HandleObject wrapper, MutableHandleIdVector props) const { 98 MOZ_CRASH("FilteringWrappers are now always opaque"); 99 } 100 101 template <typename Base, typename Policy> 102 bool FilteringWrapper<Base, Policy>::enumerate( 103 JSContext* cx, HandleObject wrapper, 104 JS::MutableHandleIdVector props) const { 105 MOZ_CRASH("FilteringWrappers are now always opaque"); 106 } 107 108 template <typename Base, typename Policy> 109 bool FilteringWrapper<Base, Policy>::call(JSContext* cx, 110 JS::Handle<JSObject*> wrapper, 111 const JS::CallArgs& args) const { 112 if (!Policy::checkCall(cx, wrapper, args)) { 113 return false; 114 } 115 return Base::call(cx, wrapper, args); 116 } 117 118 template <typename Base, typename Policy> 119 bool FilteringWrapper<Base, Policy>::construct(JSContext* cx, 120 JS::Handle<JSObject*> wrapper, 121 const JS::CallArgs& args) const { 122 if (!Policy::checkCall(cx, wrapper, args)) { 123 return false; 124 } 125 return Base::construct(cx, wrapper, args); 126 } 127 128 template <typename Base, typename Policy> 129 bool FilteringWrapper<Base, Policy>::nativeCall( 130 JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl, 131 const JS::CallArgs& args) const { 132 if (Policy::allowNativeCall(cx, test, impl)) { 133 return Base::Permissive::nativeCall(cx, test, impl, args); 134 } 135 return Base::Restrictive::nativeCall(cx, test, impl, args); 136 } 137 138 template <typename Base, typename Policy> 139 bool FilteringWrapper<Base, Policy>::getPrototype( 140 JSContext* cx, JS::HandleObject wrapper, 141 JS::MutableHandleObject protop) const { 142 // Filtering wrappers do not allow access to the prototype. 143 protop.set(nullptr); 144 return true; 145 } 146 147 template <typename Base, typename Policy> 148 bool FilteringWrapper<Base, Policy>::enter(JSContext* cx, HandleObject wrapper, 149 HandleId id, Wrapper::Action act, 150 bool mayThrow, bool* bp) const { 151 if (!Policy::check(cx, wrapper, id, act)) { 152 *bp = 153 JS_IsExceptionPending(cx) ? false : Policy::deny(cx, act, id, mayThrow); 154 return false; 155 } 156 *bp = true; 157 return true; 158 } 159 160 #define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque> 161 #define NNXOWC FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall> 162 163 template <> 164 const NNXOW NNXOW::singleton(0); 165 template <> 166 const NNXOWC NNXOWC::singleton(0); 167 168 template class NNXOW; 169 template class NNXOWC; 170 template class ChromeObjectWrapperBase; 171 } // namespace xpc