From 6b8b572b84c9e3eaf39892bc6f4d27d1dd95ffd7 Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Mon, 24 Aug 2026 15:21:31 -0500 Subject: [PATCH 1/2] [rb] reject an inbound BiDi scalar outside its union's declared arms --- .../selenium/webdriver/bidi/serialization/union.rb | 14 ++++++++++---- .../selenium/webdriver/bidi/serialization_spec.rb | 9 ++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/rb/lib/selenium/webdriver/bidi/serialization/union.rb b/rb/lib/selenium/webdriver/bidi/serialization/union.rb index 7eb19754f9682..0c09c22354211 100644 --- a/rb/lib/selenium/webdriver/bidi/serialization/union.rb +++ b/rb/lib/selenium/webdriver/bidi/serialization/union.rb @@ -54,13 +54,19 @@ def object_only = @object_only = true def scalar_values(*values) = @scalar_values = values # A non-Hash payload is a bare scalar arm (e.g. input.Origin's "viewport") with no - # object to dispatch on, so it is returned unchanged — unless every arm is an object - # (object_only), where a non-Hash cannot match any variant and is a wire error. + # object to dispatch on, so it stands for itself — but only as a literal the schema + # pins, since anything else matches no arm. Where every arm is an object + # (object_only), a non-Hash cannot match any variant at all. def from_json(json_payload) unless json_payload.is_a?(::Hash) - return json_payload unless @object_only + if @object_only + raise Error::SerializationError, + "#{name} expected an object on the wire, got #{json_payload.inspect}" + end + return json_payload if scalar_arm?(json_payload) - raise Error::SerializationError, "#{name} expected an object on the wire, got #{json_payload.inspect}" + raise Error::SerializationError, + "#{name} received a scalar not in this Selenium's BiDi schema: #{json_payload.inspect}" end variant = select(json_payload) diff --git a/rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb b/rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb index b3425197c6298..c4272334e79e2 100644 --- a/rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb +++ b/rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb @@ -87,10 +87,17 @@ def valid_cookie_attrs .to raise_error(Error::SerializationError, /RemoteValue expected an object/) end - it 'passes a bare scalar through a union that has a scalar arm (input.Origin)' do + it 'passes a declared bare scalar through a union that has a scalar arm (input.Origin)' do expect(Input::Origin.from_json('viewport')).to eq('viewport') end + # The inbound mirror of the outbound arm check: a scalar the schema does not pin + # matches no arm, so it cannot yield a valid value and is a wire error. + it 'raises when a bare scalar is not one of the union scalar arms' do + expect { Input::Origin.from_json('banana') } + .to raise_error(Error::SerializationError, /Origin received a scalar not in this Selenium/) + end + it 'keeps a map string key while typing its object value (object-only value union)' do parsed = Script::ObjectRemoteValue.from_json( 'type' => 'object', 'value' => [['k', {'type' => 'number', 'value' => 2}]] From de4cf0ca3865903e6d2aa8c0bb0769f2d9e43093 Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Tue, 25 Aug 2026 13:00:42 -0500 Subject: [PATCH 2/2] [rb] trim comments to essentials --- rb/lib/selenium/webdriver/bidi/serialization/union.rb | 6 ++---- rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/rb/lib/selenium/webdriver/bidi/serialization/union.rb b/rb/lib/selenium/webdriver/bidi/serialization/union.rb index 0c09c22354211..cd4821105f4c8 100644 --- a/rb/lib/selenium/webdriver/bidi/serialization/union.rb +++ b/rb/lib/selenium/webdriver/bidi/serialization/union.rb @@ -53,10 +53,8 @@ def object_only = @object_only = true # An outbound scalar outside that set matches no arm, so it is a caller error. def scalar_values(*values) = @scalar_values = values - # A non-Hash payload is a bare scalar arm (e.g. input.Origin's "viewport") with no - # object to dispatch on, so it stands for itself — but only as a literal the schema - # pins, since anything else matches no arm. Where every arm is an object - # (object_only), a non-Hash cannot match any variant at all. + # A non-Hash payload is a bare scalar arm (e.g. input.Origin's "viewport"), valid only + # as a literal the schema pins; under object_only it cannot match any variant at all. def from_json(json_payload) unless json_payload.is_a?(::Hash) if @object_only diff --git a/rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb b/rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb index c4272334e79e2..73eeb893aa762 100644 --- a/rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb +++ b/rb/spec/unit/selenium/webdriver/bidi/serialization_spec.rb @@ -91,8 +91,6 @@ def valid_cookie_attrs expect(Input::Origin.from_json('viewport')).to eq('viewport') end - # The inbound mirror of the outbound arm check: a scalar the schema does not pin - # matches no arm, so it cannot yield a valid value and is a wire error. it 'raises when a bare scalar is not one of the union scalar arms' do expect { Input::Origin.from_json('banana') } .to raise_error(Error::SerializationError, /Origin received a scalar not in this Selenium/)