summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/wrappers/FilteringWrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/wrappers/FilteringWrapper.cpp')
-rw-r--r--js/xpconnect/wrappers/FilteringWrapper.cpp172
1 files changed, 172 insertions, 0 deletions
diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp
new file mode 100644
index 0000000000..f4812e04ba
--- /dev/null
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FilteringWrapper.h"
+#include "AccessCheck.h"
+#include "ChromeObjectWrapper.h"
+#include "XrayWrapper.h"
+#include "nsJSUtils.h"
+#include "mozilla/ErrorResult.h"
+#include "xpcpublic.h"
+#include "xpcprivate.h"
+
+#include "jsapi.h"
+#include "js/Symbol.h"
+
+using namespace JS;
+using namespace js;
+
+namespace xpc {
+
+static JS::SymbolCode sCrossOriginWhitelistedSymbolCodes[] = {
+ JS::SymbolCode::toStringTag, JS::SymbolCode::hasInstance,
+ JS::SymbolCode::isConcatSpreadable};
+
+static bool IsCrossOriginWhitelistedSymbol(JSContext* cx, JS::HandleId id) {
+ if (!id.isSymbol()) {
+ return false;
+ }
+
+ JS::Symbol* symbol = id.toSymbol();
+ for (auto code : sCrossOriginWhitelistedSymbolCodes) {
+ if (symbol == JS::GetWellKnownSymbol(cx, code)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool IsCrossOriginWhitelistedProp(JSContext* cx, JS::HandleId id) {
+ return id == GetJSIDByIndex(cx, XPCJSContext::IDX_THEN) ||
+ IsCrossOriginWhitelistedSymbol(cx, id);
+}
+
+bool AppendCrossOriginWhitelistedPropNames(JSContext* cx,
+ JS::MutableHandleIdVector props) {
+ // Add "then" if it's not already in the list.
+ RootedIdVector thenProp(cx);
+ if (!thenProp.append(GetJSIDByIndex(cx, XPCJSContext::IDX_THEN))) {
+ return false;
+ }
+
+ if (!AppendUnique(cx, props, thenProp)) {
+ return false;
+ }
+
+ // Now add the three symbol-named props cross-origin objects have.
+#ifdef DEBUG
+ for (size_t n = 0; n < props.length(); ++n) {
+ MOZ_ASSERT(!props[n].isSymbol(), "Unexpected existing symbol-name prop");
+ }
+#endif
+ if (!props.reserve(
+ props.length() +
+ mozilla::ArrayLength(sCrossOriginWhitelistedSymbolCodes))) {
+ return false;
+ }
+
+ for (auto code : sCrossOriginWhitelistedSymbolCodes) {
+ props.infallibleAppend(JS::GetWellKnownSymbolKey(cx, code));
+ }
+
+ return true;
+}
+
+// Note: Previously, FilteringWrapper supported complex access policies where
+// certain properties on an object were accessible and others weren't. Today,
+// the only supported policies are Opaque and OpaqueWithCall, none of which need
+// that. So we just stub out the unreachable paths.
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor(
+ JSContext* cx, HandleObject wrapper, HandleId id,
+ MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const {
+ MOZ_CRASH("FilteringWrappers are now always opaque");
+}
+
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::ownPropertyKeys(
+ JSContext* cx, HandleObject wrapper, MutableHandleIdVector props) const {
+ MOZ_CRASH("FilteringWrappers are now always opaque");
+}
+
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::getOwnEnumerablePropertyKeys(
+ JSContext* cx, HandleObject wrapper, MutableHandleIdVector props) const {
+ MOZ_CRASH("FilteringWrappers are now always opaque");
+}
+
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::enumerate(
+ JSContext* cx, HandleObject wrapper,
+ JS::MutableHandleIdVector props) const {
+ MOZ_CRASH("FilteringWrappers are now always opaque");
+}
+
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::call(JSContext* cx,
+ JS::Handle<JSObject*> wrapper,
+ const JS::CallArgs& args) const {
+ if (!Policy::checkCall(cx, wrapper, args)) {
+ return false;
+ }
+ return Base::call(cx, wrapper, args);
+}
+
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::construct(JSContext* cx,
+ JS::Handle<JSObject*> wrapper,
+ const JS::CallArgs& args) const {
+ if (!Policy::checkCall(cx, wrapper, args)) {
+ return false;
+ }
+ return Base::construct(cx, wrapper, args);
+}
+
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::nativeCall(
+ JSContext* cx, JS::IsAcceptableThis test, JS::NativeImpl impl,
+ const JS::CallArgs& args) const {
+ if (Policy::allowNativeCall(cx, test, impl)) {
+ return Base::Permissive::nativeCall(cx, test, impl, args);
+ }
+ return Base::Restrictive::nativeCall(cx, test, impl, args);
+}
+
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::getPrototype(
+ JSContext* cx, JS::HandleObject wrapper,
+ JS::MutableHandleObject protop) const {
+ // Filtering wrappers do not allow access to the prototype.
+ protop.set(nullptr);
+ return true;
+}
+
+template <typename Base, typename Policy>
+bool FilteringWrapper<Base, Policy>::enter(JSContext* cx, HandleObject wrapper,
+ HandleId id, Wrapper::Action act,
+ bool mayThrow, bool* bp) const {
+ if (!Policy::check(cx, wrapper, id, act)) {
+ *bp =
+ JS_IsExceptionPending(cx) ? false : Policy::deny(cx, act, id, mayThrow);
+ return false;
+ }
+ *bp = true;
+ return true;
+}
+
+#define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>
+#define NNXOWC FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>
+
+template <>
+const NNXOW NNXOW::singleton(0);
+template <>
+const NNXOWC NNXOWC::singleton(0);
+
+template class NNXOW;
+template class NNXOWC;
+template class ChromeObjectWrapperBase;
+} // namespace xpc