// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "include/v8-context.h" #include "include/v8-function.h" #include "include/v8-isolate.h" #include "include/v8-local-handle.h" #include "include/v8-primitive.h" #include "include/v8-script.h" #include "include/v8-template.h" #include "src/debug/debug.h" #include "test/unittests/test-utils.h" #include "testing/gmock-support.h" #include "testing/gtest/include/gtest/gtest.h" namespace v8 { using testing::IsInt32; using testing::IsString; int32_t g_cross_context_int = 0; bool g_expect_interceptor_call = false; class AccessCheckTest : public TestWithIsolate { public: void CheckCanRunScriptInContext(Local context) { HandleScope handle_scope(isolate()); Context::Scope context_scope(context); g_expect_interceptor_call = false; g_cross_context_int = 0; // Running script in this context should work. RunJS("this.foo = 42; this[23] = true;"); EXPECT_THAT(RunJS("this.all_can_read"), IsInt32(42)); RunJS("this.cross_context_int = 23"); CHECK_EQ(g_cross_context_int, 23); EXPECT_THAT(RunJS("this.cross_context_int"), IsInt32(23)); } void CheckCrossContextAccess(Local accessing_context, Local accessed_object) { HandleScope handle_scope(isolate()); accessing_context->Global() ->Set(accessing_context, NewString("other"), accessed_object) .FromJust(); Context::Scope context_scope(accessing_context); g_expect_interceptor_call = true; g_cross_context_int = 23; { TryCatch try_catch(isolate()); CHECK(TryRunJS("this.other.foo").IsEmpty()); } { TryCatch try_catch(isolate()); CHECK(TryRunJS("this.other[23]").IsEmpty()); } // AllCanRead properties are also inaccessible. { TryCatch try_catch(isolate()); CHECK(TryRunJS("this.other.all_can_read").IsEmpty()); } // Intercepted properties are accessible, however. EXPECT_THAT(RunJS("this.other.cross_context_int"), IsInt32(23)); RunJS("this.other.cross_context_int = 42"); EXPECT_THAT(RunJS("this.other[7]"), IsInt32(42)); EXPECT_THAT(RunJS("JSON.stringify(Object.getOwnPropertyNames(this.other))"), IsString("[\"7\",\"cross_context_int\"]")); } void CheckCrossContextAccessWithException(Local accessing_context, Local accessed_object) { HandleScope handle_scope(isolate()); accessing_context->Global() ->Set(accessing_context, NewString("other"), accessed_object) .FromJust(); Context::Scope context_scope(accessing_context); { TryCatch try_catch(isolate()); TryRunJS("this.other.should_throw"); CHECK(try_catch.HasCaught()); CHECK(try_catch.Exception()->IsString()); CHECK(NewString("exception") ->Equals(accessing_context, try_catch.Exception()) .FromJust()); } { TryCatch try_catch(isolate()); TryRunJS("this.other.should_throw = 8"); CHECK(try_catch.HasCaught()); CHECK(try_catch.Exception()->IsString()); CHECK(NewString("exception") ->Equals(accessing_context, try_catch.Exception()) .FromJust()); } { TryCatch try_catch(isolate()); TryRunJS("this.other[42]"); CHECK(try_catch.HasCaught()); CHECK(try_catch.Exception()->IsString()); CHECK(NewString("exception") ->Equals(accessing_context, try_catch.Exception()) .FromJust()); } { TryCatch try_catch(isolate()); TryRunJS("this.other[42] = 8"); CHECK(try_catch.HasCaught()); CHECK(try_catch.Exception()->IsString()); CHECK(NewString("exception") ->Equals(accessing_context, try_catch.Exception()) .FromJust()); } } }; namespace { bool AccessCheck(Local accessing_context, Local accessed_object, Local data) { return false; } MaybeLocal CompileRun(Isolate* isolate, const char* source) { Local source_string = String::NewFromUtf8(isolate, source).ToLocalChecked(); Local context = isolate->GetCurrentContext(); Local