Skip to content

[dotnet][py][rb] prevent CDP access with Firefox - #17849

Merged
titusfortner merged 3 commits into
SeleniumHQ:trunkfrom
titusfortner:cdp-deprecations
Jul 31, 2026
Merged

[dotnet][py][rb] prevent CDP access with Firefox#17849
titusfortner merged 3 commits into
SeleniumHQ:trunkfrom
titusfortner:cdp-deprecations

Conversation

@titusfortner

Copy link
Copy Markdown
Member

🔗 Related Issues

Follow on from #11736

💥 What does this PR do?

  • All bindings either prevent using CDP with Firefox structurally or with a clear error message
  • .NET — replaces the warning with a thrown error in GetDevToolsSession(), and adds the same error to the previously-unguarded GetDevToolsSession(DevToolsOptions) overload
  • Python — raises new errors in two CDP entry points, execute_cdp_cmd and bidi_connection (start_devtools already raised)
  • Ruby — removed warnings and removed access to the CDP-backed methods from Firefox sessions

🔧 Implementation Notes

  • Ruby removed access entirely by deleting HasLogEvents / HasNetworkInterception from Firefox::Driver::EXTENSIONS
  • Python / .NET can't prevent access structurally in a backwards compatible way and still support CDP for Chrome on RemoteWebDriver, and uses a runtime guard instead.

🤖 AI assistance

  • AI assisted (complete below)
    • Tool(s): Claude Code
    • What was generated: the Firefox guards / extension removal across the three bindings
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

  • Java already removed Firefox implementation of HasDevTools interface as well as removing it from being augmented to RemoteWebDriver for Firefox sessions
  • JavaScript already throws for Firefox CDP; unchanged.

🔄 Types of changes

  • Breaking change (Firefox CDP calls now error, but the feature has been non-functional for a while)

@selenium-ci selenium-ci added C-py Python Bindings C-rb Ruby Bindings C-dotnet .NET Bindings labels Jul 30, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Disallow CDP usage on Firefox across .NET, Python, and Ruby bindings

✨ Enhancement 🐞 Bug fix 🕐 20-40 Minutes

Grey Divider

AI Description

• Throw/raise a clear error when CDP APIs are invoked on Firefox sessions.
• Add missing runtime guards for previously unguarded CDP entry points.
• Remove Firefox driver extensions in Ruby so CDP-backed helpers are unavailable.
Diagram

