| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 | 29 |
| 30 #include "cctest.h" | 30 #include "cctest.h" |
| 31 | 31 |
| 32 using namespace v8; | 32 using namespace v8; |
| 33 namespace i = v8::internal; | 33 namespace i = v8::internal; |
| 34 | 34 |
| 35 | 35 |
| 36 TEST(PerIsolateState) { | 36 TEST(PerIsolateState) { |
| 37 HandleScope scope(CcTest::isolate()); | 37 HandleScope scope(CcTest::isolate()); |
| 38 LocalContext context1(CcTest::isolate()); | 38 LocalContext context1(CcTest::isolate()); |
| 39 |
| 40 Local<Value> foo = v8_str("foo"); |
| 41 context1->SetSecurityToken(foo); |
| 42 |
| 39 CompileRun( | 43 CompileRun( |
| 40 "var count = 0;" | 44 "var count = 0;" |
| 41 "var calls = 0;" | 45 "var calls = 0;" |
| 42 "var observer = function(records) { count = records.length; calls++ };" | 46 "var observer = function(records) { count = records.length; calls++ };" |
| 43 "var obj = {};" | 47 "var obj = {};" |
| 44 "Object.observe(obj, observer);"); | 48 "Object.observe(obj, observer);"); |
| 45 Handle<Value> observer = CompileRun("observer"); | 49 Handle<Value> observer = CompileRun("observer"); |
| 46 Handle<Value> obj = CompileRun("obj"); | 50 Handle<Value> obj = CompileRun("obj"); |
| 47 Handle<Value> notify_fun1 = CompileRun( | 51 Handle<Value> notify_fun1 = CompileRun( |
| 48 "(function() { obj.foo = 'bar'; })"); | 52 "(function() { obj.foo = 'bar'; })"); |
| 49 Handle<Value> notify_fun2; | 53 Handle<Value> notify_fun2; |
| 50 { | 54 { |
| 51 LocalContext context2(CcTest::isolate()); | 55 LocalContext context2(CcTest::isolate()); |
| 56 context2->SetSecurityToken(foo); |
| 52 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), | 57 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), |
| 53 obj); | 58 obj); |
| 54 notify_fun2 = CompileRun( | 59 notify_fun2 = CompileRun( |
| 55 "(function() { obj.foo = 'baz'; })"); | 60 "(function() { obj.foo = 'baz'; })"); |
| 56 } | 61 } |
| 57 Handle<Value> notify_fun3; | 62 Handle<Value> notify_fun3; |
| 58 { | 63 { |
| 59 LocalContext context3(CcTest::isolate()); | 64 LocalContext context3(CcTest::isolate()); |
| 65 context3->SetSecurityToken(foo); |
| 60 context3->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), | 66 context3->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), |
| 61 obj); | 67 obj); |
| 62 notify_fun3 = CompileRun( | 68 notify_fun3 = CompileRun( |
| 63 "(function() { obj.foo = 'bat'; })"); | 69 "(function() { obj.foo = 'bat'; })"); |
| 64 } | 70 } |
| 65 { | 71 { |
| 66 LocalContext context4(CcTest::isolate()); | 72 LocalContext context4(CcTest::isolate()); |
| 73 context4->SetSecurityToken(foo); |
| 67 context4->Global()->Set( | 74 context4->Global()->Set( |
| 68 String::NewFromUtf8(CcTest::isolate(), "observer"), observer); | 75 String::NewFromUtf8(CcTest::isolate(), "observer"), observer); |
| 69 context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun1"), | 76 context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun1"), |
| 70 notify_fun1); | 77 notify_fun1); |
| 71 context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun2"), | 78 context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun2"), |
| 72 notify_fun2); | 79 notify_fun2); |
| 73 context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun3"), | 80 context4->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "fun3"), |
| 74 notify_fun3); | 81 notify_fun3); |
| 75 CompileRun("fun1(); fun2(); fun3(); Object.deliverChangeRecords(observer)"); | 82 CompileRun("fun1(); fun2(); fun3(); Object.deliverChangeRecords(observer)"); |
| 76 } | 83 } |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 202 " Object.observe(objArr[objArr.length-1], function(){});" | 209 " Object.observe(objArr[objArr.length-1], function(){});" |
| 203 "}" | 210 "}" |
| 204 "Object.observe(obj, observer);"); | 211 "Object.observe(obj, observer);"); |
| 205 } | 212 } |
| 206 // obj is now marked "is_observed", but our map has moved. | 213 // obj is now marked "is_observed", but our map has moved. |
| 207 CompileRun("obj.foo = 'bar'"); | 214 CompileRun("obj.foo = 'bar'"); |
| 208 CHECK(CompileRun("ran")->BooleanValue()); | 215 CHECK(CompileRun("ran")->BooleanValue()); |
| 209 } | 216 } |
| 210 | 217 |
| 211 | 218 |
| 212 TEST(GlobalObjectObservation) { | |
| 213 LocalContext context(CcTest::isolate()); | |
| 214 HandleScope scope(CcTest::isolate()); | |
| 215 Handle<Object> global_proxy = context->Global(); | |
| 216 CompileRun( | |
| 217 "var records = [];" | |
| 218 "var global = this;" | |
| 219 "Object.observe(global, function(r) { [].push.apply(records, r) });" | |
| 220 "global.foo = 'hello';"); | |
| 221 CHECK_EQ(1, CompileRun("records.length")->Int32Value()); | |
| 222 CHECK(global_proxy->StrictEquals(CompileRun("records[0].object"))); | |
| 223 | |
| 224 // Detached, mutating the proxy has no effect. | |
| 225 context->DetachGlobal(); | |
| 226 CompileRun("global.bar = 'goodbye';"); | |
| 227 CHECK_EQ(1, CompileRun("records.length")->Int32Value()); | |
| 228 CompileRun("this.baz = 'goodbye';"); | |
| 229 CHECK_EQ(1, CompileRun("records.length")->Int32Value()); | |
| 230 | |
| 231 // Attached to a different context, should not leak mutations | |
| 232 // to the old context. | |
| 233 context->DetachGlobal(); | |
| 234 { | |
| 235 LocalContext context2(CcTest::isolate()); | |
| 236 CompileRun( | |
| 237 "var records2 = [];" | |
| 238 "var global = this;" | |
| 239 "Object.observe(this, function(r) { [].push.apply(records2, r) });" | |
| 240 "this.v1 = 'context2';"); | |
| 241 context2->DetachGlobal(); | |
| 242 CompileRun( | |
| 243 "global.v2 = 'context2';" | |
| 244 "this.v3 = 'context2';"); | |
| 245 CHECK_EQ(1, CompileRun("records2.length")->Int32Value()); | |
| 246 } | |
| 247 CHECK_EQ(1, CompileRun("records.length")->Int32Value()); | |
| 248 | |
| 249 // Attaching by passing to Context::New | |
| 250 { | |
| 251 // Delegates to Context::New | |
| 252 LocalContext context3( | |
| 253 CcTest::isolate(), NULL, Handle<ObjectTemplate>(), global_proxy); | |
| 254 CompileRun( | |
| 255 "var records3 = [];" | |
| 256 "Object.observe(this, function(r) { [].push.apply(records3, r) });" | |
| 257 "this.qux = 'context3';"); | |
| 258 CHECK_EQ(1, CompileRun("records3.length")->Int32Value()); | |
| 259 CHECK(global_proxy->StrictEquals(CompileRun("records3[0].object"))); | |
| 260 } | |
| 261 CHECK_EQ(1, CompileRun("records.length")->Int32Value()); | |
| 262 } | |
| 263 | |
| 264 | |
| 265 struct RecordExpectation { | 219 struct RecordExpectation { |
| 266 Handle<Value> object; | 220 Handle<Value> object; |
| 267 const char* type; | 221 const char* type; |
| 268 const char* name; | 222 const char* name; |
| 269 Handle<Value> old_value; | 223 Handle<Value> old_value; |
| 270 }; | 224 }; |
| 271 | 225 |
| 272 | 226 |
| 273 // TODO(adamk): Use this helper elsewhere in this file. | 227 // TODO(adamk): Use this helper elsewhere in this file. |
| 274 static void ExpectRecords(v8::Isolate* isolate, | 228 static void ExpectRecords(v8::Isolate* isolate, |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 423 CHECK_EQ(1, NumberOfElements(callbackInfoMap)); | 377 CHECK_EQ(1, NumberOfElements(callbackInfoMap)); |
| 424 CHECK_EQ(1, NumberOfElements(objectInfoMap)); | 378 CHECK_EQ(1, NumberOfElements(objectInfoMap)); |
| 425 CHECK_EQ(1, NumberOfElements(notifierObjectInfoMap)); | 379 CHECK_EQ(1, NumberOfElements(notifierObjectInfoMap)); |
| 426 i_isolate->heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); | 380 i_isolate->heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); |
| 427 CHECK_EQ(0, NumberOfElements(callbackInfoMap)); | 381 CHECK_EQ(0, NumberOfElements(callbackInfoMap)); |
| 428 CHECK_EQ(0, NumberOfElements(objectInfoMap)); | 382 CHECK_EQ(0, NumberOfElements(objectInfoMap)); |
| 429 CHECK_EQ(0, NumberOfElements(notifierObjectInfoMap)); | 383 CHECK_EQ(0, NumberOfElements(notifierObjectInfoMap)); |
| 430 } | 384 } |
| 431 | 385 |
| 432 | 386 |
| 433 static bool NamedAccessAlwaysAllowed(Local<Object>, Local<Value>, AccessType, | 387 static int TestObserveSecurity(Handle<Context> observer_context, |
| 434 Local<Value>) { | 388 Handle<Context> object_context, |
| 435 return true; | 389 Handle<Context> mutation_context) { |
| 436 } | 390 Context::Scope observer_scope(observer_context); |
| 437 | 391 CompileRun("var records = null;" |
| 438 | 392 "var observer = function(r) { records = r };"); |
| 439 static bool IndexedAccessAlwaysAllowed(Local<Object>, uint32_t, AccessType, | 393 Handle<Value> observer = CompileRun("observer"); |
| 440 Local<Value>) { | 394 { |
| 441 return true; | 395 Context::Scope object_scope(object_context); |
| 442 } | 396 object_context->Global()->Set( |
| 443 | 397 String::NewFromUtf8(CcTest::isolate(), "observer"), observer); |
| 444 | 398 CompileRun("var obj = {};" |
| 445 static AccessType g_access_block_type = ACCESS_GET; | 399 "obj.length = 0;" |
| 446 static const uint32_t kBlockedContextIndex = 1337; | 400 "Object.observe(obj, observer," |
| 447 | 401 "['add', 'update', 'delete','reconfigure','splice']" |
| 448 | 402 ");"); |
| 449 static bool NamedAccessAllowUnlessBlocked(Local<Object> host, | 403 Handle<Value> obj = CompileRun("obj"); |
| 450 Local<Value> key, | |
| 451 AccessType type, | |
| 452 Local<Value> data) { | |
| 453 if (type != g_access_block_type) return true; | |
| 454 v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>( | |
| 455 Utils::OpenHandle(*host)->GetIsolate()); | |
| 456 Handle<Object> global = isolate->GetCurrentContext()->Global(); | |
| 457 if (!global->Has(kBlockedContextIndex)) return true; | |
| 458 return !key->IsString() || !key->Equals(data); | |
| 459 } | |
| 460 | |
| 461 | |
| 462 static bool IndexedAccessAllowUnlessBlocked(Local<Object> host, | |
| 463 uint32_t index, | |
| 464 AccessType type, | |
| 465 Local<Value> data) { | |
| 466 if (type != g_access_block_type) return true; | |
| 467 v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>( | |
| 468 Utils::OpenHandle(*host)->GetIsolate()); | |
| 469 Handle<Object> global = isolate->GetCurrentContext()->Global(); | |
| 470 if (!global->Has(kBlockedContextIndex)) return true; | |
| 471 return index != data->Uint32Value(); | |
| 472 } | |
| 473 | |
| 474 | |
| 475 static bool BlockAccessKeys(Local<Object> host, Local<Value> key, | |
| 476 AccessType type, Local<Value>) { | |
| 477 v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>( | |
| 478 Utils::OpenHandle(*host)->GetIsolate()); | |
| 479 Handle<Object> global = isolate->GetCurrentContext()->Global(); | |
| 480 return type != ACCESS_KEYS || !global->Has(kBlockedContextIndex); | |
| 481 } | |
| 482 | |
| 483 | |
| 484 static Handle<Object> CreateAccessCheckedObject( | |
| 485 v8::Isolate* isolate, | |
| 486 NamedSecurityCallback namedCallback, | |
| 487 IndexedSecurityCallback indexedCallback, | |
| 488 Handle<Value> data = Handle<Value>()) { | |
| 489 Handle<ObjectTemplate> tmpl = ObjectTemplate::New(isolate); | |
| 490 tmpl->SetAccessCheckCallbacks(namedCallback, indexedCallback, data); | |
| 491 Handle<Object> instance = tmpl->NewInstance(); | |
| 492 Handle<Object> global = instance->CreationContext()->Global(); | |
| 493 global->Set(String::NewFromUtf8(isolate, "obj"), instance); | |
| 494 global->Set(kBlockedContextIndex, v8::True(isolate)); | |
| 495 return instance; | |
| 496 } | |
| 497 | |
| 498 | |
| 499 TEST(NamedAccessCheck) { | |
| 500 const AccessType types[] = { ACCESS_GET, ACCESS_HAS }; | |
| 501 for (size_t i = 0; i < ARRAY_SIZE(types); ++i) { | |
| 502 HandleScope scope(CcTest::isolate()); | |
| 503 LocalContext context(CcTest::isolate()); | |
| 504 g_access_block_type = types[i]; | |
| 505 Handle<Object> instance = CreateAccessCheckedObject( | |
| 506 CcTest::isolate(), | |
| 507 NamedAccessAllowUnlessBlocked, | |
| 508 IndexedAccessAlwaysAllowed, | |
| 509 String::NewFromUtf8(CcTest::isolate(), "foo")); | |
| 510 CompileRun("var records = null;" | |
| 511 "var objNoCheck = {};" | |
| 512 "var observer = function(r) { records = r };" | |
| 513 "Object.observe(obj, observer);" | |
| 514 "Object.observe(objNoCheck, observer);"); | |
| 515 Handle<Value> obj_no_check = CompileRun("objNoCheck"); | |
| 516 { | 404 { |
| 517 LocalContext context2(CcTest::isolate()); | 405 Context::Scope mutation_scope(mutation_context); |
| 518 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), | 406 mutation_context->Global()->Set( |
| 519 instance); | 407 String::NewFromUtf8(CcTest::isolate(), "obj"), obj); |
| 520 context2->Global()->Set( | 408 CompileRun("obj.foo = 'bar';" |
| 521 String::NewFromUtf8(CcTest::isolate(), "objNoCheck"), | 409 "obj.foo = 'baz';" |
| 522 obj_no_check); | 410 "delete obj.foo;" |
| 523 CompileRun("var records2 = null;" | 411 "Object.defineProperty(obj, 'bar', {value: 'bot'});" |
| 524 "var observer2 = function(r) { records2 = r };" | 412 "Array.prototype.push.call(obj, 1, 2, 3);" |
| 525 "Object.observe(obj, observer2);" | 413 "Array.prototype.splice.call(obj, 1, 2, 2, 4);" |
| 526 "Object.observe(objNoCheck, observer2);" | 414 "Array.prototype.pop.call(obj);" |
| 527 "obj.foo = 'bar';" | 415 "Array.prototype.shift.call(obj);"); |
| 528 "Object.defineProperty(obj, 'foo', {value: 5});" | |
| 529 "Object.defineProperty(obj, 'foo', {get: function(){}});" | |
| 530 "obj.bar = 'baz';" | |
| 531 "objNoCheck.baz = 'quux'"); | |
| 532 const RecordExpectation expected_records2[] = { | |
| 533 { instance, "add", "foo", Handle<Value>() }, | |
| 534 { instance, "update", "foo", | |
| 535 String::NewFromUtf8(CcTest::isolate(), "bar") }, | |
| 536 { instance, "reconfigure", "foo", | |
| 537 Number::New(CcTest::isolate(), 5) }, | |
| 538 { instance, "add", "bar", Handle<Value>() }, | |
| 539 { obj_no_check, "add", "baz", Handle<Value>() }, | |
| 540 }; | |
| 541 EXPECT_RECORDS(CompileRun("records2"), expected_records2); | |
| 542 } | 416 } |
| 543 const RecordExpectation expected_records[] = { | 417 } |
| 544 { instance, "add", "bar", Handle<Value>() }, | 418 return CompileRun("records ? records.length : 0")->Int32Value(); |
| 545 { obj_no_check, "add", "baz", Handle<Value>() } | 419 } |
| 546 }; | 420 |
| 547 EXPECT_RECORDS(CompileRun("records"), expected_records); | 421 |
| 548 } | 422 TEST(ObserverSecurityAAA) { |
| 549 } | 423 v8::Isolate* isolate = CcTest::isolate(); |
| 550 | 424 v8::HandleScope scope(isolate); |
| 551 | 425 v8::Local<Context> contextA = Context::New(isolate); |
| 552 TEST(IndexedAccessCheck) { | 426 CHECK_EQ(8, TestObserveSecurity(contextA, contextA, contextA)); |
| 553 const AccessType types[] = { ACCESS_GET, ACCESS_HAS }; | 427 } |
| 554 for (size_t i = 0; i < ARRAY_SIZE(types); ++i) { | 428 |
| 555 HandleScope scope(CcTest::isolate()); | 429 |
| 556 LocalContext context(CcTest::isolate()); | 430 TEST(ObserverSecurityA1A2A3) { |
| 557 g_access_block_type = types[i]; | 431 v8::Isolate* isolate = CcTest::isolate(); |
| 558 Handle<Object> instance = CreateAccessCheckedObject( | 432 v8::HandleScope scope(isolate); |
| 559 CcTest::isolate(), NamedAccessAlwaysAllowed, | 433 |
| 560 IndexedAccessAllowUnlessBlocked, Number::New(CcTest::isolate(), 7)); | 434 v8::Local<Context> contextA1 = Context::New(isolate); |
| 561 CompileRun("var records = null;" | 435 v8::Local<Context> contextA2 = Context::New(isolate); |
| 562 "var objNoCheck = {};" | 436 v8::Local<Context> contextA3 = Context::New(isolate); |
| 563 "var observer = function(r) { records = r };" | 437 |
| 564 "Object.observe(obj, observer);" | 438 Local<Value> foo = v8_str("foo"); |
| 565 "Object.observe(objNoCheck, observer);"); | 439 contextA1->SetSecurityToken(foo); |
| 566 Handle<Value> obj_no_check = CompileRun("objNoCheck"); | 440 contextA2->SetSecurityToken(foo); |
| 567 { | 441 contextA3->SetSecurityToken(foo); |
| 568 LocalContext context2(CcTest::isolate()); | 442 |
| 569 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), | 443 CHECK_EQ(8, TestObserveSecurity(contextA1, contextA2, contextA3)); |
| 570 instance); | 444 } |
| 571 context2->Global()->Set( | 445 |
| 572 String::NewFromUtf8(CcTest::isolate(), "objNoCheck"), | 446 |
| 573 obj_no_check); | 447 TEST(ObserverSecurityAAB) { |
| 574 CompileRun("var records2 = null;" | 448 v8::Isolate* isolate = CcTest::isolate(); |
| 575 "var observer2 = function(r) { records2 = r };" | 449 v8::HandleScope scope(isolate); |
| 576 "Object.observe(obj, observer2);" | 450 v8::Local<Context> contextA = Context::New(isolate); |
| 577 "Object.observe(objNoCheck, observer2);" | 451 v8::Local<Context> contextB = Context::New(isolate); |
| 578 "obj[7] = 'foo';" | 452 CHECK_EQ(0, TestObserveSecurity(contextA, contextA, contextB)); |
| 579 "Object.defineProperty(obj, '7', {value: 5});" | 453 } |
| 580 "Object.defineProperty(obj, '7', {get: function(){}});" | 454 |
| 581 "obj[8] = 'bar';" | 455 |
| 582 "objNoCheck[42] = 'quux'"); | 456 TEST(ObserverSecurityA1A2B) { |
| 583 const RecordExpectation expected_records2[] = { | 457 v8::Isolate* isolate = CcTest::isolate(); |
| 584 { instance, "add", "7", Handle<Value>() }, | 458 v8::HandleScope scope(isolate); |
| 585 { instance, "update", "7", | 459 |
| 586 String::NewFromUtf8(CcTest::isolate(), "foo") }, | 460 v8::Local<Context> contextA1 = Context::New(isolate); |
| 587 { instance, "reconfigure", "7", Number::New(CcTest::isolate(), 5) }, | 461 v8::Local<Context> contextA2 = Context::New(isolate); |
| 588 { instance, "add", "8", Handle<Value>() }, | 462 v8::Local<Context> contextB = Context::New(isolate); |
| 589 { obj_no_check, "add", "42", Handle<Value>() } | 463 |
| 590 }; | 464 Local<Value> foo = v8_str("foo"); |
| 591 EXPECT_RECORDS(CompileRun("records2"), expected_records2); | 465 contextA1->SetSecurityToken(foo); |
| 592 } | 466 contextA2->SetSecurityToken(foo); |
| 593 const RecordExpectation expected_records[] = { | 467 |
| 594 { instance, "add", "8", Handle<Value>() }, | 468 CHECK_EQ(0, TestObserveSecurity(contextA1, contextA2, contextB)); |
| 595 { obj_no_check, "add", "42", Handle<Value>() } | 469 } |
| 596 }; | 470 |
| 597 EXPECT_RECORDS(CompileRun("records"), expected_records); | 471 |
| 598 } | 472 TEST(ObserverSecurityABA) { |
| 599 } | 473 v8::Isolate* isolate = CcTest::isolate(); |
| 600 | 474 v8::HandleScope scope(isolate); |
| 601 | 475 v8::Local<Context> contextA = Context::New(isolate); |
| 602 TEST(SpliceAccessCheck) { | 476 v8::Local<Context> contextB = Context::New(isolate); |
| 603 HandleScope scope(CcTest::isolate()); | 477 CHECK_EQ(0, TestObserveSecurity(contextA, contextB, contextA)); |
| 604 LocalContext context(CcTest::isolate()); | 478 } |
| 605 g_access_block_type = ACCESS_GET; | 479 |
| 606 Handle<Object> instance = CreateAccessCheckedObject( | 480 |
| 607 CcTest::isolate(), NamedAccessAlwaysAllowed, | 481 TEST(ObserverSecurityA1BA2) { |
| 608 IndexedAccessAllowUnlessBlocked, Number::New(CcTest::isolate(), 1)); | 482 v8::Isolate* isolate = CcTest::isolate(); |
| 609 CompileRun("var records = null;" | 483 v8::HandleScope scope(isolate); |
| 610 "obj[1] = 'foo';" | 484 v8::Local<Context> contextA1 = Context::New(isolate); |
| 611 "obj.length = 2;" | 485 v8::Local<Context> contextA2 = Context::New(isolate); |
| 612 "var objNoCheck = {1: 'bar', length: 2};" | 486 v8::Local<Context> contextB = Context::New(isolate); |
| 613 "observer = function(r) { records = r };" | 487 |
| 614 "Array.observe(obj, observer);" | 488 Local<Value> foo = v8_str("foo"); |
| 615 "Array.observe(objNoCheck, observer);"); | 489 contextA1->SetSecurityToken(foo); |
| 616 Handle<Value> obj_no_check = CompileRun("objNoCheck"); | 490 contextA2->SetSecurityToken(foo); |
| 617 { | 491 |
| 618 LocalContext context2(CcTest::isolate()); | 492 CHECK_EQ(0, TestObserveSecurity(contextA1, contextB, contextA2)); |
| 619 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), | 493 } |
| 620 instance); | 494 |
| 621 context2->Global()->Set( | 495 |
| 622 String::NewFromUtf8(CcTest::isolate(), "objNoCheck"), obj_no_check); | 496 TEST(ObserverSecurityBAA) { |
| 623 CompileRun("var records2 = null;" | 497 v8::Isolate* isolate = CcTest::isolate(); |
| 624 "var observer2 = function(r) { records2 = r };" | 498 v8::HandleScope scope(isolate); |
| 625 "Array.observe(obj, observer2);" | 499 v8::Local<Context> contextA = Context::New(isolate); |
| 626 "Array.observe(objNoCheck, observer2);" | 500 v8::Local<Context> contextB = Context::New(isolate); |
| 627 // No one should hear about this: no splice records are emitted | 501 CHECK_EQ(0, TestObserveSecurity(contextB, contextA, contextA)); |
| 628 // for access-checked objects | 502 } |
| 629 "[].push.call(obj, 5);" | 503 |
| 630 "[].splice.call(obj, 1, 1);" | 504 |
| 631 "[].pop.call(obj);" | 505 TEST(ObserverSecurityBA1A2) { |
| 632 "[].pop.call(objNoCheck);"); | 506 v8::Isolate* isolate = CcTest::isolate(); |
| 633 // TODO(adamk): Extend EXPECT_RECORDS to be able to assert more things | 507 v8::HandleScope scope(isolate); |
| 634 // about splice records. For this test it's not so important since | 508 v8::Local<Context> contextA1 = Context::New(isolate); |
| 635 // we just want to guarantee the machinery is in operation at all. | 509 v8::Local<Context> contextA2 = Context::New(isolate); |
| 636 const RecordExpectation expected_records2[] = { | 510 v8::Local<Context> contextB = Context::New(isolate); |
| 637 { obj_no_check, "splice", "", Handle<Value>() } | 511 |
| 638 }; | 512 Local<Value> foo = v8_str("foo"); |
| 639 EXPECT_RECORDS(CompileRun("records2"), expected_records2); | 513 contextA1->SetSecurityToken(foo); |
| 640 } | 514 contextA2->SetSecurityToken(foo); |
| 641 const RecordExpectation expected_records[] = { | 515 |
| 642 { obj_no_check, "splice", "", Handle<Value>() } | 516 CHECK_EQ(0, TestObserveSecurity(contextB, contextA1, contextA2)); |
| 643 }; | 517 } |
| 644 EXPECT_RECORDS(CompileRun("records"), expected_records); | 518 |
| 645 } | 519 |
| 646 | 520 TEST(ObserverSecurityNotify) { |
| 647 | 521 v8::Isolate* isolate = CcTest::isolate(); |
| 648 TEST(DisallowAllForAccessKeys) { | 522 v8::HandleScope scope(isolate); |
| 649 HandleScope scope(CcTest::isolate()); | 523 v8::Local<Context> contextA = Context::New(isolate); |
| 650 LocalContext context(CcTest::isolate()); | 524 v8::Local<Context> contextB = Context::New(isolate); |
| 651 Handle<Object> instance = CreateAccessCheckedObject( | 525 |
| 652 CcTest::isolate(), BlockAccessKeys, IndexedAccessAlwaysAllowed); | 526 Context::Scope scopeA(contextA); |
| 653 CompileRun("var records = null;" | 527 CompileRun("var obj = {};" |
| 654 "var objNoCheck = {};" | 528 "var recordsA = null;" |
| 655 "var observer = function(r) { records = r };" | 529 "var observerA = function(r) { recordsA = r };" |
| 656 "Object.observe(obj, observer);" | 530 "Object.observe(obj, observerA);"); |
| 657 "Object.observe(objNoCheck, observer);"); | 531 Handle<Value> obj = CompileRun("obj"); |
| 658 Handle<Value> obj_no_check = CompileRun("objNoCheck"); | 532 |
| 659 { | 533 { |
| 660 LocalContext context2(CcTest::isolate()); | 534 Context::Scope scopeB(contextB); |
| 661 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), | 535 contextB->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), obj); |
| 662 instance); | 536 CompileRun("var recordsB = null;" |
| 663 context2->Global()->Set( | 537 "var observerB = function(r) { recordsB = r };" |
| 664 String::NewFromUtf8(CcTest::isolate(), "objNoCheck"), obj_no_check); | 538 "Object.observe(obj, observerB);"); |
| 665 CompileRun("var records2 = null;" | 539 } |
| 666 "var observer2 = function(r) { records2 = r };" | 540 |
| 667 "Object.observe(obj, observer2);" | 541 CompileRun("var notifier = Object.getNotifier(obj);" |
| 668 "Object.observe(objNoCheck, observer2);" | 542 "notifier.notify({ type: 'update' });"); |
| 669 "obj.foo = 'bar';" | 543 CHECK_EQ(1, CompileRun("recordsA ? recordsA.length : 0")->Int32Value()); |
| 670 "obj[5] = 'baz';" | 544 |
| 671 "objNoCheck.baz = 'quux'"); | 545 { |
| 672 const RecordExpectation expected_records2[] = { | 546 Context::Scope scopeB(contextB); |
| 673 { instance, "add", "foo", Handle<Value>() }, | 547 CHECK_EQ(0, CompileRun("recordsB ? recordsB.length : 0")->Int32Value()); |
| 674 { instance, "add", "5", Handle<Value>() }, | 548 } |
| 675 { obj_no_check, "add", "baz", Handle<Value>() }, | |
| 676 }; | |
| 677 EXPECT_RECORDS(CompileRun("records2"), expected_records2); | |
| 678 } | |
| 679 const RecordExpectation expected_records[] = { | |
| 680 { obj_no_check, "add", "baz", Handle<Value>() } | |
| 681 }; | |
| 682 EXPECT_RECORDS(CompileRun("records"), expected_records); | |
| 683 } | |
| 684 | |
| 685 | |
| 686 TEST(AccessCheckDisallowApiModifications) { | |
| 687 HandleScope scope(CcTest::isolate()); | |
| 688 LocalContext context(CcTest::isolate()); | |
| 689 Handle<Object> instance = CreateAccessCheckedObject( | |
| 690 CcTest::isolate(), BlockAccessKeys, IndexedAccessAlwaysAllowed); | |
| 691 CompileRun("var records = null;" | |
| 692 "var observer = function(r) { records = r };" | |
| 693 "Object.observe(obj, observer);"); | |
| 694 { | |
| 695 LocalContext context2(CcTest::isolate()); | |
| 696 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), | |
| 697 instance); | |
| 698 CompileRun("var records2 = null;" | |
| 699 "var observer2 = function(r) { records2 = r };" | |
| 700 "Object.observe(obj, observer2);"); | |
| 701 instance->Set(5, String::NewFromUtf8(CcTest::isolate(), "bar")); | |
| 702 instance->Set(String::NewFromUtf8(CcTest::isolate(), "foo"), | |
| 703 String::NewFromUtf8(CcTest::isolate(), "bar")); | |
| 704 CompileRun(""); // trigger delivery | |
| 705 const RecordExpectation expected_records2[] = { | |
| 706 { instance, "add", "5", Handle<Value>() }, | |
| 707 { instance, "add", "foo", Handle<Value>() } | |
| 708 }; | |
| 709 EXPECT_RECORDS(CompileRun("records2"), expected_records2); | |
| 710 } | |
| 711 CHECK(CompileRun("records")->IsNull()); | |
| 712 } | 549 } |
| 713 | 550 |
| 714 | 551 |
| 715 TEST(HiddenPropertiesLeakage) { | 552 TEST(HiddenPropertiesLeakage) { |
| 716 HandleScope scope(CcTest::isolate()); | 553 HandleScope scope(CcTest::isolate()); |
| 717 LocalContext context(CcTest::isolate()); | 554 LocalContext context(CcTest::isolate()); |
| 718 CompileRun("var obj = {};" | 555 CompileRun("var obj = {};" |
| 719 "var records = null;" | 556 "var records = null;" |
| 720 "var observer = function(r) { records = r };" | 557 "var observer = function(r) { records = r };" |
| 721 "Object.observe(obj, observer);"); | 558 "Object.observe(obj, observer);"); |
| 722 Handle<Value> obj = | 559 Handle<Value> obj = |
| 723 context->Global()->Get(String::NewFromUtf8(CcTest::isolate(), "obj")); | 560 context->Global()->Get(String::NewFromUtf8(CcTest::isolate(), "obj")); |
| 724 Handle<Object>::Cast(obj) | 561 Handle<Object>::Cast(obj) |
| 725 ->SetHiddenValue(String::NewFromUtf8(CcTest::isolate(), "foo"), | 562 ->SetHiddenValue(String::NewFromUtf8(CcTest::isolate(), "foo"), |
| 726 Null(CcTest::isolate())); | 563 Null(CcTest::isolate())); |
| 727 CompileRun(""); // trigger delivery | 564 CompileRun(""); // trigger delivery |
| 728 CHECK(CompileRun("records")->IsNull()); | 565 CHECK(CompileRun("records")->IsNull()); |
| 729 } | 566 } |
| 567 |
| 568 |
| 569 TEST(GetNotifierFromOtherContext) { |
| 570 HandleScope scope(CcTest::isolate()); |
| 571 LocalContext context(CcTest::isolate()); |
| 572 CompileRun("var obj = {};"); |
| 573 Handle<Value> instance = CompileRun("obj"); |
| 574 { |
| 575 LocalContext context2(CcTest::isolate()); |
| 576 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), |
| 577 instance); |
| 578 CHECK(CompileRun("Object.getNotifier(obj)")->IsNull()); |
| 579 } |
| 580 } |
| 581 |
| 582 |
| 583 TEST(GetNotifierFromOtherOrigin) { |
| 584 HandleScope scope(CcTest::isolate()); |
| 585 Handle<Value> foo = String::NewFromUtf8(CcTest::isolate(), "foo"); |
| 586 Handle<Value> bar = String::NewFromUtf8(CcTest::isolate(), "bar"); |
| 587 LocalContext context(CcTest::isolate()); |
| 588 context->SetSecurityToken(foo); |
| 589 CompileRun("var obj = {};"); |
| 590 Handle<Value> instance = CompileRun("obj"); |
| 591 { |
| 592 LocalContext context2(CcTest::isolate()); |
| 593 context2->SetSecurityToken(bar); |
| 594 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), |
| 595 instance); |
| 596 CHECK(CompileRun("Object.getNotifier(obj)")->IsNull()); |
| 597 } |
| 598 } |
| 599 |
| 600 |
| 601 TEST(GetNotifierFromSameOrigin) { |
| 602 HandleScope scope(CcTest::isolate()); |
| 603 Handle<Value> foo = String::NewFromUtf8(CcTest::isolate(), "foo"); |
| 604 LocalContext context(CcTest::isolate()); |
| 605 context->SetSecurityToken(foo); |
| 606 CompileRun("var obj = {};"); |
| 607 Handle<Value> instance = CompileRun("obj"); |
| 608 { |
| 609 LocalContext context2(CcTest::isolate()); |
| 610 context2->SetSecurityToken(foo); |
| 611 context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"), |
| 612 instance); |
| 613 CHECK(CompileRun("Object.getNotifier(obj)")->IsObject()); |
| 614 } |
| 615 } |
| OLD | NEW |