1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
32
33 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
34 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
35 #include "third_party/blink/renderer/bindings/core/v8/v8_location.h"
36 #include "third_party/blink/renderer/bindings/core/v8/v8_window.h"
37 #include "third_party/blink/renderer/core/dom/document.h"
38 #include "third_party/blink/renderer/core/frame/dom_window.h"
39 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
40 #include "third_party/blink/renderer/core/frame/local_frame.h"
41 #include "third_party/blink/renderer/core/frame/location.h"
42 #include "third_party/blink/renderer/core/frame/settings.h"
43 #include "third_party/blink/renderer/core/frame/web_feature.h"
44 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
45 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
46 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
47 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
48
49 namespace blink {
50
51 namespace {
52
53 // Documents that have the same WindowAgentFactory should be able to
54 // share data with each other if they have the same Agent and are
55 // SameOriginDomain.
IsSameWindowAgentFactory(Document * doc1,Document * doc2)56 bool IsSameWindowAgentFactory(Document* doc1, Document* doc2) {
57 return doc1->GetWindowAgentFactory() == doc2->GetWindowAgentFactory();
58 }
59
60 } // namespace
61
Init()62 void BindingSecurity::Init() {
63 BindingSecurityForPlatform::SetShouldAllowAccessToV8ContextWithExceptionState(
64 ShouldAllowAccessToV8Context);
65 BindingSecurityForPlatform::
66 SetShouldAllowAccessToV8ContextWithErrorReportOption(
67 ShouldAllowAccessToV8Context);
68 BindingSecurityForPlatform::SetShouldAllowWrapperCreationOrThrowException(
69 ShouldAllowWrapperCreationOrThrowException);
70 BindingSecurityForPlatform::SetRethrowWrapperCreationException(
71 RethrowWrapperCreationException);
72 }
73
74 namespace {
75
ReportOrThrowSecurityError(const LocalDOMWindow * accessing_window,const DOMWindow * target_window,DOMWindow::CrossDocumentAccessPolicy cross_document_access,ExceptionState & exception_state)76 void ReportOrThrowSecurityError(
77 const LocalDOMWindow* accessing_window,
78 const DOMWindow* target_window,
79 DOMWindow::CrossDocumentAccessPolicy cross_document_access,
80 ExceptionState& exception_state) {
81 if (target_window) {
82 exception_state.ThrowSecurityError(
83 target_window->SanitizedCrossDomainAccessErrorMessage(
84 accessing_window, cross_document_access),
85 target_window->CrossDomainAccessErrorMessage(accessing_window,
86 cross_document_access));
87 } else {
88 exception_state.ThrowSecurityError("Cross origin access was denied.");
89 }
90 }
91
ReportOrThrowSecurityError(const LocalDOMWindow * accessing_window,const DOMWindow * target_window,DOMWindow::CrossDocumentAccessPolicy cross_document_access,BindingSecurity::ErrorReportOption reporting_option)92 void ReportOrThrowSecurityError(
93 const LocalDOMWindow* accessing_window,
94 const DOMWindow* target_window,
95 DOMWindow::CrossDocumentAccessPolicy cross_document_access,
96 BindingSecurity::ErrorReportOption reporting_option) {
97 if (reporting_option == BindingSecurity::ErrorReportOption::kDoNotReport)
98 return;
99
100 if (accessing_window && target_window) {
101 accessing_window->PrintErrorMessage(
102 target_window->CrossDomainAccessErrorMessage(accessing_window,
103 cross_document_access));
104 } else if (accessing_window) {
105 accessing_window->PrintErrorMessage("Cross origin access was denied.");
106 } else {
107 // Nowhere to report the error.
108 }
109 }
110
CanAccessWindowInternal(const LocalDOMWindow * accessing_window,const DOMWindow * target_window,DOMWindow::CrossDocumentAccessPolicy * cross_document_access)111 bool CanAccessWindowInternal(
112 const LocalDOMWindow* accessing_window,
113 const DOMWindow* target_window,
114 DOMWindow::CrossDocumentAccessPolicy* cross_document_access) {
115 SECURITY_CHECK(!(target_window && target_window->GetFrame()) ||
116 target_window == target_window->GetFrame()->DomWindow());
117 DCHECK_EQ(DOMWindow::CrossDocumentAccessPolicy::kAllowed,
118 *cross_document_access);
119
120 // It's important to check that target_window is a LocalDOMWindow: it's
121 // possible for a remote frame and local frame to have the same security
122 // origin, depending on the model being used to allocate Frames between
123 // processes. See https://crbug.com/601629.
124 const auto* local_target_window = DynamicTo<LocalDOMWindow>(target_window);
125 if (!(accessing_window && local_target_window))
126 return false;
127
128 const SecurityOrigin* accessing_origin =
129 accessing_window->document()->GetSecurityOrigin();
130
131 SecurityOrigin::AccessResultDomainDetail detail;
132 bool can_access = accessing_origin->CanAccess(
133 local_target_window->document()->GetSecurityOrigin(), detail);
134 if (detail ==
135 SecurityOrigin::AccessResultDomainDetail::kDomainSetByOnlyOneOrigin ||
136 detail ==
137 SecurityOrigin::AccessResultDomainDetail::kDomainMatchNecessary ||
138 detail == SecurityOrigin::AccessResultDomainDetail::kDomainMismatch) {
139 UseCounter::Count(
140 accessing_window->document(),
141 can_access ? WebFeature::kDocumentDomainEnabledCrossOriginAccess
142 : WebFeature::kDocumentDomainBlockedCrossOriginAccess);
143 }
144 if (!can_access) {
145 // Ensure that if we got a cluster mismatch that it was due to a feature
146 // policy being enabled and not a logic bug.
147 if (detail == SecurityOrigin::AccessResultDomainDetail::
148 kDomainNotRelevantAgentClusterMismatch) {
149 // Assert that because the agent clusters are different than the
150 // WindowAgentFactories must also be different.
151 SECURITY_CHECK(!IsSameWindowAgentFactory(
152 accessing_window->document(), local_target_window->document()));
153
154 *cross_document_access =
155 DOMWindow::CrossDocumentAccessPolicy::kDisallowed;
156 }
157 return false;
158 }
159
160 // Notify the loader's client if the initial document has been accessed.
161 LocalFrame* target_frame = local_target_window->GetFrame();
162 if (target_frame &&
163 target_frame->Loader().StateMachine()->IsDisplayingInitialEmptyDocument())
164 target_frame->Loader().DidAccessInitialDocument();
165
166 return true;
167 }
168
169 template <typename ExceptionStateOrErrorReportOption>
CanAccessWindow(const LocalDOMWindow * accessing_window,const DOMWindow * target_window,ExceptionStateOrErrorReportOption & error_report)170 bool CanAccessWindow(const LocalDOMWindow* accessing_window,
171 const DOMWindow* target_window,
172 ExceptionStateOrErrorReportOption& error_report) {
173 DOMWindow::CrossDocumentAccessPolicy cross_document_access =
174 DOMWindow::CrossDocumentAccessPolicy::kAllowed;
175 if (CanAccessWindowInternal(accessing_window, target_window,
176 &cross_document_access))
177 return true;
178
179 ReportOrThrowSecurityError(accessing_window, target_window,
180 cross_document_access, error_report);
181 return false;
182 }
183
FindWindow(v8::Isolate * isolate,const WrapperTypeInfo * type,v8::Local<v8::Object> holder)184 DOMWindow* FindWindow(v8::Isolate* isolate,
185 const WrapperTypeInfo* type,
186 v8::Local<v8::Object> holder) {
187 if (V8Window::GetWrapperTypeInfo()->Equals(type))
188 return V8Window::ToImpl(holder);
189
190 if (V8Location::GetWrapperTypeInfo()->Equals(type))
191 return V8Location::ToImpl(holder)->DomWindow();
192
193 // This function can handle only those types listed above.
194 NOTREACHED();
195 return nullptr;
196 }
197
198 } // namespace
199
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const DOMWindow * target,ExceptionState & exception_state)200 bool BindingSecurity::ShouldAllowAccessTo(
201 const LocalDOMWindow* accessing_window,
202 const DOMWindow* target,
203 ExceptionState& exception_state) {
204 DCHECK(target);
205
206 // TODO(https://crbug.com/723057): This is intended to match the legacy
207 // behavior of when access checks revolved around Frame pointers rather than
208 // DOMWindow pointers. This prevents web-visible behavior changes, since the
209 // previous implementation had to follow the back pointer to the Frame, and
210 // would have to early return when it was null.
211 if (!target->GetFrame())
212 return false;
213 bool can_access = CanAccessWindow(accessing_window, target, exception_state);
214
215 if (!can_access && accessing_window) {
216 UseCounter::Count(accessing_window->document(),
217 WebFeature::kCrossOriginPropertyAccess);
218 if (target->opener() == accessing_window) {
219 UseCounter::Count(accessing_window->document(),
220 WebFeature::kCrossOriginPropertyAccessFromOpener);
221 }
222 }
223
224 return can_access;
225 }
226
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const DOMWindow * target,ErrorReportOption reporting_option)227 bool BindingSecurity::ShouldAllowAccessTo(
228 const LocalDOMWindow* accessing_window,
229 const DOMWindow* target,
230 ErrorReportOption reporting_option) {
231 DCHECK(target);
232
233 // TODO(https://crbug.com/723057): This is intended to match the legacy
234 // behavior of when access checks revolved around Frame pointers rather than
235 // DOMWindow pointers. This prevents web-visible behavior changes, since the
236 // previous implementation had to follow the back pointer to the Frame, and
237 // would have to early return when it was null.
238 if (!target->GetFrame())
239 return false;
240
241 bool can_access = CanAccessWindow(accessing_window, target, reporting_option);
242
243 if (!can_access && accessing_window) {
244 UseCounter::Count(accessing_window->document(),
245 WebFeature::kCrossOriginPropertyAccess);
246 if (target->opener() == accessing_window) {
247 UseCounter::Count(accessing_window->document(),
248 WebFeature::kCrossOriginPropertyAccessFromOpener);
249 }
250 }
251
252 return can_access;
253 }
254
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const Location * target,ExceptionState & exception_state)255 bool BindingSecurity::ShouldAllowAccessTo(
256 const LocalDOMWindow* accessing_window,
257 const Location* target,
258 ExceptionState& exception_state) {
259 DCHECK(target);
260
261 // TODO(https://crbug.com/723057): This is intended to match the legacy
262 // behavior of when access checks revolved around Frame pointers rather than
263 // DOMWindow pointers. This prevents web-visible behavior changes, since the
264 // previous implementation had to follow the back pointer to the Frame, and
265 // would have to early return when it was null.
266 if (!target->DomWindow()->GetFrame())
267 return false;
268
269 bool can_access =
270 CanAccessWindow(accessing_window, target->DomWindow(), exception_state);
271
272 if (!can_access && accessing_window) {
273 UseCounter::Count(accessing_window->document(),
274 WebFeature::kCrossOriginPropertyAccess);
275 if (target->DomWindow()->opener() == accessing_window) {
276 UseCounter::Count(accessing_window->document(),
277 WebFeature::kCrossOriginPropertyAccessFromOpener);
278 }
279 }
280
281 return can_access;
282 }
283
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const Location * target,ErrorReportOption reporting_option)284 bool BindingSecurity::ShouldAllowAccessTo(
285 const LocalDOMWindow* accessing_window,
286 const Location* target,
287 ErrorReportOption reporting_option) {
288 DCHECK(target);
289
290 // TODO(https://crbug.com/723057): This is intended to match the legacy
291 // behavior of when access checks revolved around Frame pointers rather than
292 // DOMWindow pointers. This prevents web-visible behavior changes, since the
293 // previous implementation had to follow the back pointer to the Frame, and
294 // would have to early return when it was null.
295 if (!target->DomWindow()->GetFrame())
296 return false;
297
298 bool can_access =
299 CanAccessWindow(accessing_window, target->DomWindow(), reporting_option);
300
301 if (!can_access && accessing_window) {
302 UseCounter::Count(accessing_window->document(),
303 WebFeature::kCrossOriginPropertyAccess);
304 if (target->DomWindow()->opener() == accessing_window) {
305 UseCounter::Count(accessing_window->document(),
306 WebFeature::kCrossOriginPropertyAccessFromOpener);
307 }
308 }
309
310 return can_access;
311 }
312
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const Node * target,ExceptionState & exception_state)313 bool BindingSecurity::ShouldAllowAccessTo(
314 const LocalDOMWindow* accessing_window,
315 const Node* target,
316 ExceptionState& exception_state) {
317 if (!target)
318 return false;
319 return CanAccessWindow(accessing_window, target->GetDocument().domWindow(),
320 exception_state);
321 }
322
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const Node * target,ErrorReportOption reporting_option)323 bool BindingSecurity::ShouldAllowAccessTo(
324 const LocalDOMWindow* accessing_window,
325 const Node* target,
326 ErrorReportOption reporting_option) {
327 if (!target)
328 return false;
329 return CanAccessWindow(accessing_window, target->GetDocument().domWindow(),
330 reporting_option);
331 }
332
ShouldAllowAccessToFrame(const LocalDOMWindow * accessing_window,const Frame * target,ExceptionState & exception_state)333 bool BindingSecurity::ShouldAllowAccessToFrame(
334 const LocalDOMWindow* accessing_window,
335 const Frame* target,
336 ExceptionState& exception_state) {
337 if (!target || !target->GetSecurityContext())
338 return false;
339 return CanAccessWindow(accessing_window, target->DomWindow(),
340 exception_state);
341 }
342
ShouldAllowAccessToFrame(const LocalDOMWindow * accessing_window,const Frame * target,ErrorReportOption reporting_option)343 bool BindingSecurity::ShouldAllowAccessToFrame(
344 const LocalDOMWindow* accessing_window,
345 const Frame* target,
346 ErrorReportOption reporting_option) {
347 if (!target || !target->GetSecurityContext())
348 return false;
349 return CanAccessWindow(accessing_window, target->DomWindow(),
350 reporting_option);
351 }
352
353 namespace {
354
355 template <typename ExceptionStateOrErrorReportOption>
ShouldAllowAccessToV8ContextInternal(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> target_context,ExceptionStateOrErrorReportOption & error_report)356 bool ShouldAllowAccessToV8ContextInternal(
357 v8::Local<v8::Context> accessing_context,
358 v8::Local<v8::Context> target_context,
359 ExceptionStateOrErrorReportOption& error_report) {
360 // Fast path for the most likely case.
361 if (accessing_context == target_context)
362 return true;
363
364 // Workers and worklets do not support multiple contexts, so both of
365 // |accessing_context| and |target_context| must be windows at this point.
366
367 // remote_object->CreationContext() returns the empty handle. Remote contexts
368 // are unconditionally treated as cross origin.
369 if (target_context.IsEmpty()) {
370 ReportOrThrowSecurityError(ToLocalDOMWindow(accessing_context), nullptr,
371 DOMWindow::CrossDocumentAccessPolicy::kAllowed,
372 error_report);
373 return false;
374 }
375
376 LocalFrame* target_frame = ToLocalFrameIfNotDetached(target_context);
377 // TODO(dcheng): Why doesn't this code just use DOMWindows throughout? Can't
378 // we just always use ToLocalDOMWindow(context)?
379 if (!target_frame) {
380 // Sandbox detached frames - they can't create cross origin objects.
381 LocalDOMWindow* accessing_window = ToLocalDOMWindow(accessing_context);
382 LocalDOMWindow* target_window = ToLocalDOMWindow(target_context);
383
384 // TODO(https://crbug.com/723057): This is tricky: this intentionally uses
385 // the internal CanAccessWindow() helper rather than ShouldAllowAccessTo().
386 // ShouldAllowAccessTo() unconditionally denies access if the DOMWindow is
387 // not attached to a Frame, but this code is intended for handling the
388 // detached DOMWindow case.
389 return CanAccessWindow(accessing_window, target_window, error_report);
390 }
391
392 const DOMWrapperWorld& accessing_world =
393 DOMWrapperWorld::World(accessing_context);
394 const DOMWrapperWorld& target_world = DOMWrapperWorld::World(target_context);
395 CHECK_EQ(accessing_world.GetWorldId(), target_world.GetWorldId());
396
397 return !accessing_world.IsMainWorld() ||
398 BindingSecurity::ShouldAllowAccessToFrame(
399 ToLocalDOMWindow(accessing_context), target_frame, error_report);
400 }
401
402 } // namespace
403
ShouldAllowAccessToV8Context(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> target_context,ExceptionState & exception_state)404 bool BindingSecurity::ShouldAllowAccessToV8Context(
405 v8::Local<v8::Context> accessing_context,
406 v8::Local<v8::Context> target_context,
407 ExceptionState& exception_state) {
408 return ShouldAllowAccessToV8ContextInternal(accessing_context, target_context,
409 exception_state);
410 }
411
ShouldAllowAccessToV8Context(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> target_context,ErrorReportOption reporting_option)412 bool BindingSecurity::ShouldAllowAccessToV8Context(
413 v8::Local<v8::Context> accessing_context,
414 v8::Local<v8::Context> target_context,
415 ErrorReportOption reporting_option) {
416 return ShouldAllowAccessToV8ContextInternal(accessing_context, target_context,
417 reporting_option);
418 }
419
ShouldAllowWrapperCreationOrThrowException(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> creation_context,const WrapperTypeInfo * wrapper_type_info)420 bool BindingSecurity::ShouldAllowWrapperCreationOrThrowException(
421 v8::Local<v8::Context> accessing_context,
422 v8::Local<v8::Context> creation_context,
423 const WrapperTypeInfo* wrapper_type_info) {
424 // Fast path for the most likely case.
425 if (accessing_context == creation_context)
426 return true;
427
428 // According to
429 // https://html.spec.whatwg.org/C/#security-location,
430 // cross-origin script access to a few properties of Location is allowed.
431 // Location already implements the necessary security checks.
432 if (wrapper_type_info->Equals(V8Location::GetWrapperTypeInfo()))
433 return true;
434
435 ExceptionState exception_state(accessing_context->GetIsolate(),
436 ExceptionState::kConstructionContext,
437 wrapper_type_info->interface_name);
438 return ShouldAllowAccessToV8Context(accessing_context, creation_context,
439 exception_state);
440 }
441
RethrowWrapperCreationException(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> creation_context,const WrapperTypeInfo * wrapper_type_info,v8::Local<v8::Value> cross_context_exception)442 void BindingSecurity::RethrowWrapperCreationException(
443 v8::Local<v8::Context> accessing_context,
444 v8::Local<v8::Context> creation_context,
445 const WrapperTypeInfo* wrapper_type_info,
446 v8::Local<v8::Value> cross_context_exception) {
447 DCHECK(!cross_context_exception.IsEmpty());
448 v8::Isolate* isolate = creation_context->GetIsolate();
449 ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
450 wrapper_type_info->interface_name);
451 if (!ShouldAllowAccessToV8Context(accessing_context, creation_context,
452 exception_state)) {
453 // A cross origin exception has turned into a SecurityError.
454 CHECK(exception_state.HadException());
455 return;
456 }
457 exception_state.RethrowV8Exception(cross_context_exception);
458 }
459
FailedAccessCheckFor(v8::Isolate * isolate,const WrapperTypeInfo * type,v8::Local<v8::Object> holder)460 void BindingSecurity::FailedAccessCheckFor(v8::Isolate* isolate,
461 const WrapperTypeInfo* type,
462 v8::Local<v8::Object> holder) {
463 DOMWindow* target = FindWindow(isolate, type, holder);
464 // Failing to find a target means something is wrong. Failing to throw an
465 // exception could be a security issue, so just crash.
466 CHECK(target);
467
468 // TODO(https://crbug.com/723057): This is intended to match the legacy
469 // behavior of when access checks revolved around Frame pointers rather than
470 // DOMWindow pointers. This prevents web-visible behavior changes, since the
471 // previous implementation had to follow the back pointer to the Frame, and
472 // would have to early return when it was null.
473 if (!target->GetFrame())
474 return;
475
476 auto* local_dom_window = CurrentDOMWindow(isolate);
477 // Determine if the access check failure was because of cross-origin or if the
478 // WindowAgentFactory is different. If the WindowAgentFactories are different
479 // it indicates that the "disallowdocumentaccess" attribute was used on an
480 // iframe somewhere in the ancestor chain so report the error as "restricted"
481 // instead of "cross-origin".
482 DOMWindow::CrossDocumentAccessPolicy cross_document_access =
483 (!target->ToLocalDOMWindow() ||
484 IsSameWindowAgentFactory(local_dom_window->document(),
485 target->ToLocalDOMWindow()->document()))
486 ? DOMWindow::CrossDocumentAccessPolicy::kAllowed
487 : DOMWindow::CrossDocumentAccessPolicy::kDisallowed;
488
489 // TODO(dcheng): Add ContextType, interface name, and property name as
490 // arguments, so the generated exception can be more descriptive.
491 ExceptionState exception_state(isolate, ExceptionState::kUnknownContext,
492 nullptr, nullptr);
493 exception_state.ThrowSecurityError(
494 target->SanitizedCrossDomainAccessErrorMessage(local_dom_window,
495 cross_document_access),
496 target->CrossDomainAccessErrorMessage(local_dom_window,
497 cross_document_access));
498 }
499
ShouldAllowNamedAccessTo(const DOMWindow * accessing_window,const DOMWindow * target_window)500 bool BindingSecurity::ShouldAllowNamedAccessTo(
501 const DOMWindow* accessing_window,
502 const DOMWindow* target_window) {
503 const Frame* accessing_frame = accessing_window->GetFrame();
504 DCHECK(accessing_frame);
505 DCHECK(accessing_frame->GetSecurityContext());
506 const SecurityOrigin* accessing_origin =
507 accessing_frame->GetSecurityContext()->GetSecurityOrigin();
508
509 const Frame* target_frame = target_window->GetFrame();
510 DCHECK(target_frame);
511 DCHECK(target_frame->GetSecurityContext());
512 const SecurityOrigin* target_origin =
513 target_frame->GetSecurityContext()->GetSecurityOrigin();
514 SECURITY_CHECK(!(target_window && target_window->GetFrame()) ||
515 target_window == target_window->GetFrame()->DomWindow());
516
517 if (!accessing_origin->CanAccess(target_origin))
518 return false;
519
520 // Note that there is no need to call back
521 // FrameLoader::didAccessInitialDocument() because |targetWindow| must be
522 // a child window inside iframe or frame and it doesn't have a URL bar,
523 // so there is no need to worry about URL spoofing.
524
525 return true;
526 }
527
528 } // namespace blink
529