Skip to content

Commit f33cc4f

Browse files
committed
Re-add changes from deleted commits
1 parent ad51624 commit f33cc4f

File tree

3 files changed

+10
-112
lines changed

3 files changed

+10
-112
lines changed

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ artifacts = ["/src/reactpy/static/"]
5454
artifacts = ["/src/reactpy/static/"]
5555

5656
[tool.hatch.metadata]
57-
license-files = { paths = ["LICENSE.md"] }
57+
license-files = { paths = ["LICENSE"] }
5858

5959
[tool.hatch.envs.default]
6060
installer = "uv"

src/reactpy/html.py

+6-51
Original file line numberDiff line numberDiff line change
@@ -422,54 +422,10 @@ def _script(
422422
key is given, the key is inferred to be the content of the script or, lastly its
423423
'src' attribute if that is given.
424424
425-
If no attributes are given, the content of the script may evaluate to a function.
426-
This function will be called when the script is initially created or when the
427-
content of the script changes. The function may itself optionally return a teardown
428-
function that is called when the script element is removed from the tree, or when
429-
the script content changes.
430-
431425
Notes:
432426
Do not use unsanitized data from untrusted sources anywhere in your script.
433-
Doing so may allow for malicious code injection. Consider this **insecure**
434-
code:
435-
436-
.. code-block::
437-
438-
my_script = html.script(f"console.log('{user_bio}');")
439-
440-
A clever attacker could construct ``user_bio`` such that they could escape the
441-
string and execute arbitrary code to perform cross-site scripting
442-
(`XSS <https://en.wikipedia.org/wiki/Cross-site_scripting>`__`). For example,
443-
what if ``user_bio`` were of the form:
444-
445-
.. code-block:: text
446-
447-
'); attackerCodeHere(); ('
448-
449-
This would allow the following Javascript code to be executed client-side:
450-
451-
.. code-block:: js
452-
453-
console.log(''); attackerCodeHere(); ('');
454-
455-
One way to avoid this could be to escape ``user_bio`` so as to prevent the
456-
injection of Javascript code. For example:
457-
458-
.. code-block:: python
459-
460-
import json
461-
462-
my_script = html.script(f"console.log({json.dumps(user_bio)});")
463-
464-
This would prevent the injection of Javascript code by escaping the ``user_bio``
465-
string. In this case, the following client-side code would be executed instead:
466-
467-
.. code-block:: js
468-
469-
console.log("'); attackerCodeHere(); ('");
470-
471-
This is a very simple example, but it illustrates the point that you should
472-
always be careful when using unsanitized data from untrusted sources.
427+
Doing so may allow for malicious code injection
428+
(`XSS <https://en.wikipedia.org/wiki/Cross-site_scripting>`__`).
473429
"""
474430
model: VdomDict = {"tagName": "script"}
475431

@@ -481,13 +437,12 @@ def _script(
481437
if len(children) > 1:
482438
msg = "'script' nodes may have, at most, one child."
483439
raise ValueError(msg)
484-
elif not isinstance(children[0], str):
440+
if not isinstance(children[0], str):
485441
msg = "The child of a 'script' must be a string."
486442
raise ValueError(msg)
487-
else:
488-
model["children"] = children
489-
if key is None:
490-
key = children[0]
443+
model["children"] = children
444+
if key is None:
445+
key = children[0]
491446

492447
if attributes:
493448
model["attributes"] = attributes

tests/test_html.py

+3-60
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,7 @@
33
from reactpy import component, config, html
44
from reactpy.testing import DisplayFixture, poll
55
from reactpy.utils import Ref
6-
from tests.tooling.hooks import use_counter, use_toggle
7-
8-
9-
async def test_script_mount_unmount(display: DisplayFixture):
10-
toggle_is_mounted = Ref()
11-
12-
@component
13-
def Root():
14-
is_mounted, toggle_is_mounted.current = use_toggle(True)
15-
return html.div(
16-
html.div({"id": "mount-state", "data_value": False}),
17-
HasScript() if is_mounted else html.div(),
18-
)
19-
20-
@component
21-
def HasScript():
22-
return html.script(
23-
"""() => {
24-
const mapping = {"false": false, "true": true};
25-
const mountStateEl = document.getElementById("mount-state");
26-
mountStateEl.setAttribute(
27-
"data-value", !mapping[mountStateEl.getAttribute("data-value")]);
28-
return () => mountStateEl.setAttribute(
29-
"data-value", !mapping[mountStateEl.getAttribute("data-value")]);
30-
}"""
31-
)
32-
33-
await display.show(Root)
34-
35-
mount_state = await display.page.wait_for_selector("#mount-state", state="attached")
36-
poll_mount_state = poll(mount_state.get_attribute, "data-value")
37-
38-
await poll_mount_state.until_equals("true")
39-
40-
toggle_is_mounted.current()
41-
42-
await poll_mount_state.until_equals("false")
43-
44-
toggle_is_mounted.current()
45-
46-
await poll_mount_state.until_equals("true")
6+
from tests.tooling.hooks import use_counter
477

488

499
async def test_script_re_run_on_content_change(display: DisplayFixture):
@@ -54,14 +14,9 @@ def HasScript():
5414
count, incr_count.current = use_counter(1)
5515
return html.div(
5616
html.div({"id": "mount-count", "data_value": 0}),
57-
html.div({"id": "unmount-count", "data_value": 0}),
5817
html.script(
59-
f"""() => {{
60-
const mountCountEl = document.getElementById("mount-count");
61-
const unmountCountEl = document.getElementById("unmount-count");
62-
mountCountEl.setAttribute("data-value", {count});
63-
return () => unmountCountEl.setAttribute("data-value", {count});;
64-
}}"""
18+
'const mountCountEl = document.getElementById("mount-count");'
19+
f'mountCountEl.setAttribute("data-value", {count});'
6520
),
6621
)
6722

@@ -70,23 +25,11 @@ def HasScript():
7025
mount_count = await display.page.wait_for_selector("#mount-count", state="attached")
7126
poll_mount_count = poll(mount_count.get_attribute, "data-value")
7227

73-
unmount_count = await display.page.wait_for_selector(
74-
"#unmount-count", state="attached"
75-
)
76-
poll_unmount_count = poll(unmount_count.get_attribute, "data-value")
77-
7828
await poll_mount_count.until_equals("1")
79-
await poll_unmount_count.until_equals("0")
80-
8129
incr_count.current()
82-
8330
await poll_mount_count.until_equals("2")
84-
await poll_unmount_count.until_equals("1")
85-
8631
incr_count.current()
87-
8832
await poll_mount_count.until_equals("3")
89-
await poll_unmount_count.until_equals("2")
9033

9134

9235
async def test_script_from_src(display: DisplayFixture):

0 commit comments

Comments
 (0)