Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessing contentWindow on cross domain iframes with static js interop doesn't work #54938

Closed
jezell opened this issue Feb 16, 2024 · 17 comments · Fixed by dart-lang/web#291
Closed
Assignees
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. P1 A high priority bug; for example, a single project is unusable or has many test failures web-dev-compiler web-js-interop Issues that impact all js interop

Comments

@jezell
Copy link

jezell commented Feb 16, 2024

A security exception is thrown when trying to access the contentWindow property of a cross domain iframe using static interop:

frame.contentWindow!.postMessage(...)

Given the same setup using non static interop, the call succeeds:

(frame as html.IFrameElement).contentWindow!.postMessage(...)

It blows up here in the rtti:

      default:
        // The interceptors for native JavaScript types like bool, string, etc.
        // (excluding number and function, see above) are stored as a symbolized
        // property and can be accessed from the native value itself.
        classRef = JS('', '#[#]', obj, _extensionType);
        

With this message:


Uncaught DOMException: Failed to read a named property from 'Window': Blocked a frame with origin "http://localhost:8002" from accessing a cross-origin frame.
   at Object.getInterceptorForRti (http://localhost:8002/dart_sdk.js:12719:27)
   at dart_rti.Rti.new._isTestViaProperty (http://localhost:8002/dart_sdk.js:24534:28)
   at dart_rti.Rti.new._generalNullableIsTestImplementation (http://localhost:8002/dart_sdk.js:24525:24)
   at dart_rti.Rti.new._generalNullableAsCheckImplementation (http://localhost:8002/dart_sdk.js:24563:28)
   at Object.getProperty (http://localhost:8002/dart_sdk.js:71713:18)
   ...
   at internalCallback (http://localhost:8002/dart_sdk.js:37398:11)
   

It appears something about the rtti check is accessing a property that is locked down on cross domain frames when trying to get the Window object, but I'm unable to step deeper into the stack in the devtools to see exactly the property that is being checked.

@dart-github-bot
Copy link
Collaborator

Item Details
Summary Static interop fails to access contentWindow on cross-domain iframes due to security exception.
Triage to area-dart-cli (medium confidence)

(what's this?)

@devoncarew devoncarew added the area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. label Feb 16, 2024
@sigmundch sigmundch added web-js-interop Issues that impact all js interop web-dev-compiler and removed web-js-interop Issues that impact all js interop labels Feb 16, 2024
@sigmundch
Copy link
Member

cc @nshahan @fishythefish - I believe this wouldn't affect the dart2js compiler due to differences in how we lookup the interceptor there, but it's worth checking nonetheless.

@jezell
Copy link
Author

jezell commented Feb 16, 2024

Can repro similar behavior in JS trying to access properties that don't exist on a cross domain iframe.

var frame = document.createElement("iframe")
frame.src = "https://some_other_domain.com";
frame.contentWindow["property_that_does_not_exist"]; // throws

however

var frame = document.createElement("iframe")
frame.src = "https://some_other_domain.com";
frame.contentWindow["property_that_does_not_exist"] = 1;
frame.contentWindow["property_that_does_not_exist"]; // doesn't throw

So it is checking to see if it is a dart object or a js object, but that can't be done without an exception being thrown on the Window object since it is cross domain. Perhaps the an exception looking up the symbol could be handled and then the symbol set to null in the catch for a minimal performance penalty so it only happens first time around?

@nshahan
Copy link
Contributor

nshahan commented Feb 16, 2024

@jezell Thanks for the helpful report!

@sigmundch I agree with your assessment. The code referenced here is only run in DDC. If dart2js has a problem finding the interceptor for cross origin values it will need to be addressed separately. Would dart2js allow for apps from different frames to pass Dart values between them or are they forced to do some type of conversion to a JavaScript interop value first?

If there isn't any Dart to Dart object passing allowed then in DDC it seems like the right thing to do here is to treat any cross origin value as a LegacyJavaScriptObject. We just need to find the right way to test for that as efficiently as possible.

@sigmundch
Copy link
Member

cc @rakudrama

precisely - I think treating it as JSObject makes sense.

Anything sent between separate apps (whether they are on the same page or separate iframes) will need to go through interop, and as a result is treated as JS values. Even if you send raw unboxed value through the interop boundary (bypassing some interop conversions), all RTI data is isolated, so it will be viewed as a JS object.

@srujzs
Copy link
Contributor

srujzs commented Feb 16, 2024

Similar issue: #54443. It's possible we may come across other issues here even if this specific bug is fixed in the RTI.

@DanTup
Copy link
Collaborator

DanTup commented Mar 27, 2024

The Flutter sidebar in VS Code is failing with what seems like the same error:

Dart-Code/Dart-Code#5049

The sidebar (part of Flutter DevTools) is hosted inside a sandboxed iframe in VS Code and trying to communicate with its parent using window.parent?.postMessage

If this isn't a simple fix, are there any reasonable workarounds we could apply to that code?

@jezell
Copy link
Author

jezell commented Mar 27, 2024

@DanTup As a workaround, I believe you can cast to the old dart:html version which doesn't have the problem.

DanTup added a commit to DanTup/devtools that referenced this issue Mar 27, 2024
This is a workaround for dart-lang/sdk#54938 because the package:web version of postMessage causes security exceptions.

Fixes Dart-Code/Dart-Code#5049
@srujzs
Copy link
Contributor

srujzs commented Mar 27, 2024

That issue seems more like #54443 considering it's dart2js, but it's a similar issue around cross-domain frames. The error seems to arise from _generalNullableAsCheckImplementation, indicating there's a downcast with a nullable type.

Looking at the source I wonder if it's parent?.postMessage triggering this. Parker is using a workaround with getProperty to work around this. Maybe that can help here too, but I'll need to look at the JS code to determine which part is the issue. Is there a good way to repro this and get the sources?

@DanTup
Copy link
Collaborator

DanTup commented Mar 27, 2024

@srujzs you can repro this using VS Code and Flutter master (just try to load the Flutter Sidebar), but it's probably not very easy to debug that way (however I don't know if it can easily be reproduced outside of it because of the sandboxed iframe etc.).

You can click Help -> Toggle Developer Tools in VS Code to open the Chrome DevTools.

I'll see if getProperty helps.

@DanTup
Copy link
Collaborator

DanTup commented Mar 27, 2024

I tried all of these, but no change:

final parent = window.parent;
if (parent != null) {
  parent.postMessage(message.jsify(), targetOrigin.toJS);
}
(window.parent as JSObject?)
      ?.callMethod('postMessage'.toJS, message.jsify(), targetOrigin.toJS);
void postMessage(Object? message, String targetOrigin) {
  (window as JSObject)
      .getProperty<JSObject?>('parent'.toJS)
      ?.callMethod('postMessage'.toJS, message.jsify(), targetOrigin.toJS);
}
var parent = (window as JSObject).getProperty<JSObject?>('parent'.toJS);
if (parent != null) {
  parent.callMethod('postMessage'.toJS, message.jsify(), targetOrigin.toJS);
}

I don't know if any of them were exactly what you were suggesting though.

@DanTup
Copy link
Collaborator

DanTup commented Mar 27, 2024

Oh by the way, you can get at the source from your Flutter SDK checkout (be on master, not stable):

flutter\bin\cache\dart-sdk\bin\resources\devtools\main.dart.js

(let me know if I should move this to another issue since it sounds like it might not be the same)

For convenience, here's the bit of code I suspect you care about:

  A.DartToolingApiImpl_DartToolingApiImpl$postMessage_closure.prototype = {
    call$1(message) {
      var t1 = type$.nullable_JavaScriptObject._as(type$.JavaScriptObject._as(self.window).parent);
      if (t1 != null)
        A.callMethod(t1, "postMessage", [A.jsify(message), "*"], type$.void);
    },
    $signature: 7
  };

@srujzs
Copy link
Contributor

srujzs commented Mar 27, 2024

(following up in flutter/devtools#7476 - thanks for all the details!)

@jezell
Copy link
Author

jezell commented Aug 8, 2024

This is still blocking us from being able to move to package:web.

@mraleph
Copy link
Member

mraleph commented Aug 8, 2024

Can we bump priority of fixing this? cc @natebiggs

@srujzs
Copy link
Contributor

srujzs commented Aug 8, 2024

Yeah, I don't see a usable workaround for this like we had with the Window wrapper in dart:html that wrapped dynamic. I'll work on seeing if we can get a wrapper like that in package:web next.

@srujzs srujzs self-assigned this Aug 8, 2024
@srujzs srujzs added the web-js-interop Issues that impact all js interop label Aug 8, 2024
@kevmoo kevmoo added the P1 A high priority bug; for example, a single project is unusable or has many test failures label Aug 8, 2024
@biggs0125
Copy link

@srujzs and I talked about this earlier. We've prioritized finding a solution here.

We want to make sure that it's an separate opt-in API for cases where a user knows they might be dealing with a x-domain object. Given the need for extra interceptor logic/a wrapper we want to make sure we don't degrade other interop use cases.

srujzs added a commit to srujzs/web that referenced this issue Aug 24, 2024
Closes dart-lang/sdk#54443
Closes dart-lang#247
Closes dart-lang/sdk#54938

Since cross-origin objects have limitations around
access, wrappers are introduced to do the only safe
operations. Extension methods are added to get instances
of these wrappers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. P1 A high priority bug; for example, a single project is unusable or has many test failures web-dev-compiler web-js-interop Issues that impact all js interop
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

10 participants