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

[pull] master from skulpt:master #17

Merged
merged 3 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions src/ffi.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Sk.ffi = {

const OBJECT_PROTO = Object.prototype;
const FUNC_PROTO = Function.prototype;
const MAP_PROTO = Map.prototype;
const SET_PROTO = Set.prototype;

/**
* maps from Javascript Object/Array/string to Python dict/list/str.
Expand Down Expand Up @@ -308,6 +310,11 @@ function toPyDict(obj, hooks) {
return ret;
}

function isCrossOriginWindow(obj) {
// based on https://github.com/weizman/is-cross-origin
return obj !== null && typeof obj === "object" && obj.window === obj && Object.getPrototypeOf(obj) === null;
}

// cache the proxied objects in a weakmap
const _proxied = new WeakMap();
const methodSelfCache = new WeakMap();
Expand Down Expand Up @@ -338,10 +345,10 @@ function proxy(obj, flags) {
} else if (Array.isArray(obj)) {
rv = new JsProxyList(obj);
} else {
const constructor = obj.constructor;
if (constructor === Map) {
const proto = Object.getPrototypeOf(obj);
if (proto === MAP_PROTO) {
rv = new JsProxyMap(obj);
} else if (constructor === Set) {
} else if (proto === SET_PROTO) {
rv = new JsProxySet(obj);
} else {
rv = new JsProxy(obj, flags);
Expand Down Expand Up @@ -667,6 +674,10 @@ const JsProxy = Sk.abstr.buildNativeClass("Proxy", {
const jsName = pyName.toString();
const attr = this.js$wrapped[jsName];
if (attr !== undefined) {
if (isCrossOriginWindow(attr)) {
// we can't do the usual toPy dance, since accessing attributes breaks the same-origin policy
return proxy(attr, { name: "CrossOriginWindow" });
}
// here we override the funcHook to pass the bound object
return toPy(attr, boundHook(this.js$wrapped, jsName));
} else if (jsName in this.js$wrapped) {
Expand Down
26 changes: 26 additions & 0 deletions test/unit3/test_skulpt_interop.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,32 @@ def bar(self):
window.method_2 = method_2
self.assertIsNot(method_1, method_2)
self.assertIs(window.method_1, window.method_2)

def test_cross_origin_window(self):
window.x = {"foo": None}
self.assertIsNone(window.x.foo)

if "window" not in window:
# can't test this in a node environment
return

iframe = window.document.createElement("iframe")
iframe.src = "http://skulpt.org"

window.document.body.prepend(iframe)

contentWindow = iframe.contentWindow
def wait_for_load(resolve, reject):
iframe.onload = resolve
iframe.onerror = reject

p = window.Promise(wait_for_load).then(lambda *args: print("iframe loaded"))

with self.assertRaisesRegex(Exception, "SecurityError"):
# should be a cross origin error
hasattr(contentWindow, "foo")

contentWindow.postMessage({"data": "*"})


if __name__ == "__main__":
Expand Down
Loading