Skip to content

Commit 79149e8

Browse files
authored
[py][bidi]: support accept_insecure_certs and proxy parameters in create_user_context (#15983)
1 parent 9b49091 commit 79149e8

File tree

3 files changed

+194
-2
lines changed

3 files changed

+194
-2
lines changed

py/selenium/webdriver/common/bidi/browser.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
# under the License.
1717

1818

19+
from typing import Optional
20+
1921
from selenium.webdriver.common.bidi.common import command_builder
22+
from selenium.webdriver.common.proxy import Proxy
2023

2124

2225
class ClientWindowState:
@@ -182,14 +185,27 @@ class Browser:
182185
def __init__(self, conn):
183186
self.conn = conn
184187

185-
def create_user_context(self) -> str:
188+
def create_user_context(self, accept_insecure_certs: Optional[bool] = None, proxy: Optional[Proxy] = None) -> str:
186189
"""Creates a new user context.
187190
191+
Parameters:
192+
-----------
193+
accept_insecure_certs: Optional flag to accept insecure TLS certificates
194+
proxy: Optional proxy configuration for the user context
195+
188196
Returns:
189197
-------
190198
str: The ID of the created user context.
191199
"""
192-
result = self.conn.execute(command_builder("browser.createUserContext", {}))
200+
params = {}
201+
202+
if accept_insecure_certs is not None:
203+
params["acceptInsecureCerts"] = accept_insecure_certs
204+
205+
if proxy is not None:
206+
params["proxy"] = proxy.to_bidi_dict()
207+
208+
result = self.conn.execute(command_builder("browser.createUserContext", params))
193209
return result["userContext"]
194210

195211
def get_user_contexts(self) -> list[str]:

py/selenium/webdriver/common/proxy.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,39 @@ def to_capabilities(self):
325325
if attr_value:
326326
proxy_caps[proxy] = attr_value
327327
return proxy_caps
328+
329+
def to_bidi_dict(self):
330+
"""Convert proxy settings to BiDi format.
331+
332+
Returns:
333+
-------
334+
dict: Proxy configuration in BiDi format.
335+
"""
336+
proxy_type = self.proxyType["string"].lower()
337+
result = {"proxyType": proxy_type}
338+
339+
if proxy_type == "manual":
340+
if self.httpProxy:
341+
result["httpProxy"] = self.httpProxy
342+
if self.sslProxy:
343+
result["sslProxy"] = self.sslProxy
344+
if self.socksProxy:
345+
result["socksProxy"] = self.socksProxy
346+
if self.socksVersion is not None:
347+
result["socksVersion"] = self.socksVersion
348+
if self.noProxy:
349+
# Convert comma-separated string to list
350+
if isinstance(self.noProxy, str):
351+
result["noProxy"] = [host.strip() for host in self.noProxy.split(",") if host.strip()]
352+
elif isinstance(self.noProxy, list):
353+
if not all(isinstance(h, str) for h in self.noProxy):
354+
raise TypeError("no_proxy list must contain only strings")
355+
result["noProxy"] = self.noProxy
356+
else:
357+
raise TypeError("no_proxy must be a comma-separated string or a list of strings")
358+
359+
elif proxy_type == "pac":
360+
if self.proxyAutoconfigUrl:
361+
result["proxyAutoconfigUrl"] = self.proxyAutoconfigUrl
362+
363+
return result

py/test/selenium/webdriver/common/bidi_browser_tests.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,32 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18+
import http.server
19+
import socketserver
20+
import threading
21+
1822
import pytest
1923

2024
from selenium.webdriver.common.bidi.browser import ClientWindowInfo, ClientWindowState
25+
from selenium.webdriver.common.by import By
26+
from selenium.webdriver.common.proxy import Proxy, ProxyType
27+
from selenium.webdriver.common.utils import free_port
28+
from selenium.webdriver.common.window import WindowTypes
29+
30+
31+
class FakeProxyHandler(http.server.SimpleHTTPRequestHandler):
32+
def do_GET(self):
33+
print(f"[Fake Proxy] Intercepted request to: {self.path}")
34+
self.send_response(200)
35+
self.end_headers()
36+
self.wfile.write(b"proxied response")
37+
38+
39+
def start_fake_proxy(port):
40+
server = socketserver.TCPServer(("localhost", port), FakeProxyHandler)
41+
thread = threading.Thread(target=server.serve_forever, daemon=True)
42+
thread.start()
43+
return server
2144

2245

2346
def test_browser_initialized(driver):
@@ -95,3 +118,120 @@ def test_client_window_state_constants(driver):
95118
assert ClientWindowState.MAXIMIZED == "maximized"
96119
assert ClientWindowState.MINIMIZED == "minimized"
97120
assert ClientWindowState.NORMAL == "normal"
121+
122+
123+
def test_create_user_context_with_accept_insecure_certs(driver):
124+
"""Test creating a user context with accept_insecure_certs parameter."""
125+
INSECURE_TEST_SITE = "https://self-signed.badssl.com/"
126+
user_context = driver.browser.create_user_context(accept_insecure_certs=True)
127+
128+
bc = driver.browsing_context.create(type=WindowTypes.WINDOW, user_context=user_context)
129+
driver.switch_to.window(bc)
130+
assert user_context is not None
131+
assert bc is not None
132+
133+
driver.get(INSECURE_TEST_SITE)
134+
135+
h1 = driver.find_element(By.TAG_NAME, "h1")
136+
assert h1.text.strip() == "self-signed.\nbadssl.com"
137+
138+
# Clean up
139+
driver.browser.remove_user_context(user_context)
140+
141+
142+
def test_create_user_context_with_direct_proxy(driver):
143+
"""Test creating a user context with direct proxy configuration."""
144+
proxy = Proxy()
145+
proxy.proxy_type = ProxyType.DIRECT
146+
147+
user_context = driver.browser.create_user_context(proxy=proxy)
148+
assert user_context is not None
149+
150+
bc = driver.browsing_context.create(type=WindowTypes.WINDOW, user_context=user_context)
151+
driver.switch_to.window(bc)
152+
153+
# Visiting a site should load directly without proxy
154+
driver.get("http://example.com/")
155+
body_text = driver.find_element(By.TAG_NAME, "body").text.lower()
156+
assert "example domain" in body_text
157+
158+
# Clean up
159+
driver.browser.remove_user_context(user_context)
160+
161+
162+
@pytest.mark.xfail_chrome(reason="Chrome auto upgrades HTTP to HTTPS in untrusted networks like CI environments")
163+
@pytest.mark.xfail_firefox(reason="Firefox proxy settings are different")
164+
@pytest.mark.xfail_remote
165+
def test_create_user_context_with_manual_proxy_all_params(driver):
166+
"""Test creating a user context with manual proxy configuration."""
167+
# Start a fake proxy server
168+
port = free_port()
169+
fake_proxy_server = start_fake_proxy(port=port)
170+
171+
proxy = Proxy()
172+
proxy.proxy_type = ProxyType.MANUAL
173+
proxy.http_proxy = f"localhost:{port}"
174+
proxy.ssl_proxy = f"localhost:{port}"
175+
proxy.socks_proxy = f"localhost:{port}"
176+
proxy.socks_version = 5
177+
proxy.no_proxy = ["the-internet.herokuapp.com"]
178+
179+
user_context = driver.browser.create_user_context(proxy=proxy)
180+
181+
# Create and switch to a new browsing context using this proxy
182+
bc = driver.browsing_context.create(type=WindowTypes.WINDOW, user_context=user_context)
183+
driver.switch_to.window(bc)
184+
185+
try:
186+
# Visit no proxy site, it should bypass proxy
187+
driver.get("http://the-internet.herokuapp.com/")
188+
body_text = driver.find_element(By.TAG_NAME, "body").text.lower()
189+
assert "welcome to the-internet" in body_text
190+
191+
# Visit a site that should be proxied
192+
driver.get("http://example.com/")
193+
194+
body_text = driver.find_element("tag name", "body").text
195+
assert "proxied response" in body_text.lower()
196+
197+
finally:
198+
driver.browser.remove_user_context(user_context)
199+
fake_proxy_server.shutdown()
200+
fake_proxy_server.server_close()
201+
202+
203+
@pytest.mark.xfail_chrome(reason="Chrome auto upgrades HTTP to HTTPS in untrusted networks like CI environments")
204+
@pytest.mark.xfail_firefox(reason="Firefox proxy settings are different")
205+
@pytest.mark.xfail_remote
206+
def test_create_user_context_with_proxy_and_accept_insecure_certs(driver):
207+
"""Test creating a user context with both acceptInsecureCerts and proxy parameters."""
208+
# Start fake proxy server
209+
port = free_port()
210+
fake_proxy_server = start_fake_proxy(port=port)
211+
212+
proxy = Proxy()
213+
proxy.proxy_type = ProxyType.MANUAL
214+
proxy.http_proxy = f"localhost:{port}"
215+
proxy.ssl_proxy = f"localhost:{port}"
216+
proxy.no_proxy = ["self-signed.badssl.com"]
217+
218+
user_context = driver.browser.create_user_context(accept_insecure_certs=True, proxy=proxy)
219+
220+
bc = driver.browsing_context.create(type=WindowTypes.WINDOW, user_context=user_context)
221+
driver.switch_to.window(bc)
222+
223+
try:
224+
# Visit a site with an invalid certificate
225+
driver.get("https://self-signed.badssl.com/")
226+
h1 = driver.find_element(By.TAG_NAME, "h1")
227+
assert "badssl.com" in h1.text.lower()
228+
229+
# Visit a site that should go through the fake proxy
230+
driver.get("http://example.com/")
231+
body_text = driver.find_element(By.TAG_NAME, "body").text
232+
assert "proxied response" in body_text.lower()
233+
234+
finally:
235+
driver.browser.remove_user_context(user_context)
236+
fake_proxy_server.shutdown()
237+
fake_proxy_server.server_close()

0 commit comments

Comments
 (0)