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 |