Skip to content

[py]: Apply ClientConfig user_agent and extra_headers per connection - #17944

Open
navin772 wants to merge 2 commits into
SeleniumHQ:trunkfrom
navin772:user-agent-headers-leak
Open

[py]: Apply ClientConfig user_agent and extra_headers per connection#17944
navin772 wants to merge 2 commits into
SeleniumHQ:trunkfrom
navin772:user-agent-headers-leak

Conversation

@navin772

Copy link
Copy Markdown
Member

🔗 Related Issues

💥 What does this PR do?

RemoteConnection.__init__ copied the connection's user_agent and extra_headers onto the class, not the instance:

RemoteConnection.extra_headers = self._client_config.extra_headers or RemoteConnection.extra_headers
RemoteConnection.user_agent = self._client_config.user_agent or RemoteConnection.user_agent

Every RemoteConnection in the process then shared them, and they outlived driver.quit(). A driver pointed at an authenticated Grid followed by a local driver meant the local driver sent the Grid's Authorization header:

a = webdriver.Remote(client_config=ClientConfig(url_a, extra_headers={"Authorization": "Bearer SECRET"}))
b = webdriver.Remote(client_config=ClientConfig(url_b))  # also sends Bearer SECRET

Now they're applied per-instance in _request() from self._client_config, so one connection's headers can't reach another. Class-level RemoteConnection.extra_headers / user_agent still work as process-wide defaults.

🔧 Implementation Notes

🤖 AI assistance

  • AI assisted (complete below)
    • Tool(s): Claude code
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

🔄 Types of changes

  • Bug fix (backwards compatible)

@selenium-ci selenium-ci added the C-py Python Bindings label Aug 25, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

[py] Prevent RemoteConnection user-agent/header leakage across instances

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Stop mirroring per-connection headers onto RemoteConnection class attributes.
• Apply ClientConfig user_agent and extra_headers only within each request.
• Add unit tests asserting headers never leak between separate RemoteConnection instances.
Diagram

graph TD
  A((Caller code)) --> B[/"ClientConfig"/] --> C["RemoteConnection instance"] --> D["_request()"] --> E["urllib3 PoolManager"] --> F{{"Remote Server"}}

  subgraph Legend
    direction LR
    _a((Caller)) ~~~ _b[/Config/] ~~~ _c[Component] ~~~ _d{{External}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Cache computed headers on the instance in __init__
  • ➕ Avoids recomputing/merging headers on every request
  • ➕ Makes the per-instance nature explicit (self._headers)
  • ➖ Must keep cache in sync with any runtime mutations (e.g., changing auth/proxy/keep-alive)
  • ➖ More state to maintain and reason about than reading from ClientConfig at request time
2. Extend get_remote_connection_headers() to accept per-instance overrides
  • ➕ Keeps all header construction in one method
  • ➕ Makes override points explicit via parameters rather than post-merge logic
  • ➖ Requires changing a widely-used classmethod signature or adding a parallel API
  • ➖ Still needs careful ordering rules (defaults vs overrides vs auth) to avoid regressions

Recommendation: The chosen approach (apply ClientConfig.user_agent/extra_headers inside _request() per instance) is the best balance of safety and compatibility: it eliminates cross-instance leakage without changing the public header-building API, while preserving RemoteConnection class attributes as process-wide defaults.

Files changed (2) +78 / -29

Bug fix (1) +9 / -2
remote_connection.pyApply ClientConfig user_agent/extra_headers per request (no class-level mirroring) +9/-2

Apply ClientConfig user_agent/extra_headers per request (no class-level mirroring)

• Removes copying ClientConfig.user_agent and extra_headers onto RemoteConnection class attributes during initialization. Instead, _request() now overlays per-instance user_agent and extra_headers onto the computed request headers, preventing cross-connection leakage while keeping class attributes as defaults.

py/selenium/webdriver/remote/remote_connection.py

Tests (1) +69 / -27
remote_connection_tests.pyAssert per-connection headers reach requests and never leak between connections +69/-27

Assert per-connection headers reach requests and never leak between connections

• Refactors tests to capture actual headers passed to the underlying connection request rather than mocking header construction. Adds a regression test proving that a connection with authenticated/extra headers does not contaminate a subsequently created plain RemoteConnection instance.

py/test/unit/selenium/webdriver/remote/remote_connection_tests.py

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

No code changes since the last review — review skipped

Qodo Logo

@navin772
navin772 requested a review from cgoldberg August 25, 2026 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-py Python Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants