summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/python/python3-urllib3/CVE-2025-50182.patch
blob: 2f6ba478d57bdd98b04a7984cfc890b0b75a3052 (plain)
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
122
123
124
125
From 7eb4a2aafe49a279c29b6d1f0ed0f42e9736194f Mon Sep 17 00:00:00 2001
From: Illia Volochii <illia.volochii@gmail.com>
Date: Wed, 18 Jun 2025 16:30:35 +0300
Subject: [PATCH] Merge commit from fork

CVE: CVE-2025-50182
Upstream-Status: Backport [https://github.com/urllib3/urllib3/commit/7eb4a2aafe49a279c29b6d1f0ed0f42e9736194f]

Signed-off-by: Yogita Urade <yogita.urade@windriver.com>
---
 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:
--
2.40.0