mirror of https://github.com/pyodide/pyodide.git
Fix creation of PyProxy when CSP without unsafe-eval is used (#3075)
See discussion following #2432 (comment).
This commit is contained in:
parent
60714a9cf0
commit
fbed5b0cf7
|
@ -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`
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!-- Bootstrap HTML for running the unit tests. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>pyodide</title>
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src http: 'wasm-unsafe-eval'"
|
||||
/>
|
||||
<script type="text/javascript">
|
||||
window.logs = [];
|
||||
console.log = function (message) {
|
||||
window.logs.push(message);
|
||||
};
|
||||
console.warn = function (message) {
|
||||
window.logs.push(message);
|
||||
};
|
||||
console.info = function (message) {
|
||||
window.logs.push(message);
|
||||
};
|
||||
console.error = function (message) {
|
||||
window.logs.push(message);
|
||||
};
|
||||
</script>
|
||||
<script src="./pyodide.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
|
@ -1,4 +1,5 @@
|
|||
import re
|
||||
import shutil
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
|
@ -7,8 +8,9 @@ from typing import Any
|
|||
import pytest
|
||||
from pytest_pyodide import run_in_pyodide
|
||||
|
||||
from conftest import ROOT_PATH
|
||||
from conftest import DIST_PATH, ROOT_PATH
|
||||
from pyodide.code import CodeRunner, eval_code, find_imports, should_quiet # noqa: E402
|
||||
from pyodide_build.common import get_pyodide_root
|
||||
|
||||
|
||||
def _strip_assertions_stderr(messages: Sequence[str]) -> list[str]:
|
||||
|
@ -1315,8 +1317,6 @@ def test_relative_index_url(selenium, tmp_path):
|
|||
if version_result.stdout.startswith("v14"):
|
||||
extra_node_args.append("--experimental-wasm-bigint")
|
||||
|
||||
import shutil
|
||||
|
||||
shutil.copy(ROOT_PATH / "dist/pyodide.js", tmp_dir / "pyodide.js")
|
||||
shutil.copytree(ROOT_PATH / "dist/node_modules", tmp_dir / "node_modules")
|
||||
|
||||
|
@ -1357,3 +1357,18 @@ def test_relative_index_url(selenium, tmp_path):
|
|||
assert result.stdout.strip().split("\n")[-1] == str(ROOT_PATH / "dist") + "/"
|
||||
finally:
|
||||
print_result(result)
|
||||
|
||||
|
||||
@pytest.mark.xfail_browsers(
|
||||
node="Browser only", safari="Safari doesn't support wasm-unsafe-eval"
|
||||
)
|
||||
def test_csp(selenium_standalone_noload):
|
||||
selenium = selenium_standalone_noload
|
||||
target_path = DIST_PATH / "test_csp.html"
|
||||
try:
|
||||
shutil.copy(get_pyodide_root() / "src/templates/test_csp.html", target_path)
|
||||
selenium.goto(f"{selenium.base_url}/test_csp.html")
|
||||
selenium.javascript_setup()
|
||||
selenium.load_pyodide()
|
||||
finally:
|
||||
target_path.unlink()
|
||||
|
|
Loading…
Reference in New Issue