graph TD
  A["Client code"] --> B["CDP entry point"] --> C{"Firefox session?"}
  C -- "Yes" --> D["Error: CDP removed"] --> F["Use WebDriver BiDi"]
  C -- "No" --> E["Proceed with CDP (Chromium)"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Guard on CDP capability presence (e.g., se:cdp) instead of browserName
  • ➕ Would allow CDP for any future Firefox implementation that genuinely exposes a CDP endpoint
  • ➕ Avoids assuming browserName fully determines protocol availability
  • ➖ Conflicts with the explicit product decision to remove Firefox CDP support
  • ➖ May produce confusing partial support if capability is absent/present inconsistently across vendors
2. Compile-time/API-level removal in .NET/Python (structural prevention)
  • ➕ Makes unsupported calls impossible to compile/call, eliminating runtime surprises
  • ➕ Clearer API surface (Firefox sessions simply don't present CDP methods)
  • ➖ Requires breaking public API changes and/or larger refactors (especially for RemoteWebDriver where CDP remains valid for Chromium)
  • ➖ Hard to do while preserving backward compatibility across browsers and remote use cases
3. Keep deprecation warnings instead of throwing/raising
  • ➕ Minimizes breaking behavior changes for callers
  • ➕ Provides a softer migration path
  • ➖ Allows continued usage of a non-functional/removed feature, leading to late failures and support burden
  • ➖ Users may miss warnings in CI logs; errors are more actionable

Recommendation: The PR’s approach (hard error at CDP entry points, plus structural removal in Ruby where feasible) is the most practical: it aligns behavior across bindings, fails fast with a clear migration message, and avoids larger breaking API refactors in .NET/Python while preserving CDP for Chromium/RemoteWebDriver.

Files changed (5) +10 / -23

Enhancement (2) +4 / -2
webdriver.pyRaise RuntimeError when Firefox uses execute_cdp_cmd or bidi_connection +4/-0

Raise RuntimeError when Firefox uses execute_cdp_cmd or bidi_connection

• Adds explicit Firefox runtime guards to execute_cdp_cmd and the bidi_connection async context manager, raising a clear error directing users to WebDriver BiDi. This prevents entering CDP-backed paths that are no longer supported for Firefox sessions.

py/selenium/webdriver/remote/webdriver.py

driver.rbDrop CDP-backed extensions from Firefox Driver EXTENSIONS list +0/-2

Drop CDP-backed extensions from Firefox Driver EXTENSIONS list

• Removes HasLogEvents and HasNetworkInterception from Firefox::Driver::EXTENSIONS so those CDP-dependent helpers are not mixed into Firefox driver instances. This makes unsupported CDP-backed methods unavailable by construction.

rb/lib/selenium/webdriver/firefox/driver.rb

Bug fix (1) +6 / -7
RemoteWebDriver.csThrow on Firefox CDP access for both GetDevToolsSession overloads +6/-7

Throw on Firefox CDP access for both GetDevToolsSession overloads

• Replaces the prior Firefox CDP deprecation warning with a thrown WebDriverException in GetDevToolsSession(). Adds the same Firefox guard to the GetDevToolsSession(DevToolsOptions) overload and removes the now-unused internal logging dependency/field.

dotnet/src/webdriver/Remote/RemoteWebDriver.cs

Refactor (2) +0 / -14
has_log_events.rbRemove Firefox-specific deprecation warning from on_log_event +0/-7

Remove Firefox-specific deprecation warning from on_log_event

• Deletes the Firefox-only deprecation logging branch in on_log_event, leaving the method’s behavior unchanged for supported (Chromium) drivers. This pairs with removing the extension from Firefox entirely so the method won’t be available there.

rb/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb

has_network_interception.rbRemove Firefox-specific deprecation warning from intercept +0/-7

Remove Firefox-specific deprecation warning from intercept

• Deletes the Firefox-only deprecation logging branch in intercept while keeping the underlying DevTools-based interceptor logic intact. With the extension removed from Firefox, intercept is no longer exposed on Firefox sessions.

rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Case-sensitive Firefox guard 🐞 Bug ≡ Correctness
Description
RemoteWebDriver.GetDevToolsSession(DevToolsOptions) blocks Firefox only when browserName is
exactly "firefox" (case-sensitive), so non-normalized values like "Firefox" from nonstandard
endpoints could skip the new removal guard and proceed into CDP session creation. This undermines
the intent of structurally preventing Firefox CDP access in edge-case environments (e.g., custom
grids/mocks).
Code

dotnet/src/webdriver/Remote/RemoteWebDriver.cs[R442-445]

+        if (this.Capabilities.GetCapability(CapabilityType.BrowserName) is "firefox")
+        {
+            throw new WebDriverException("CDP support for Firefox has been removed. Please switch to WebDriver BiDi.");
+        }
Evidence
The new overload guard uses a case-sensitive pattern match against a lowercase literal. Elsewhere in
the repo (Python), browserName is explicitly normalized via .lower(), suggesting defensive
handling of casing differences is expected.

dotnet/src/webdriver/Remote/RemoteWebDriver.cs[422-446]
py/selenium/webdriver/remote/webdriver.py[193-201]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The Firefox CDP removal guard compares `browserName` to the lowercase literal `"firefox"` using a case-sensitive match. If a remote endpoint returns a differently-cased browser name (e.g., `"Firefox"`), the guard won't trigger and the code will attempt to create a CDP DevTools session.

### Issue Context
Other parts of the repo defensively normalize `browserName` with `.lower()` (Python), which indicates casing may not always be consistent across environments.

### Fix Focus Areas
- dotnet/src/webdriver/Remote/RemoteWebDriver.cs[422-446]

### Suggested change
- Read `browserName` as a string (`?.ToString()`), then compare with `StringComparison.OrdinalIgnoreCase`.
- Apply the same normalization in both `GetDevToolsSession()` overloads for consistency.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unsafe browserName indexing 🐞 Bug ☼ Reliability
Description
Python execute_cdp_cmd now unconditionally evaluates self.caps["browserName"].lower(), which can
raise TypeError/KeyError/AttributeError if self.caps is None, missing browserName, or
non-string. The same new pattern in bidi_connection can fail before raising the intended “CDP
removed” message, producing confusing errors in malformed/nonstandard capability responses and test
doubles.
Code

py/selenium/webdriver/remote/webdriver.py[R463-465]

+        if self.caps["browserName"].lower() == "firefox":
+            raise RuntimeError("CDP support for Firefox has been removed. Please switch to WebDriver BiDi.")
        return self.execute("executeCdpCommand", {"cmd": cmd, "params": cmd_args})["value"]
Evidence
execute_cdp_cmd and bidi_connection now index self.caps["browserName"] directly, while
start_session can set self.caps from a .get() (potentially None). The name property
demonstrates the preferred defensive pattern for missing browserName.

py/selenium/webdriver/remote/webdriver.py[445-466]
py/selenium/webdriver/remote/webdriver.py[1184-1190]
py/selenium/webdriver/remote/webdriver.py[388-402]
py/selenium/webdriver/remote/webdriver.py[362-372]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
New Firefox CDP guards directly index `self.caps["browserName"]` and call `.lower()`. If `self.caps` is `None` (or missing `browserName`), this raises a low-level exception unrelated to CDP removal.

### Issue Context
`start_session()` assigns `self.caps = response.get("capabilities")`, which can be `None` for malformed/nonstandard responses. The class already contains a `name` property that checks for `browserName` and raises a descriptive error.

### Fix Focus Areas
- py/selenium/webdriver/remote/webdriver.py[445-466]
- py/selenium/webdriver/remote/webdriver.py[1184-1188]

### Suggested change
- Use a null-safe lookup and type-check, e.g.:
 - `browser = (self.caps or {}).get("browserName", "")`
 - `if isinstance(browser, str) and browser.lower() == "firefox": ...`
- Alternatively, reuse the existing `self.name` property (but ensure the resulting error is still clear for CDP removal vs missing capabilities).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread dotnet/src/webdriver/Remote/RemoteWebDriver.cs
Comment thread py/selenium/webdriver/remote/webdriver.py
@titusfortner
titusfortner merged commit 144db0c into SeleniumHQ:trunk Jul 31, 2026
43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-dotnet .NET Bindings C-py Python Bindings C-rb Ruby Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants