diff --git a/docs/project/changelog.md b/docs/project/changelog.md index 945e088a1..46aa93be5 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -44,6 +44,14 @@ substitutions: `indexURL` (this was a regression in v0.21.2). {pr}`3077` +- {{ Enhancement }} Pyodide now works with a content security policy that + doesn't include `unsafe-eval`. It is still necessary to include + `wasm-unsafe-eval` (and probably always will be). Since current Safari + versions have no support for `wasm-unsafe-eval`, it is necessary to include + `unsafe-eval` in order to work in Safari. This will likely be fixed in the + next Safari release: https://bugs.webkit.org/show_bug.cgi?id=235408 + {pr}`3075` + - {{ Fix }} Add `url` to list of pollyfilled packages for webpack compatibility. {pr}`3080` diff --git a/src/core/pyproxy.ts b/src/core/pyproxy.ts index 741a94666..b50b4cde7 100644 --- a/src/core/pyproxy.ts +++ b/src/core/pyproxy.ts @@ -131,19 +131,23 @@ type PyProxyCache = { cacheId: number; refcnt: number; leaked?: boolean }; Module.pyproxy_new = function (ptrobj: number, cache?: PyProxyCache) { let flags = Module._pyproxy_getflags(ptrobj); let cls = Module.getPyProxyClass(flags); - // Reflect.construct calls the constructor of Module.PyProxyClass but sets - // the prototype as cls.prototype. This gives us a way to dynamically create - // subclasses of PyProxyClass (as long as we don't need to use the "new - // cls(ptrobj)" syntax). let target; if (flags & IS_CALLABLE) { - // To make a callable proxy, we must call the Function constructor. - // In this case we are effectively subclassing Function. - target = Reflect.construct(Function, [], cls); + // In this case we are effectively subclassing Function in order to ensure + // that the proxy is callable. With a Content Security Protocol that doesn't + // allow unsafe-eval, we can't invoke the Function constructor directly. So + // instead we create a function in the universally allowed way and then use + // `setPrototypeOf`. The documentation for `setPrototypeOf` says to use + // `Object.create` or `Reflect.construct` instead for performance reasons + // but neither of those work here. + target = function () {}; + Object.setPrototypeOf(target, cls.prototype); // Remove undesirable properties added by Function constructor. Note: we // can't remove "arguments" or "caller" because they are not configurable // and not writable + // @ts-ignore delete target.length; + // @ts-ignore delete target.name; // prototype isn't configurable so we can't delete it but it's writable. target.prototype = undefined; diff --git a/src/templates/test_csp.html b/src/templates/test_csp.html new file mode 100644 index 000000000..fd83e4a96 --- /dev/null +++ b/src/templates/test_csp.html @@ -0,0 +1,28 @@ + + + +
+