File: CVE-2025-50182.patch

package info (click to toggle)
python-urllib3 2.3.0-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 2,376 kB
  • sloc: python: 25,957; makefile: 122; javascript: 92; sh: 11
file content (121 lines) | stat: -rw-r--r-- 4,482 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
From: Illia Volochii <illia.volochii@gmail.com>
Date: Wed, 18 Jun 2025 16:30:35 +0300
Subject: Merge commit from fork

Origin: upstream, https://github.com/urllib3/urllib3/commit/7eb4a2aafe49a279c29b6d1f0ed0f42e9736194f
Bug-Debian: https://bugs.debian.org/1108077
Last-Update: 2025-07-13
---
 docs/reference/contrib/emscripten.rst      |  2 +-
 src/urllib3/contrib/emscripten/fetch.py    | 20 +++++++++++++
 test/contrib/emscripten/test_emscripten.py | 46 ++++++++++++++++++++++++++++++
 3 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/docs/reference/contrib/emscripten.rst b/docs/reference/contrib/emscripten.rst
index a8f1cda..4670757 100644
--- a/docs/reference/contrib/emscripten.rst
+++ b/docs/reference/contrib/emscripten.rst
@@ -65,7 +65,7 @@ Features which are usable with Emscripten support are:
 * Timeouts
 * Retries
 * Streaming (with Web Workers and Cross-Origin Isolation)
-* Redirects (determined by browser/runtime, not restrictable with urllib3)
+* Redirects (urllib3 controls redirects in Node.js but not in browsers where behavior is determined by runtime)
 * Decompressing response bodies
 
 Features which don't work with Emscripten:
diff --git a/src/urllib3/contrib/emscripten/fetch.py b/src/urllib3/contrib/emscripten/fetch.py
index a514306..6695821 100644
--- a/src/urllib3/contrib/emscripten/fetch.py
+++ b/src/urllib3/contrib/emscripten/fetch.py
@@ -573,6 +573,11 @@ def send_jspi_request(
         "method": request.method,
         "signal": js_abort_controller.signal,
     }
+    # Node.js returns the whole response (unlike opaqueredirect in browsers),
+    # so urllib3 can set `redirect: manual` to control redirects itself.
+    # https://stackoverflow.com/a/78524615
+    if _is_node_js():
+        fetch_data["redirect"] = "manual"
     # Call JavaScript fetch (async api, returns a promise)
     fetcher_promise_js = js.fetch(request.url, _obj_from_dict(fetch_data))
     # Now suspend WebAssembly until we resolve that promise
@@ -693,6 +698,21 @@ def has_jspi() -> bool:
         return False
 
 
+def _is_node_js() -> bool:
+    """
+    Check if we are in Node.js.
+
+    :return: True if we are in Node.js.
+    :rtype: bool
+    """
+    return (
+        hasattr(js, "process")
+        and hasattr(js.process, "release")
+        # According to the Node.js documentation, the release name is always "node".
+        and js.process.release.name == "node"
+    )
+
+
 def streaming_ready() -> bool | None:
     if _fetcher:
         return _fetcher.streaming_ready
diff --git a/test/contrib/emscripten/test_emscripten.py b/test/contrib/emscripten/test_emscripten.py
index 5eaa674..fbf89fc 100644
--- a/test/contrib/emscripten/test_emscripten.py
+++ b/test/contrib/emscripten/test_emscripten.py
@@ -960,6 +960,52 @@ def test_redirects(
     )
 
 
+@pytest.mark.with_jspi
+def test_disabled_redirects(
+    selenium_coverage: typing.Any, testserver_http: PyodideServerInfo
+) -> None:
+    """
+    Test that urllib3 can control redirects in Node.js.
+    """
+
+    @run_in_pyodide  # type: ignore[misc]
+    def pyodide_test(selenium_coverage: typing.Any, host: str, port: int) -> None:
+        import pytest
+
+        from urllib3 import PoolManager, request
+        from urllib3.contrib.emscripten.fetch import _is_node_js
+        from urllib3.exceptions import MaxRetryError
+
+        if not _is_node_js():
+            pytest.skip("urllib3 does not control redirects in browsers.")
+
+        redirect_url = f"http://{host}:{port}/redirect"
+
+        with PoolManager(retries=0) as http:
+            with pytest.raises(MaxRetryError):
+                http.request("GET", redirect_url)
+
+            response = http.request("GET", redirect_url, redirect=False)
+            assert response.status == 303
+
+        with PoolManager(retries=False) as http:
+            response = http.request("GET", redirect_url)
+            assert response.status == 303
+
+        with pytest.raises(MaxRetryError):
+            request("GET", redirect_url, retries=0)
+
+        response = request("GET", redirect_url, redirect=False)
+        assert response.status == 303
+
+        response = request("GET", redirect_url, retries=0, redirect=False)
+        assert response.status == 303
+
+    pyodide_test(
+        selenium_coverage, testserver_http.http_host, testserver_http.http_port
+    )
+
+
 def test_insecure_requests_warning(
     selenium_coverage: typing.Any, testserver_http: PyodideServerInfo
 ) -> None: