From ab67bdd793a19318c6223c25eb8f85753c93f3f9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 31 Aug 2026 23:49:49 +0900 Subject: [PATCH 01/67] test(core): require admitted node authority for pointer click --- ...river_bidi_pointer_click_node_authority.rs | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs new file mode 100644 index 000000000..c77615a9e --- /dev/null +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -0,0 +1,189 @@ +use std::error::Error; + +use originweave_core::{ + BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, + BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, + BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, + BrowserRegistryError, BrowserSessionId, BrowsingContextId, NodeHandleError, ObservedNodeHandle, + Origin, OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, + WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, + WebDriverBiDiPointerClickCommand, WebDriverBiDiPointerClickCommandError, + WebDriverBiDiRemoteNodeReference, +}; + +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +struct AdmittedNodeFixture { + registry: BrowserAuthorityRegistry, + browser_session: BrowserSessionId, + browsing_context: BrowsingContextId, + handle: ObservedNodeHandle, + remote: WebDriverBiDiRemoteNodeReference, +} + +fn semantic_observation_proof() -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::SemanticObservation], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::SemanticObservation, + )?) +} + +fn admitted_node() -> Result> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session("webdriver-session")?; + let browsing_context = registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://app.example")?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let command = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handles = command.bind_response_document_nodes( + document, + semantic_observation_proof()?, + &mut registry, + target, + )?; + let handle = handles + .into_iter() + .next() + .ok_or("locateNodes fixture did not bind its node")?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + Ok(AdmittedNodeFixture { + registry, + browser_session, + browsing_context, + handle, + remote, + }) +} + +#[test] +fn pointer_click_serialization_requires_the_exact_current_admitted_wire_node() +-> Result<(), Box> { + let fixture = admitted_node()?; + let command = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fixture.handle, + &fixture.remote, + &fixture.registry, + )?; + + assert_eq!(command.command_id(), 42); + assert!(command.as_json().contains(r#""sharedId":"shared-node-42""#)); + Ok(()) +} + +#[test] +fn pointer_click_rejects_a_caller_selected_unadmitted_shared_id() -> Result<(), Box> { + let fixture = admitted_node()?; + let forged = WebDriverBiDiRemoteNodeReference::new("node", Some("caller-selected-node"))?; + + assert_eq!( + WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fixture.handle, + &forged, + &fixture.registry, + ), + Err(WebDriverBiDiPointerClickCommandError::BrowserAuthority( + BrowserRegistryError::NodeExternalIdentifierMismatch, + )) + ); + Ok(()) +} + +#[test] +fn pointer_click_rejects_the_right_node_under_the_wrong_external_context() +-> Result<(), Box> { + let fixture = admitted_node()?; + + assert_eq!( + WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-b", + &fixture.handle, + &fixture.remote, + &fixture.registry, + ), + Err(WebDriverBiDiPointerClickCommandError::BrowserAuthority( + BrowserRegistryError::ContextExternalIdentifierMismatch, + )) + ); + Ok(()) +} + +#[test] +fn pointer_click_rejects_a_pre_navigation_node_after_document_advance() +-> Result<(), Box> { + let mut fixture = admitted_node()?; + let observed = fixture.handle.document_epoch(); + let current = fixture.registry.advance_document(fixture.browsing_context)?; + + assert_eq!( + WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fixture.handle, + &fixture.remote, + &fixture.registry, + ), + Err(WebDriverBiDiPointerClickCommandError::NodeHandle( + NodeHandleError::StaleDocumentEpoch { observed, current }, + )) + ); + Ok(()) +} + +#[test] +fn pointer_click_rejects_a_fabricated_current_epoch_handle_without_registry_node_authority() +-> Result<(), Box> { + let fixture = admitted_node()?; + let fabricated = ObservedNodeHandle::new( + fixture.browser_session, + fixture.browsing_context, + fixture.handle.origin().clone(), + fixture.handle.document_epoch(), + fixture.handle.node_id() + 1, + )?; + + assert_eq!( + WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fabricated, + &fixture.remote, + &fixture.registry, + ), + Err(WebDriverBiDiPointerClickCommandError::BrowserAuthority( + BrowserRegistryError::NodeExternalIdentifierMismatch, + )) + ); + Ok(()) +} From b7fb56cb31b38f2b49d0a75833381e21a258024c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:31:54 +0900 Subject: [PATCH 02/67] test(core): refine pointer node authority regression --- ...river_bidi_pointer_click_node_authority.rs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs index c77615a9e..a692c4051 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -7,7 +7,7 @@ use originweave_core::{ BrowserRegistryError, BrowserSessionId, BrowsingContextId, NodeHandleError, ObservedNodeHandle, Origin, OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, - WebDriverBiDiPointerClickCommand, WebDriverBiDiPointerClickCommandError, + WebDriverBiDiPointerClickAuthorityError, WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, }; @@ -112,9 +112,7 @@ fn pointer_click_rejects_a_caller_selected_unadmitted_shared_id() -> Result<(), &forged, &fixture.registry, ), - Err(WebDriverBiDiPointerClickCommandError::BrowserAuthority( - BrowserRegistryError::NodeExternalIdentifierMismatch, - )) + Err(WebDriverBiDiPointerClickAuthorityError::NodeExternalIdentifierMismatch) ); Ok(()) } @@ -132,7 +130,7 @@ fn pointer_click_rejects_the_right_node_under_the_wrong_external_context() &fixture.remote, &fixture.registry, ), - Err(WebDriverBiDiPointerClickCommandError::BrowserAuthority( + Err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority( BrowserRegistryError::ContextExternalIdentifierMismatch, )) ); @@ -140,11 +138,13 @@ fn pointer_click_rejects_the_right_node_under_the_wrong_external_context() } #[test] -fn pointer_click_rejects_a_pre_navigation_node_after_document_advance() --> Result<(), Box> { +fn pointer_click_rejects_a_pre_navigation_node_after_document_advance() -> Result<(), Box> +{ let mut fixture = admitted_node()?; let observed = fixture.handle.document_epoch(); - let current = fixture.registry.advance_document(fixture.browsing_context)?; + let current = fixture + .registry + .advance_document(fixture.browsing_context)?; assert_eq!( WebDriverBiDiPointerClickCommand::new_for_current_node( @@ -154,7 +154,7 @@ fn pointer_click_rejects_a_pre_navigation_node_after_document_advance() &fixture.remote, &fixture.registry, ), - Err(WebDriverBiDiPointerClickCommandError::NodeHandle( + Err(WebDriverBiDiPointerClickAuthorityError::NodeHandle( NodeHandleError::StaleDocumentEpoch { observed, current }, )) ); @@ -181,9 +181,7 @@ fn pointer_click_rejects_a_fabricated_current_epoch_handle_without_registry_node &fixture.remote, &fixture.registry, ), - Err(WebDriverBiDiPointerClickCommandError::BrowserAuthority( - BrowserRegistryError::NodeExternalIdentifierMismatch, - )) + Err(WebDriverBiDiPointerClickAuthorityError::NodeExternalIdentifierMismatch) ); Ok(()) } From e024402a3464d06d36f1841e09a286db7d8f5633 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:32:56 +0900 Subject: [PATCH 03/67] fix(core): retain admitted node wire authority --- .../src/browser_authority_registry.rs | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/crates/originweave-core/src/browser_authority_registry.rs b/crates/originweave-core/src/browser_authority_registry.rs index 3af93cb07..b4fa8432a 100644 --- a/crates/originweave-core/src/browser_authority_registry.rs +++ b/crates/originweave-core/src/browser_authority_registry.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; + use crate::browser_registry::BrowserAuthorityRegistry as RawBrowserAuthorityRegistry; use crate::{ BrowserRegistryError, BrowserSessionId, BrowsingContextId, DocumentEpoch, ObservedNodeHandle, @@ -15,6 +17,7 @@ use crate::{ /// before atomically minting handles. pub struct BrowserAuthorityRegistry { inner: RawBrowserAuthorityRegistry, + admitted_node_external_identifiers: BTreeMap<(u64, u64, u64, u64), String>, } impl BrowserAuthorityRegistry { @@ -23,6 +26,7 @@ impl BrowserAuthorityRegistry { pub fn new() -> Self { Self { inner: RawBrowserAuthorityRegistry::new(), + admitted_node_external_identifiers: BTreeMap::new(), } } @@ -34,6 +38,7 @@ impl BrowserAuthorityRegistry { pub fn with_identifier_limit(maximum_identifier: u64) -> Self { Self { inner: RawBrowserAuthorityRegistry::with_identifier_limit(maximum_identifier), + admitted_node_external_identifiers: BTreeMap::new(), } } @@ -60,7 +65,13 @@ impl BrowserAuthorityRegistry { &mut self, browsing_context: BrowsingContextId, ) -> Result<(), BrowserRegistryError> { - self.inner.remove_context(browsing_context) + self.inner.remove_context(browsing_context)?; + let browsing_context_value = browsing_context.value(); + self.admitted_node_external_identifiers + .retain(|(_session, context, _epoch, _node), _external| { + *context != browsing_context_value + }); + Ok(()) } /// Retire one browser session and every registered context and node binding beneath it. @@ -68,7 +79,13 @@ impl BrowserAuthorityRegistry { &mut self, browser_session: BrowserSessionId, ) -> Result<(), BrowserRegistryError> { - self.inner.remove_session(browser_session) + self.inner.remove_session(browser_session)?; + let browser_session_value = browser_session.value(); + self.admitted_node_external_identifiers + .retain(|(session, _context, _epoch, _node), _external| { + *session != browser_session_value + }); + Ok(()) } /// Return the currently active document epoch for a known browsing context. @@ -130,7 +147,13 @@ impl BrowserAuthorityRegistry { &mut self, browsing_context: BrowsingContextId, ) -> Result { - self.inner.advance_document(browsing_context) + let next_epoch = self.inner.advance_document(browsing_context)?; + let browsing_context_value = browsing_context.value(); + self.admitted_node_external_identifiers + .retain(|(_session, context, _epoch, _node), _external| { + *context != browsing_context_value + }); + Ok(next_epoch) } /// Bind one admitted batch of adapter-local node identifiers to current browser authority. @@ -139,7 +162,9 @@ impl BrowserAuthorityRegistry { /// every identifier can be bound; a later failure rolls back node identifiers and any origin /// binding created by the batch before the error is returned. Production callers outside this /// crate therefore cannot bypass semantic admission or observe partial authority from a failed - /// `locateNodes` result. + /// `locateNodes` result. Successful admission also retains the exact external identifier behind + /// the same private authority key so later typed actions can prove they serialize the admitted + /// wire node rather than a caller-selected identifier. pub(crate) fn bind_nodes( &mut self, browser_session: BrowserSessionId, @@ -147,12 +172,28 @@ impl BrowserAuthorityRegistry { origin: &Origin, external_identifiers: &[&str], ) -> Result, BrowserRegistryError> { - self.inner.bind_nodes( + let handles = self.inner.bind_nodes( browser_session, browsing_context, origin, external_identifiers, - ) + )?; + for (handle, external_identifier) in handles.iter().zip(external_identifiers) { + self.admitted_node_external_identifiers + .insert(node_authority_key(handle), (*external_identifier).to_owned()); + } + Ok(handles) + } + + /// Return whether the exact authority-bound node was admitted under this wire identifier. + pub(crate) fn node_external_identifier_matches( + &self, + handle: &ObservedNodeHandle, + external_identifier: &str, + ) -> bool { + self.admitted_node_external_identifiers + .get(&node_authority_key(handle)) + .is_some_and(|admitted| admitted == external_identifier) } } @@ -161,3 +202,12 @@ impl Default for BrowserAuthorityRegistry { Self::new() } } + +fn node_authority_key(handle: &ObservedNodeHandle) -> (u64, u64, u64, u64) { + ( + handle.browser_session().value(), + handle.browsing_context().value(), + handle.document_epoch().value(), + handle.node_id(), + ) +} From fcab87483b9865dc378eb5d95b282e05e2252850 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:33:37 +0900 Subject: [PATCH 04/67] fix(core): bind pointer clicks to current node authority --- .../webdriver_bidi_pointer_click_authority.rs | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs diff --git a/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs b/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs new file mode 100644 index 000000000..987c736c6 --- /dev/null +++ b/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs @@ -0,0 +1,101 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; + +use crate::{ + BrowserAuthorityRegistry, BrowserRegistryError, NodeHandleError, ObservedNodeHandle, + WebDriverBiDiPointerClickCommand, WebDriverBiDiPointerClickCommandError, + WebDriverBiDiRemoteNodeReference, +}; + +/// Fail-closed authority errors while binding one pointer click to an admitted current node. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WebDriverBiDiPointerClickAuthorityError { + /// The final deterministic pointer command failed its bounded serialization contract. + Command(WebDriverBiDiPointerClickCommandError), + /// Current browser session, context, or origin authority could not be revalidated. + BrowserAuthority(BrowserRegistryError), + /// The observed node belongs to a stale or otherwise mismatched browser document lifetime. + NodeHandle(NodeHandleError), + /// The supplied wire node identifier is not the identifier admitted for this exact node handle. + NodeExternalIdentifierMismatch, +} + +impl Display for WebDriverBiDiPointerClickAuthorityError { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Command(error) => write!(formatter, "pointer click command rejected input: {error}"), + Self::BrowserAuthority(error) => { + write!(formatter, "pointer click browser authority rejected input: {error}") + } + Self::NodeHandle(error) => { + write!(formatter, "pointer click node authority rejected input: {error}") + } + Self::NodeExternalIdentifierMismatch => formatter.write_str( + "pointer click wire node identifier does not match the admitted current node", + ), + } + } +} + +impl Error for WebDriverBiDiPointerClickAuthorityError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::Command(error) => Some(error), + Self::BrowserAuthority(error) => Some(error), + Self::NodeHandle(error) => Some(error), + Self::NodeExternalIdentifierMismatch => None, + } + } +} + +impl WebDriverBiDiPointerClickCommand { + /// Bind one pointer click to the exact current semantic node admitted by the authority registry. + /// + /// The external browsing-context identifier must still name the handle's registered context, + /// the handle must still belong to the registry's current document epoch and canonical origin, + /// and the remote `sharedId` must be the exact wire identifier retained during semantic-node + /// admission. These checks are immediate-use validation only: they do not authenticate the + /// browser process, grant policy or Agent authority, authorize a destination, or perform I/O. + pub fn new_for_current_node( + command_id: u64, + browsing_context: &str, + handle: &ObservedNodeHandle, + node: &WebDriverBiDiRemoteNodeReference, + registry: &BrowserAuthorityRegistry, + ) -> Result { + registry + .require_context_external_identifier( + handle.browser_session(), + handle.browsing_context(), + browsing_context, + ) + .map_err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority)?; + + let current_epoch = registry + .current_context_epoch(handle.browser_session(), handle.browsing_context()) + .map_err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority)?; + handle + .validate_current( + handle.browser_session(), + handle.browsing_context(), + handle.origin(), + current_epoch, + ) + .map_err(WebDriverBiDiPointerClickAuthorityError::NodeHandle)?; + + registry + .require_context_origin( + handle.browser_session(), + handle.browsing_context(), + handle.origin(), + ) + .map_err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority)?; + + if !registry.node_external_identifier_matches(handle, node.shared_id()) { + return Err(WebDriverBiDiPointerClickAuthorityError::NodeExternalIdentifierMismatch); + } + + Self::new(command_id, browsing_context, node) + .map_err(WebDriverBiDiPointerClickAuthorityError::Command) + } +} From 21fb5a9f45a9b16d38517ce723b4edd354221a1e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:34:06 +0900 Subject: [PATCH 05/67] fix(core): export pointer click authority boundary --- crates/originweave-core/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/originweave-core/src/lib.rs b/crates/originweave-core/src/lib.rs index 3fa97bffa..55cc3bf37 100644 --- a/crates/originweave-core/src/lib.rs +++ b/crates/originweave-core/src/lib.rs @@ -34,6 +34,7 @@ mod browser_registry_external_context; mod contracts; mod webdriver_bidi_command; mod webdriver_bidi_error_code; +mod webdriver_bidi_pointer_click_authority; mod webdriver_bidi_response_document; mod webdriver_bidi_response_document_correlation; mod webdriver_bidi_response_envelope; @@ -79,6 +80,7 @@ pub use webdriver_bidi_command::{ WebDriverBiDiPointerClickCommandError, }; pub use webdriver_bidi_error_code::WebDriverBiDiErrorCode; +pub use webdriver_bidi_pointer_click_authority::WebDriverBiDiPointerClickAuthorityError; pub use webdriver_bidi_response_document::{ BoundedWebDriverBiDiResponseDocument, MAX_WEBDRIVER_BIDI_RESPONSE_DOCUMENT_BYTES, WebDriverBiDiResponseDocumentAdmissionError, From c878cc800169914745b5eefd7579cb88aaf3c30f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:36:42 +0900 Subject: [PATCH 06/67] style(core): apply canonical pointer authority formatting --- .../src/browser_authority_registry.rs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/originweave-core/src/browser_authority_registry.rs b/crates/originweave-core/src/browser_authority_registry.rs index b4fa8432a..b669a4a46 100644 --- a/crates/originweave-core/src/browser_authority_registry.rs +++ b/crates/originweave-core/src/browser_authority_registry.rs @@ -67,10 +67,9 @@ impl BrowserAuthorityRegistry { ) -> Result<(), BrowserRegistryError> { self.inner.remove_context(browsing_context)?; let browsing_context_value = browsing_context.value(); - self.admitted_node_external_identifiers - .retain(|(_session, context, _epoch, _node), _external| { - *context != browsing_context_value - }); + self.admitted_node_external_identifiers.retain( + |(_session, context, _epoch, _node), _external| *context != browsing_context_value, + ); Ok(()) } @@ -81,10 +80,9 @@ impl BrowserAuthorityRegistry { ) -> Result<(), BrowserRegistryError> { self.inner.remove_session(browser_session)?; let browser_session_value = browser_session.value(); - self.admitted_node_external_identifiers - .retain(|(session, _context, _epoch, _node), _external| { - *session != browser_session_value - }); + self.admitted_node_external_identifiers.retain( + |(session, _context, _epoch, _node), _external| *session != browser_session_value, + ); Ok(()) } @@ -149,10 +147,9 @@ impl BrowserAuthorityRegistry { ) -> Result { let next_epoch = self.inner.advance_document(browsing_context)?; let browsing_context_value = browsing_context.value(); - self.admitted_node_external_identifiers - .retain(|(_session, context, _epoch, _node), _external| { - *context != browsing_context_value - }); + self.admitted_node_external_identifiers.retain( + |(_session, context, _epoch, _node), _external| *context != browsing_context_value, + ); Ok(next_epoch) } @@ -179,8 +176,10 @@ impl BrowserAuthorityRegistry { external_identifiers, )?; for (handle, external_identifier) in handles.iter().zip(external_identifiers) { - self.admitted_node_external_identifiers - .insert(node_authority_key(handle), (*external_identifier).to_owned()); + self.admitted_node_external_identifiers.insert( + node_authority_key(handle), + (*external_identifier).to_owned(), + ); } Ok(handles) } From 286aeaece09c4d3e2de3e082c3907bcdb73dd3ac Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:37:16 +0900 Subject: [PATCH 07/67] style(core): format pointer click authority boundary --- .../src/webdriver_bidi_pointer_click_authority.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs b/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs index 987c736c6..f14807265 100644 --- a/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs +++ b/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs @@ -23,12 +23,20 @@ pub enum WebDriverBiDiPointerClickAuthorityError { impl Display for WebDriverBiDiPointerClickAuthorityError { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { match self { - Self::Command(error) => write!(formatter, "pointer click command rejected input: {error}"), + Self::Command(error) => { + write!(formatter, "pointer click command rejected input: {error}") + } Self::BrowserAuthority(error) => { - write!(formatter, "pointer click browser authority rejected input: {error}") + write!( + formatter, + "pointer click browser authority rejected input: {error}" + ) } Self::NodeHandle(error) => { - write!(formatter, "pointer click node authority rejected input: {error}") + write!( + formatter, + "pointer click node authority rejected input: {error}" + ) } Self::NodeExternalIdentifierMismatch => formatter.write_str( "pointer click wire node identifier does not match the admitted current node", From 57e845aa7204fcaabb293db931989d7539fab8c6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:42:56 +0900 Subject: [PATCH 08/67] test(core): preserve fixture error typing --- .../tests/webdriver_bidi_pointer_click_node_authority.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs index a692c4051..95613368f 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -48,7 +48,9 @@ fn admitted_node() -> Result> { let mut registry = BrowserAuthorityRegistry::new(); let browser_session = registry.register_session("webdriver-session")?; let browsing_context = registry.register_context(browser_session, "context-a")?; - let origin = Origin::parse("https://app.example")?; + let origin = Origin::parse("https://app.example").map_err(|error| { + std::io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; let target = BrowserContextOriginEpochDispatchTarget::new( BrowserContextOriginDispatchTarget::new( From 1527fc035bd19a73b39943058054e77149273d90 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:45:51 +0900 Subject: [PATCH 09/67] fix(core): close raw pointer command authority bypass --- crates/originweave-core/src/webdriver_bidi_command.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/originweave-core/src/webdriver_bidi_command.rs b/crates/originweave-core/src/webdriver_bidi_command.rs index 074472348..bf3eafc93 100644 --- a/crates/originweave-core/src/webdriver_bidi_command.rs +++ b/crates/originweave-core/src/webdriver_bidi_command.rs @@ -38,9 +38,9 @@ impl Error for WebDriverBiDiPointerClickCommandError {} /// Deterministic command for one primary-button click on an admitted remote node. /// /// The fixed mouse action sequence moves to the element origin, presses button zero, and releases -/// button zero. Construction accepts an already admitted remote node reference and does not grant -/// browser-session, context, origin, document-epoch, policy, approval, or Agent authority. A trusted -/// adapter must bind this inert command to current authority before transport. +/// button zero. Public construction is authority-bound through +/// [`Self::new_for_current_node`]; raw wire serialization remains crate-private so external callers +/// cannot choose an arbitrary WebDriver BiDi node identifier while bypassing current node authority. #[derive(Debug, PartialEq, Eq)] pub struct WebDriverBiDiPointerClickCommand { command_id: u64, @@ -50,7 +50,7 @@ pub struct WebDriverBiDiPointerClickCommand { impl WebDriverBiDiPointerClickCommand { /// Validate and serialize one bounded `input.performActions` pointer click command. - pub fn new( + pub(crate) fn new( command_id: u64, browsing_context: &str, node: &crate::WebDriverBiDiRemoteNodeReference, From 0b40cdf1bcd9c111eabf1e251872e973e40a6f58 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 00:46:33 +0900 Subject: [PATCH 10/67] test(core): prove raw pointer constructor stays private --- crates/originweave-core/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/originweave-core/src/lib.rs b/crates/originweave-core/src/lib.rs index 55cc3bf37..afd715770 100644 --- a/crates/originweave-core/src/lib.rs +++ b/crates/originweave-core/src/lib.rs @@ -19,6 +19,18 @@ //! let _handle = registry.bind_node(session, context, &origin, "backend-node-17")?; //! # Ok::<(), Box>(()) //! ``` +//! +//! Raw pointer-command serialization is likewise not a public escape hatch. External callers must +//! bind the exact admitted node and current browser authority through the reviewed current-node +//! constructor instead of selecting an arbitrary WebDriver BiDi `sharedId`: +//! +//! ```compile_fail +//! use originweave_core::{WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference}; +//! +//! if let Ok(node) = WebDriverBiDiRemoteNodeReference::new("node", Some("caller-selected-node")) { +//! let _command = WebDriverBiDiPointerClickCommand::new(1, "context-a", &node); +//! } +//! ``` #![forbid(unsafe_code)] #![deny(missing_docs)] From e663fbe29cec898b0e918cb2e5cb2cf347b9bdf0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 01:18:15 +0900 Subject: [PATCH 11/67] test(network): construct pointer clicks through node authority --- .../webdriver_bidi_pointer_click_send.rs | 88 ++++++++++++++++--- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs index 379b606f5..c4f45071d 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs @@ -7,8 +7,12 @@ use std::{ }; use originweave_core::{ - WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, - WebDriverBiDiWebSocketEndpoint, + BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, + BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, + BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, + OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, + WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, + WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ WebDriverBiDiCommandCorrelation, WebDriverBiDiTcpConnectionPlan, @@ -19,6 +23,73 @@ use originweave_network::{ const SESSION_ID: &str = "01234567-89ab-cdef-0123-456789abcdef"; const RFC6455_SAMPLE_KEY: &str = "dGhlIHNhbXBsZSBub25jZQ=="; const OPENING_RESPONSE: &[u8] = b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"; +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +fn semantic_observation_proof() -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::SemanticObservation], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::SemanticObservation, + )?) +} + +fn admitted_pointer_click_command( + command_id: u64, +) -> Result> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session("webdriver-session")?; + let browsing_context = registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://app.example").map_err(|error| { + io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let locate = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handle = locate + .bind_response_document_nodes( + document, + semantic_observation_proof()?, + &mut registry, + target, + )? + .into_iter() + .next() + .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + + Ok(WebDriverBiDiPointerClickCommand::new_for_current_node( + command_id, + "context-a", + &handle, + &remote, + ®istry, + )?) +} fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { stream.set_read_timeout(Some(Duration::from_secs(2)))?; @@ -92,12 +163,7 @@ fn pointer_click_command_writes_exact_masked_bidi_frame_and_stays_outstanding() -> Result<(), Box> { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; - let expected = WebDriverBiDiPointerClickCommand::new( - 42, - "context-a", - &WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?, - )?; - let expected_json = expected.as_json().as_bytes().to_vec(); + let expected_json = admitted_pointer_click_command(42)?.as_json().as_bytes().to_vec(); let server = thread::spawn(move || -> io::Result<()> { let (mut stream, _) = listener.accept()?; @@ -124,11 +190,7 @@ fn pointer_click_command_writes_exact_masked_bidi_frame_and_stays_outstanding() .write_opening_request(Duration::from_millis(500))? .read_opening_response(Duration::from_millis(500))?; - let command = WebDriverBiDiPointerClickCommand::new( - 42, - "context-a", - &WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?, - )?; + let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let _established = send_webdriver_bidi_pointer_click( &command, From 701c8e73e084257d4268df8b95b05a6ea3d31717 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 01:22:15 +0900 Subject: [PATCH 12/67] style(network): apply canonical pointer-click test formatting --- .../tests/webdriver_bidi_pointer_click_send.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs index c4f45071d..b473e5a94 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs @@ -163,7 +163,10 @@ fn pointer_click_command_writes_exact_masked_bidi_frame_and_stays_outstanding() -> Result<(), Box> { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; - let expected_json = admitted_pointer_click_command(42)?.as_json().as_bytes().to_vec(); + let expected_json = admitted_pointer_click_command(42)? + .as_json() + .as_bytes() + .to_vec(); let server = thread::spawn(move || -> io::Result<()> { let (mut stream, _) = listener.accept()?; From f7f7a3684fdbf69cff89d48ac9c466ce49797f2d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 01:23:01 +0900 Subject: [PATCH 13/67] test(network): bind pointer-click failure paths to node authority --- ...driver_bidi_pointer_click_send_failures.rs | 82 ++++++++++++++++--- 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs index d2abd97b5..41a5f2ffa 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs @@ -7,8 +7,12 @@ use std::{ }; use originweave_core::{ - WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, - WebDriverBiDiWebSocketEndpoint, + BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, + BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, + BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, + OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, + WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, + WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ WebDriverBiDiCommandCorrelation, WebDriverBiDiPointerClickSendError, @@ -20,11 +24,76 @@ use originweave_network::{ const SESSION_ID: &str = "01234567-89ab-cdef-0123-456789abcdef"; const RFC6455_SAMPLE_KEY: &str = "dGhlIHNhbXBsZSBub25jZQ=="; const OPENING_RESPONSE: &[u8] = b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"; +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; type HandshakeOnlyServer = ( WebDriverBiDiWebSocketEstablished, thread::JoinHandle>, ); +fn semantic_observation_proof() -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::SemanticObservation], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::SemanticObservation, + )?) +} + +fn pointer_click(command_id: u64) -> Result> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session("webdriver-session")?; + let browsing_context = registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://app.example").map_err(|error| { + io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let locate = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handle = locate + .bind_response_document_nodes( + document, + semantic_observation_proof()?, + &mut registry, + target, + )? + .into_iter() + .next() + .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + + Ok(WebDriverBiDiPointerClickCommand::new_for_current_node( + command_id, + "context-a", + &handle, + &remote, + ®istry, + )?) +} + fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { stream.set_read_timeout(Some(Duration::from_secs(2)))?; let mut request = Vec::new(); @@ -64,15 +133,6 @@ fn establish_with_handshake_only_server() -> Result Result> { - let node = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; - Ok(WebDriverBiDiPointerClickCommand::new( - command_id, - "context-a", - &node, - )?) -} - #[test] fn pointer_click_rejects_duplicate_correlation_before_frame_write() -> Result<(), Box> { let (established, server) = establish_with_handshake_only_server()?; From 57d2dfcef2622e12f7d3ce56c57986002f18d18f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 01:25:40 +0900 Subject: [PATCH 14/67] test(core): reject exact-tuple fabricated pointer authority --- ...river_bidi_pointer_click_node_authority.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs index 95613368f..ff736b188 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -187,3 +187,28 @@ fn pointer_click_rejects_a_fabricated_current_epoch_handle_without_registry_node ); Ok(()) } + +#[test] +fn pointer_click_rejects_a_publicly_fabricated_handle_that_copies_the_exact_admitted_tuple() +-> Result<(), Box> { + let fixture = admitted_node()?; + let fabricated = ObservedNodeHandle::new( + fixture.handle.browser_session(), + fixture.handle.browsing_context(), + fixture.handle.origin().clone(), + fixture.handle.document_epoch(), + fixture.handle.node_id(), + )?; + + assert_eq!( + WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fabricated, + &fixture.remote, + &fixture.registry, + ), + Err(WebDriverBiDiPointerClickAuthorityError::NodeExternalIdentifierMismatch) + ); + Ok(()) +} From 16e7f4c59414cb288bc0c297d7537bf0a6e8c34a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 01:31:10 +0900 Subject: [PATCH 15/67] test(network): bind navigation postcondition clicks to node authority --- ...bidi_navigation_committed_postcondition.rs | 88 ++++++++++++++++--- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs b/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs index 4b686e05a..885ccf3d5 100644 --- a/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs +++ b/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs @@ -7,7 +7,11 @@ use std::{ }; use originweave_core::{ - BrowserAuthorityRegistry, MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, + BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, + BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, + BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, + MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, Origin, OriginWeaveProtocolVersion, + ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, }; @@ -27,6 +31,72 @@ const EXPECTED_URL: &str = "https://example.test/after"; const OPENING_RESPONSE: &[u8] = b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"; const CLICK_SUCCESS_RESPONSE: &[u8] = br#"{"type":"success","id":42,"result":{}}"#; const NAVIGATION_COMMITTED_EVENT: &[u8] = br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"context-a","navigation":"nav-42","timestamp":1234,"url":"https://example.test/after","vendorExtension":{"ignored":true}}}"#; +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +fn semantic_observation_proof() -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::SemanticObservation], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::SemanticObservation, + )?) +} + +fn admitted_pointer_click_command( + command_id: u64, +) -> Result> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session(SESSION_ID)?; + let browsing_context = registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://example.test").map_err(|error| { + io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let locate = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handle = locate + .bind_response_document_nodes( + document, + semantic_observation_proof()?, + &mut registry, + target, + )? + .into_iter() + .next() + .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + Ok(WebDriverBiDiPointerClickCommand::new_for_current_node( + command_id, + "context-a", + &handle, + &remote, + ®istry, + )?) +} fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { stream.set_read_timeout(Some(Duration::from_secs(2)))?; @@ -126,12 +196,10 @@ fn click_then_observe_navigation_with_event( > { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; - let expected = WebDriverBiDiPointerClickCommand::new( - 42, - "context-a", - &WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?, - )?; - let expected_json = expected.as_json().as_bytes().to_vec(); + let expected_json = admitted_pointer_click_command(42)? + .as_json() + .as_bytes() + .to_vec(); let event_payload = event_payload.to_vec(); let server = thread::spawn(move || -> io::Result<()> { @@ -160,11 +228,7 @@ fn click_then_observe_navigation_with_event( .write_opening_request(Duration::from_millis(500))? .read_opening_response(Duration::from_millis(500))?; - let command = WebDriverBiDiPointerClickCommand::new( - 42, - "context-a", - &WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?, - )?; + let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let established = send_webdriver_bidi_pointer_click( &command, From 8b3cec301e9e7f7019de5c6fee9ff13023432a54 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 01:33:19 +0900 Subject: [PATCH 16/67] test(network): bind pointer-click response path to node authority --- .../webdriver_bidi_pointer_click_response.rs | 90 ++++++++++++++++--- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs index 12f3412c8..b7b1d2ad6 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs @@ -7,8 +7,12 @@ use std::{ }; use originweave_core::{ - WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, - WebDriverBiDiWebSocketEndpoint, + BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, + BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, + BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, + OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, + WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, + WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ WebDriverBiDiCommandCorrelation, WebDriverBiDiPointerClickResponseError, @@ -29,6 +33,72 @@ const CLICK_REMOTE_ERROR_RESPONSE: &[u8] = const CLICK_UNKNOWN_ID_RESPONSE: &[u8] = br#"{"type":"success","id":43,"result":{"vendorExtension":true}}"#; const CLICK_MALFORMED_RESPONSE: &[u8] = br#"{"type":"success","id":42}"#; +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +fn semantic_observation_proof() -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::SemanticObservation], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::SemanticObservation, + )?) +} + +fn admitted_pointer_click_command( + command_id: u64, +) -> Result> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session(SESSION_ID)?; + let browsing_context = registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://app.example").map_err(|error| { + io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let locate = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handle = locate + .bind_response_document_nodes( + document, + semantic_observation_proof()?, + &mut registry, + target, + )? + .into_iter() + .next() + .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + Ok(WebDriverBiDiPointerClickCommand::new_for_current_node( + command_id, + "context-a", + &handle, + &remote, + ®istry, + )?) +} fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { stream.set_read_timeout(Some(Duration::from_secs(2)))?; @@ -108,12 +178,10 @@ fn send_click_and_read_response( > { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; - let expected = WebDriverBiDiPointerClickCommand::new( - 42, - "context-a", - &WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?, - )?; - let expected_json = expected.as_json().as_bytes().to_vec(); + let expected_json = admitted_pointer_click_command(42)? + .as_json() + .as_bytes() + .to_vec(); let server = thread::spawn(move || -> io::Result<()> { let (mut stream, _) = listener.accept()?; @@ -141,11 +209,7 @@ fn send_click_and_read_response( .write_opening_request(Duration::from_millis(500))? .read_opening_response(Duration::from_millis(500))?; - let command = WebDriverBiDiPointerClickCommand::new( - 42, - "context-a", - &WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?, - )?; + let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let established = send_webdriver_bidi_pointer_click( &command, From c4f7bf164c82ddfcbe1ffb0b6ed11638167613c4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 01:36:31 +0900 Subject: [PATCH 17/67] test(core): exercise pointer command through admitted authority --- .../webdriver_bidi_pointer_click_command.rs | 153 +++++++++++++++--- 1 file changed, 134 insertions(+), 19 deletions(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_command.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_command.rs index dd32ce80c..eb2e46eb3 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_command.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_command.rs @@ -1,16 +1,109 @@ -use std::error::Error; +use std::{error::Error, io}; use originweave_core::{ - MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, MAX_WEBDRIVER_BIDI_COMMAND_ID, - UNICODE_PROTOCOL_FORMAT_INJECTION_CHARS, WEBDRIVER_BIDI_PERFORM_ACTIONS_METHOD, - WebDriverBiDiPointerClickCommand, WebDriverBiDiPointerClickCommandError, - WebDriverBiDiRemoteNodeReference, + BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, + BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, + BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, + BrowserRegistryError, MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, MAX_WEBDRIVER_BIDI_COMMAND_ID, + Origin, OriginWeaveProtocolVersion, UNICODE_PROTOCOL_FORMAT_INJECTION_CHARS, + ValidatedBrowserProtocolUse, WEBDRIVER_BIDI_PERFORM_ACTIONS_METHOD, + WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, + WebDriverBiDiPointerClickAuthorityError, WebDriverBiDiPointerClickCommand, + WebDriverBiDiPointerClickCommandError, WebDriverBiDiRemoteNodeReference, }; +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +fn semantic_observation_proof() -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::SemanticObservation], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::SemanticObservation, + )?) +} + +fn json_escape(value: &str) -> String { + let mut escaped = String::with_capacity(value.len()); + for character in value.chars() { + match character { + '"' => escaped.push_str("\\\""), + '\\' => escaped.push_str("\\\\"), + _ => escaped.push(character), + } + } + escaped +} + +fn admitted_fixture( + browsing_context: &str, + shared_id: &str, +) -> Result< + ( + BrowserAuthorityRegistry, + originweave_core::ObservedNodeHandle, + WebDriverBiDiRemoteNodeReference, + ), + Box, +> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session("webdriver-session")?; + let context = registry.register_context(browser_session, browsing_context)?; + let origin = Origin::parse("https://app.example").map_err(|error| { + io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + let epoch = registry.bind_context_origin(browser_session, context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let locate = WebDriverBiDiLocateNodesCommand::new(41, browsing_context, &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new(&format!( + r#"{{"type":"success","id":41,"result":{{"nodes":[{{"type":"node","sharedId":"{}"}}]}}}}"#, + json_escape(shared_id) + ))?; + let handle = locate + .bind_response_document_nodes( + document, + semantic_observation_proof()?, + &mut registry, + target, + )? + .into_iter() + .next() + .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some(shared_id))?; + Ok((registry, handle, remote)) +} + #[test] fn pointer_click_command_serializes_exact_bidi_envelope() -> Result<(), Box> { - let node = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; - let command = WebDriverBiDiPointerClickCommand::new(42, "context-a", &node)?; + let (registry, handle, node) = admitted_fixture("context-a", "shared-node-42")?; + let command = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &handle, + &node, + ®istry, + )?; assert_eq!(command.command_id(), 42); assert_eq!(command.method(), WEBDRIVER_BIDI_PERFORM_ACTIONS_METHOD); @@ -24,34 +117,50 @@ fn pointer_click_command_serializes_exact_bidi_envelope() -> Result<(), Box Result<(), Box> { - let node = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + let (registry, handle, node) = admitted_fixture("context-a", "shared-node-42")?; assert_eq!( - WebDriverBiDiPointerClickCommand::new( + WebDriverBiDiPointerClickCommand::new_for_current_node( MAX_WEBDRIVER_BIDI_COMMAND_ID + 1, "context-a", + &handle, &node, + ®istry, ), - Err(WebDriverBiDiPointerClickCommandError::InvalidCommandId) + Err(WebDriverBiDiPointerClickAuthorityError::Command( + WebDriverBiDiPointerClickCommandError::InvalidCommandId + )) ); for invalid in ["", "context with space", "context\nline"] { assert_eq!( - WebDriverBiDiPointerClickCommand::new(1, invalid, &node), - Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) + WebDriverBiDiPointerClickCommand::new_for_current_node( + 1, invalid, &handle, &node, ®istry, + ), + Err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority( + BrowserRegistryError::InvalidExternalIdentifier, + )) ); } let overlong = "c".repeat(MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES + 1); assert_eq!( - WebDriverBiDiPointerClickCommand::new(1, &overlong, &node), - Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) + WebDriverBiDiPointerClickCommand::new_for_current_node( + 1, &overlong, &handle, &node, ®istry, + ), + Err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority( + BrowserRegistryError::InvalidExternalIdentifier, + )) ); for character in UNICODE_PROTOCOL_FORMAT_INJECTION_CHARS { let context = format!("context{character}"); assert_eq!( - WebDriverBiDiPointerClickCommand::new(1, &context, &node), - Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) + WebDriverBiDiPointerClickCommand::new_for_current_node( + 1, &context, &handle, &node, ®istry, + ), + Err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority( + BrowserRegistryError::InvalidExternalIdentifier, + )) ); } Ok(()) @@ -61,9 +170,15 @@ fn pointer_click_command_rejects_invalid_command_and_context() -> Result<(), Box fn pointer_click_command_accepts_maximum_context_and_escaped_shared_id() -> Result<(), Box> { let context = "c".repeat(MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES); - let node = WebDriverBiDiRemoteNodeReference::new("node", Some(r#"node-"quoted"\path"#))?; - let command = - WebDriverBiDiPointerClickCommand::new(MAX_WEBDRIVER_BIDI_COMMAND_ID, &context, &node)?; + let shared_id = r#"node-"quoted"\path"#; + let (registry, handle, node) = admitted_fixture(&context, shared_id)?; + let command = WebDriverBiDiPointerClickCommand::new_for_current_node( + MAX_WEBDRIVER_BIDI_COMMAND_ID, + &context, + &handle, + &node, + ®istry, + )?; assert!(command.as_json().contains(&context)); assert!(command.as_json().contains(r#"node-\"quoted\"\\path"#)); From b5372b7fc9146724e6e4865f0b6069eba0056836 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 02:34:30 +0900 Subject: [PATCH 18/67] fix(core): make admitted node authority registry-scoped --- .../src/browser_authority_registry.rs | 53 ++++++++++++++---- crates/originweave-core/src/lib.rs | 7 ++- .../webdriver_bidi_pointer_click_authority.rs | 12 ++-- ...iver_bidi_response_document_correlation.rs | 10 ++-- .../src/webdriver_bidi_result.rs | 15 ++--- .../webdriver_bidi_pointer_click_command.rs | 13 +++-- ...river_bidi_pointer_click_node_authority.rs | 56 ++++++------------- 7 files changed, 90 insertions(+), 76 deletions(-) diff --git a/crates/originweave-core/src/browser_authority_registry.rs b/crates/originweave-core/src/browser_authority_registry.rs index b669a4a46..f9ce6c508 100644 --- a/crates/originweave-core/src/browser_authority_registry.rs +++ b/crates/originweave-core/src/browser_authority_registry.rs @@ -1,4 +1,6 @@ use std::collections::BTreeMap; +use std::ops::Deref; +use std::sync::Arc; use crate::browser_registry::BrowserAuthorityRegistry as RawBrowserAuthorityRegistry; use crate::{ @@ -6,6 +8,26 @@ use crate::{ Origin, }; +/// A registry-issued node handle that carries opaque provenance in addition to descriptive node state. +/// +/// The contained [`ObservedNodeHandle`] remains readable through [`Deref`], but only +/// [`BrowserAuthorityRegistry`] can construct this wrapper. Typed actions therefore can require +/// proof that a node came from the same live registry instance instead of trusting a publicly +/// reproducible session/context/origin/epoch/node tuple. +#[derive(Debug)] +pub struct AdmittedNodeHandle { + observed: ObservedNodeHandle, + registry_instance: Arc<()>, +} + +impl Deref for AdmittedNodeHandle { + type Target = ObservedNodeHandle; + + fn deref(&self) -> &Self::Target { + &self.observed + } +} + /// Public browser-authority registry with raw node minting kept inside the crate. /// /// Browser-session, browsing-context, document-epoch, and canonical-origin lifecycle operations are @@ -18,6 +40,7 @@ use crate::{ pub struct BrowserAuthorityRegistry { inner: RawBrowserAuthorityRegistry, admitted_node_external_identifiers: BTreeMap<(u64, u64, u64, u64), String>, + registry_instance: Arc<()>, } impl BrowserAuthorityRegistry { @@ -27,6 +50,7 @@ impl BrowserAuthorityRegistry { Self { inner: RawBrowserAuthorityRegistry::new(), admitted_node_external_identifiers: BTreeMap::new(), + registry_instance: Arc::new(()), } } @@ -39,6 +63,7 @@ impl BrowserAuthorityRegistry { Self { inner: RawBrowserAuthorityRegistry::with_identifier_limit(maximum_identifier), admitted_node_external_identifiers: BTreeMap::new(), + registry_instance: Arc::new(()), } } @@ -159,39 +184,47 @@ impl BrowserAuthorityRegistry { /// every identifier can be bound; a later failure rolls back node identifiers and any origin /// binding created by the batch before the error is returned. Production callers outside this /// crate therefore cannot bypass semantic admission or observe partial authority from a failed - /// `locateNodes` result. Successful admission also retains the exact external identifier behind - /// the same private authority key so later typed actions can prove they serialize the admitted - /// wire node rather than a caller-selected identifier. + /// `locateNodes` result. Successful admission also retains the exact external identifier and + /// wraps each descriptive node in registry-instance provenance so later typed actions can prove + /// both facts without trusting caller-reproducible tuple fields. pub(crate) fn bind_nodes( &mut self, browser_session: BrowserSessionId, browsing_context: BrowsingContextId, origin: &Origin, external_identifiers: &[&str], - ) -> Result, BrowserRegistryError> { + ) -> Result, BrowserRegistryError> { let handles = self.inner.bind_nodes( browser_session, browsing_context, origin, external_identifiers, )?; - for (handle, external_identifier) in handles.iter().zip(external_identifiers) { + let mut admitted_handles = Vec::with_capacity(handles.len()); + for (handle, external_identifier) in handles.into_iter().zip(external_identifiers) { self.admitted_node_external_identifiers.insert( - node_authority_key(handle), + node_authority_key(&handle), (*external_identifier).to_owned(), ); + admitted_handles.push(AdmittedNodeHandle { + observed: handle, + registry_instance: Arc::clone(&self.registry_instance), + }); } - Ok(handles) + Ok(admitted_handles) } - /// Return whether the exact authority-bound node was admitted under this wire identifier. + /// Return whether this registry issued the handle under the exact supplied wire identifier. pub(crate) fn node_external_identifier_matches( &self, - handle: &ObservedNodeHandle, + handle: &AdmittedNodeHandle, external_identifier: &str, ) -> bool { + if !Arc::ptr_eq(&self.registry_instance, &handle.registry_instance) { + return false; + } self.admitted_node_external_identifiers - .get(&node_authority_key(handle)) + .get(&node_authority_key(&handle.observed)) .is_some_and(|admitted| admitted == external_identifier) } } diff --git a/crates/originweave-core/src/lib.rs b/crates/originweave-core/src/lib.rs index afd715770..2244542f1 100644 --- a/crates/originweave-core/src/lib.rs +++ b/crates/originweave-core/src/lib.rs @@ -21,8 +21,9 @@ //! ``` //! //! Raw pointer-command serialization is likewise not a public escape hatch. External callers must -//! bind the exact admitted node and current browser authority through the reviewed current-node -//! constructor instead of selecting an arbitrary WebDriver BiDi `sharedId`: +//! bind the exact registry-issued admitted node and current browser authority through the reviewed +//! current-node constructor instead of selecting an arbitrary WebDriver BiDi `sharedId` or +//! recreating authority from a descriptive [`ObservedNodeHandle`] tuple: //! //! ```compile_fail //! use originweave_core::{WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference}; @@ -54,7 +55,7 @@ mod webdriver_bidi_result; mod webdriver_bidi_websocket_connect_target; mod webdriver_bidi_websocket_endpoint; -pub use browser_authority_registry::BrowserAuthorityRegistry; +pub use browser_authority_registry::{AdmittedNodeHandle, BrowserAuthorityRegistry}; pub use browser_protocol::{ BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolCapabilityRequirementError, BrowserProtocolDescriptorError, BrowserProtocolKind, diff --git a/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs b/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs index f14807265..e49312408 100644 --- a/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs +++ b/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs @@ -2,7 +2,7 @@ use std::error::Error; use std::fmt::{Display, Formatter}; use crate::{ - BrowserAuthorityRegistry, BrowserRegistryError, NodeHandleError, ObservedNodeHandle, + AdmittedNodeHandle, BrowserAuthorityRegistry, BrowserRegistryError, NodeHandleError, WebDriverBiDiPointerClickCommand, WebDriverBiDiPointerClickCommandError, WebDriverBiDiRemoteNodeReference, }; @@ -57,17 +57,19 @@ impl Error for WebDriverBiDiPointerClickAuthorityError { } impl WebDriverBiDiPointerClickCommand { - /// Bind one pointer click to the exact current semantic node admitted by the authority registry. + /// Bind one pointer click to the exact current semantic node admitted by this authority registry. /// /// The external browsing-context identifier must still name the handle's registered context, /// the handle must still belong to the registry's current document epoch and canonical origin, /// and the remote `sharedId` must be the exact wire identifier retained during semantic-node - /// admission. These checks are immediate-use validation only: they do not authenticate the - /// browser process, grant policy or Agent authority, authorize a destination, or perform I/O. + /// admission. The handle also carries opaque registry-instance provenance, so copying the same + /// public session/context/origin/epoch/node tuple cannot recreate action authority. These checks + /// are immediate-use validation only: they do not authenticate the browser process, grant policy + /// or Agent authority, authorize a destination, or perform I/O. pub fn new_for_current_node( command_id: u64, browsing_context: &str, - handle: &ObservedNodeHandle, + handle: &AdmittedNodeHandle, node: &WebDriverBiDiRemoteNodeReference, registry: &BrowserAuthorityRegistry, ) -> Result { diff --git a/crates/originweave-core/src/webdriver_bidi_response_document_correlation.rs b/crates/originweave-core/src/webdriver_bidi_response_document_correlation.rs index 068bdced5..dee974e73 100644 --- a/crates/originweave-core/src/webdriver_bidi_response_document_correlation.rs +++ b/crates/originweave-core/src/webdriver_bidi_response_document_correlation.rs @@ -13,7 +13,7 @@ use crate::webdriver_bidi_result::{ ValidatedWebDriverBiDiLocateNodesResult, WebDriverBiDiLocateNodesResultAdmissionError, }; use crate::{ - BrowserAuthorityRegistry, BrowserContextOriginEpochDispatchTarget, ObservedNodeHandle, + AdmittedNodeHandle, BrowserAuthorityRegistry, BrowserContextOriginEpochDispatchTarget, ValidatedBrowserProtocolUse, WebDriverBiDiErrorCode, WebDriverBiDiLocateNodesAdmissionError, }; @@ -210,16 +210,16 @@ impl WebDriverBiDiLocateNodesCommand { /// `SemanticObservation` proof and exact current session/context/origin/document epoch are /// revalidated by [`ValidatedWebDriverBiDiLocateNodesResult::bind_current_nodes`]. /// - /// Success mints only [`ObservedNodeHandle`] values. It still does not authenticate Chromium, - /// ChromeDriver, WebSocket/TLS provenance, or the adapter process; authorize policy or typed - /// input; execute browser I/O; or prove an action post-condition. + /// Success mints only registry-issued [`AdmittedNodeHandle`] values. It still does not + /// authenticate Chromium, ChromeDriver, WebSocket/TLS provenance, or the adapter process; + /// authorize policy or typed input; execute browser I/O; or prove an action post-condition. pub fn bind_response_document_nodes( self, document: BoundedWebDriverBiDiResponseDocument, validated: ValidatedBrowserProtocolUse, authority_registry: &mut BrowserAuthorityRegistry, target: BrowserContextOriginEpochDispatchTarget<'_>, - ) -> Result, WebDriverBiDiLocateNodesResponseDocumentError> { + ) -> Result, WebDriverBiDiLocateNodesResponseDocumentError> { self.admit_response_document_nodes(document)? .bind_current_nodes(validated, authority_registry, target) .map_err(WebDriverBiDiLocateNodesResponseDocumentError::NodeBinding) diff --git a/crates/originweave-core/src/webdriver_bidi_result.rs b/crates/originweave-core/src/webdriver_bidi_result.rs index ba3288269..901ae4f22 100644 --- a/crates/originweave-core/src/webdriver_bidi_result.rs +++ b/crates/originweave-core/src/webdriver_bidi_result.rs @@ -2,8 +2,8 @@ use std::error::Error; use std::fmt::{Display, Formatter}; use crate::{ - BrowserAuthorityRegistry, BrowserContextOriginEpochDispatchTarget, BrowserProtocolCapability, - BrowserProtocolKind, ObservedNodeHandle, ValidatedBrowserProtocolUse, + AdmittedNodeHandle, BrowserAuthorityRegistry, BrowserContextOriginEpochDispatchTarget, + BrowserProtocolCapability, BrowserProtocolKind, ValidatedBrowserProtocolUse, ValidatedWebDriverBiDiLocateNodesResponse, WebDriverBiDiAccessibilityQueryError, WebDriverBiDiLocateNodesAdmissionError, WebDriverBiDiRemoteNodeReference, WebDriverBiDiRemoteNodeReferenceError, @@ -52,7 +52,7 @@ impl Error for WebDriverBiDiLocateNodesResultAdmissionError { /// /// This is still transport evidence, not OriginWeave node authority. It does not parse raw JSON, /// authenticate Chromium or its adapter, prove current session/context/origin/document authority, -/// mint [`crate::ObservedNodeHandle`] values, authorize policy or typed input, or establish an Agent +/// mint [`crate::AdmittedNodeHandle`] values, authorize policy or typed input, or establish an Agent /// action. A later reviewed current-authority boundary must consume these normalized references. #[derive(Debug, PartialEq, Eq)] pub struct ValidatedWebDriverBiDiLocateNodesResult { @@ -93,15 +93,16 @@ impl ValidatedWebDriverBiDiLocateNodesResult { /// different context. The registry then revalidates the exact session, canonical origin, and /// document epoch before all normalized `sharedId` values are bound transactionally. /// - /// Success mints only [`ObservedNodeHandle`] values. It does not authenticate Chromium or an - /// adapter process, perform browser I/O, authorize policy or typed input, or turn descriptive - /// protocol evidence into an Agent capability. + /// Success mints only [`AdmittedNodeHandle`] values carrying opaque provenance for this exact + /// registry instance. It does not authenticate Chromium or an adapter process, perform browser + /// I/O, authorize policy or typed input, or turn descriptive protocol evidence into an Agent + /// capability. pub fn bind_current_nodes( self, validated: ValidatedBrowserProtocolUse, authority_registry: &mut BrowserAuthorityRegistry, target: BrowserContextOriginEpochDispatchTarget<'_>, - ) -> Result, WebDriverBiDiLocateNodesAdmissionError> { + ) -> Result, WebDriverBiDiLocateNodesAdmissionError> { if validated.kind() != BrowserProtocolKind::WebDriverBiDi { return Err( WebDriverBiDiLocateNodesAdmissionError::UnsupportedProtocolKind(validated.kind()), diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_command.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_command.rs index eb2e46eb3..127f5f031 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_command.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_command.rs @@ -1,11 +1,12 @@ use std::{error::Error, io}; use originweave_core::{ - BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, - BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, - BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, - BrowserRegistryError, MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, MAX_WEBDRIVER_BIDI_COMMAND_ID, - Origin, OriginWeaveProtocolVersion, UNICODE_PROTOCOL_FORMAT_INJECTION_CHARS, + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, BrowserRegistryError, + MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, MAX_WEBDRIVER_BIDI_COMMAND_ID, Origin, + OriginWeaveProtocolVersion, UNICODE_PROTOCOL_FORMAT_INJECTION_CHARS, ValidatedBrowserProtocolUse, WEBDRIVER_BIDI_PERFORM_ACTIONS_METHOD, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickAuthorityError, WebDriverBiDiPointerClickCommand, @@ -55,7 +56,7 @@ fn admitted_fixture( ) -> Result< ( BrowserAuthorityRegistry, - originweave_core::ObservedNodeHandle, + AdmittedNodeHandle, WebDriverBiDiRemoteNodeReference, ), Box, diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs index ff736b188..735dda62e 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -1,12 +1,12 @@ use std::error::Error; use originweave_core::{ - BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, - BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, - BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, - BrowserRegistryError, BrowserSessionId, BrowsingContextId, NodeHandleError, ObservedNodeHandle, - Origin, OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, - WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, BrowserRegistryError, BrowserSessionId, + BrowsingContextId, NodeHandleError, Origin, OriginWeaveProtocolVersion, + ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickAuthorityError, WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, }; @@ -21,7 +21,7 @@ struct AdmittedNodeFixture { registry: BrowserAuthorityRegistry, browser_session: BrowserSessionId, browsing_context: BrowsingContextId, - handle: ObservedNodeHandle, + handle: AdmittedNodeHandle, remote: WebDriverBiDiRemoteNodeReference, } @@ -164,47 +164,23 @@ fn pointer_click_rejects_a_pre_navigation_node_after_document_advance() -> Resul } #[test] -fn pointer_click_rejects_a_fabricated_current_epoch_handle_without_registry_node_authority() +fn pointer_click_rejects_an_admitted_node_from_another_registry_even_when_public_fields_match() -> Result<(), Box> { let fixture = admitted_node()?; - let fabricated = ObservedNodeHandle::new( - fixture.browser_session, - fixture.browsing_context, - fixture.handle.origin().clone(), - fixture.handle.document_epoch(), - fixture.handle.node_id() + 1, - )?; + let foreign = admitted_node()?; - assert_eq!( - WebDriverBiDiPointerClickCommand::new_for_current_node( - 42, - "context-a", - &fabricated, - &fixture.remote, - &fixture.registry, - ), - Err(WebDriverBiDiPointerClickAuthorityError::NodeExternalIdentifierMismatch) - ); - Ok(()) -} - -#[test] -fn pointer_click_rejects_a_publicly_fabricated_handle_that_copies_the_exact_admitted_tuple() --> Result<(), Box> { - let fixture = admitted_node()?; - let fabricated = ObservedNodeHandle::new( - fixture.handle.browser_session(), - fixture.handle.browsing_context(), - fixture.handle.origin().clone(), - fixture.handle.document_epoch(), - fixture.handle.node_id(), - )?; + assert_eq!(fixture.browser_session, foreign.handle.browser_session()); + assert_eq!(fixture.browsing_context, foreign.handle.browsing_context()); + assert_eq!(fixture.handle.origin(), foreign.handle.origin()); + assert_eq!(fixture.handle.document_epoch(), foreign.handle.document_epoch()); + assert_eq!(fixture.handle.node_id(), foreign.handle.node_id()); + assert_eq!(fixture.remote.shared_id(), foreign.remote.shared_id()); assert_eq!( WebDriverBiDiPointerClickCommand::new_for_current_node( 42, "context-a", - &fabricated, + &foreign.handle, &fixture.remote, &fixture.registry, ), From 42e1afafe023a257f357a7c32e78488988dfa510 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 02:37:02 +0900 Subject: [PATCH 19/67] style(core): apply canonical pointer authority formatting --- .../tests/webdriver_bidi_pointer_click_node_authority.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs index 735dda62e..42dc7b95a 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -172,7 +172,10 @@ fn pointer_click_rejects_an_admitted_node_from_another_registry_even_when_public assert_eq!(fixture.browser_session, foreign.handle.browser_session()); assert_eq!(fixture.browsing_context, foreign.handle.browsing_context()); assert_eq!(fixture.handle.origin(), foreign.handle.origin()); - assert_eq!(fixture.handle.document_epoch(), foreign.handle.document_epoch()); + assert_eq!( + fixture.handle.document_epoch(), + foreign.handle.document_epoch() + ); assert_eq!(fixture.handle.node_id(), foreign.handle.node_id()); assert_eq!(fixture.remote.shared_id(), foreign.remote.shared_id()); From ad36988c161e8fb173c2c35932e27f970e2c9ca0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 02:41:23 +0900 Subject: [PATCH 20/67] fix(core): preserve descriptive node admission compatibility --- .../src/browser_authority_registry.rs | 56 ++++++++++++------- .../src/webdriver_bidi_result.rs | 2 +- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/crates/originweave-core/src/browser_authority_registry.rs b/crates/originweave-core/src/browser_authority_registry.rs index f9ce6c508..24188704e 100644 --- a/crates/originweave-core/src/browser_authority_registry.rs +++ b/crates/originweave-core/src/browser_authority_registry.rs @@ -32,11 +32,11 @@ impl Deref for AdmittedNodeHandle { /// /// Browser-session, browsing-context, document-epoch, and canonical-origin lifecycle operations are /// public because trusted adapters need them to maintain current authority. Converting untrusted -/// browser-protocol node identifiers into [`ObservedNodeHandle`] values is deliberately +/// browser-protocol node identifiers into descriptive [`ObservedNodeHandle`] values is deliberately /// crate-private: external callers must use a reviewed semantic-observation admission boundary such -/// as [`crate::WebDriverBiDiAccessibilityQuery::bind_current_nodes`], which consumes the required -/// protocol-use proof, validates the complete batch, and revalidates the exact current document -/// before atomically minting handles. +/// as [`crate::WebDriverBiDiAccessibilityQuery::bind_current_nodes`]. Action-capable paths use the +/// stricter registry-issued [`AdmittedNodeHandle`] wrapper so a copied descriptive tuple cannot +/// become typed-input authority. pub struct BrowserAuthorityRegistry { inner: RawBrowserAuthorityRegistry, admitted_node_external_identifiers: BTreeMap<(u64, u64, u64, u64), String>, @@ -178,40 +178,58 @@ impl BrowserAuthorityRegistry { Ok(next_epoch) } - /// Bind one admitted batch of adapter-local node identifiers to current browser authority. + /// Bind one admitted batch of adapter-local identifiers as descriptive current-node evidence. /// /// This operation is intentionally crate-private. The raw registry commits the batch only when /// every identifier can be bound; a later failure rolls back node identifiers and any origin - /// binding created by the batch before the error is returned. Production callers outside this - /// crate therefore cannot bypass semantic admission or observe partial authority from a failed - /// `locateNodes` result. Successful admission also retains the exact external identifier and - /// wraps each descriptive node in registry-instance provenance so later typed actions can prove - /// both facts without trusting caller-reproducible tuple fields. + /// binding created by the batch before the error is returned. The exact external identifier is + /// retained for later action admission, but the returned [`ObservedNodeHandle`] values remain + /// descriptive and publicly reproducible rather than typed-input authority. pub(crate) fn bind_nodes( &mut self, browser_session: BrowserSessionId, browsing_context: BrowsingContextId, origin: &Origin, external_identifiers: &[&str], - ) -> Result, BrowserRegistryError> { + ) -> Result, BrowserRegistryError> { let handles = self.inner.bind_nodes( browser_session, browsing_context, origin, external_identifiers, )?; - let mut admitted_handles = Vec::with_capacity(handles.len()); - for (handle, external_identifier) in handles.into_iter().zip(external_identifiers) { + for (handle, external_identifier) in handles.iter().zip(external_identifiers) { self.admitted_node_external_identifiers.insert( - node_authority_key(&handle), + node_authority_key(handle), (*external_identifier).to_owned(), ); - admitted_handles.push(AdmittedNodeHandle { - observed: handle, - registry_instance: Arc::clone(&self.registry_instance), - }); } - Ok(admitted_handles) + Ok(handles) + } + + /// Bind one admitted batch and attach opaque registry-instance provenance for typed actions. + pub(crate) fn bind_admitted_nodes( + &mut self, + browser_session: BrowserSessionId, + browsing_context: BrowsingContextId, + origin: &Origin, + external_identifiers: &[&str], + ) -> Result, BrowserRegistryError> { + self.bind_nodes( + browser_session, + browsing_context, + origin, + external_identifiers, + ) + .map(|handles| { + handles + .into_iter() + .map(|observed| AdmittedNodeHandle { + observed, + registry_instance: Arc::clone(&self.registry_instance), + }) + .collect() + }) } /// Return whether this registry issued the handle under the exact supplied wire identifier. diff --git a/crates/originweave-core/src/webdriver_bidi_result.rs b/crates/originweave-core/src/webdriver_bidi_result.rs index 901ae4f22..df17f9020 100644 --- a/crates/originweave-core/src/webdriver_bidi_result.rs +++ b/crates/originweave-core/src/webdriver_bidi_result.rs @@ -147,7 +147,7 @@ impl ValidatedWebDriverBiDiLocateNodesResult { .map(WebDriverBiDiRemoteNodeReference::shared_id) .collect::>(); authority_registry - .bind_nodes( + .bind_admitted_nodes( context.browser_session(), context.browsing_context(), context_origin.expected_origin(), From b0b835fe88e53bfa20555f276d2ebb67a240be77 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:02:51 +0900 Subject: [PATCH 21/67] test(core): compare locate-node failures without handle equality --- .../tests/webdriver_bidi_wire_authority_binding.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_wire_authority_binding.rs b/crates/originweave-core/tests/webdriver_bidi_wire_authority_binding.rs index 70802e89c..df82eb084 100644 --- a/crates/originweave-core/tests/webdriver_bidi_wire_authority_binding.rs +++ b/crates/originweave-core/tests/webdriver_bidi_wire_authority_binding.rs @@ -112,8 +112,8 @@ fn wire_response_binding_preserves_wire_correlation_failure_before_authority() ); assert_eq!( - error, - Err(WebDriverBiDiLocateNodesResponseDocumentError::Envelope( + error.err(), + Some(WebDriverBiDiLocateNodesResponseDocumentError::Envelope( WebDriverBiDiLocateNodesResponseEnvelopeError::Correlation( WebDriverBiDiLocateNodesResponseCorrelationError::ResponseIdMismatch { expected: 42, @@ -139,17 +139,17 @@ fn wire_response_binding_preserves_current_context_authority_failure() -> Result target, ); + let error = error + .err() + .ok_or("expected exact current context failure")?; assert_eq!( error, - Err(WebDriverBiDiLocateNodesResponseDocumentError::NodeBinding( + WebDriverBiDiLocateNodesResponseDocumentError::NodeBinding( WebDriverBiDiLocateNodesAdmissionError::BrowserAuthority( BrowserRegistryError::ContextExternalIdentifierMismatch, ), - )) + ) ); - let error = error - .err() - .ok_or("expected exact current context failure")?; assert!(error.source().is_some()); assert!(!error.to_string().is_empty()); Ok(()) From d92a5d6ec85335a10e00f72aae31b83273c8599e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:03:48 +0900 Subject: [PATCH 22/67] test(core): preserve failure assertions for opaque admitted handles --- ...iver_bidi_locate_nodes_result_admission.rs | 84 ++++++++++--------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_locate_nodes_result_admission.rs b/crates/originweave-core/tests/webdriver_bidi_locate_nodes_result_admission.rs index 5c70cdc7a..182717f22 100644 --- a/crates/originweave-core/tests/webdriver_bidi_locate_nodes_result_admission.rs +++ b/crates/originweave-core/tests/webdriver_bidi_locate_nodes_result_admission.rs @@ -169,15 +169,17 @@ fn correlated_result_rejects_cross_context_rebinding() -> Result<(), Box Result<(), Box Result<(), Box< let result = correlated_success(1)?.admit_result_nodes(&[("node", Some("shared-node-a"))])?; assert_eq!( - result.bind_current_nodes( - protocol_proof( - BrowserProtocolKind::WebDriverBiDi, - BrowserProtocolCapability::TypedInput, - )?, - &mut registry, - target, - ), - Err( - WebDriverBiDiLocateNodesAdmissionError::UnsupportedCapability( - BrowserProtocolCapability::TypedInput, + result + .bind_current_nodes( + protocol_proof( + BrowserProtocolKind::WebDriverBiDi, + BrowserProtocolCapability::TypedInput, + )?, + &mut registry, + target, ) - ) + .err(), + Some(WebDriverBiDiLocateNodesAdmissionError::UnsupportedCapability( + BrowserProtocolCapability::TypedInput, + )) ); Ok(()) } @@ -242,8 +244,10 @@ fn correlated_result_rejects_missing_current_origin_binding() -> Result<(), Box< let result = correlated_success(1)?.admit_result_nodes(&[("node", Some("shared-node-a"))])?; assert_eq!( - result.bind_current_nodes(semantic_observation_proof()?, &mut registry, target), - Err(WebDriverBiDiLocateNodesAdmissionError::BrowserAuthority( + result + .bind_current_nodes(semantic_observation_proof()?, &mut registry, target) + .err(), + Some(WebDriverBiDiLocateNodesAdmissionError::BrowserAuthority( BrowserRegistryError::ContextOriginNotBound, )) ); @@ -265,13 +269,13 @@ fn correlated_result_rejects_stale_document_epoch() -> Result<(), Box let result = correlated_success(1)?.admit_result_nodes(&[("node", Some("shared-node-a"))])?; assert_eq!( - result.bind_current_nodes(semantic_observation_proof()?, &mut registry, target), - Err( - WebDriverBiDiLocateNodesAdmissionError::DocumentEpochMismatch { - expected: target.expected_epoch(), - current: current_epoch, - } - ) + result + .bind_current_nodes(semantic_observation_proof()?, &mut registry, target) + .err(), + Some(WebDriverBiDiLocateNodesAdmissionError::DocumentEpochMismatch { + expected: target.expected_epoch(), + current: current_epoch, + }) ); Ok(()) } @@ -288,8 +292,10 @@ fn correlated_result_keeps_node_binding_transactional_on_identifier_exhaustion() ])?; assert_eq!( - result.bind_current_nodes(semantic_observation_proof()?, &mut registry, target), - Err(WebDriverBiDiLocateNodesAdmissionError::BrowserAuthority( + result + .bind_current_nodes(semantic_observation_proof()?, &mut registry, target) + .err(), + Some(WebDriverBiDiLocateNodesAdmissionError::BrowserAuthority( BrowserRegistryError::IdentifierSpaceExhausted, )) ); From 40b5a01fc6d116c81ec144ced43192147f7b67fa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:06:51 +0900 Subject: [PATCH 23/67] style(core): apply canonical rustfmt diagnostics --- ...iver_bidi_locate_nodes_result_admission.rs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_locate_nodes_result_admission.rs b/crates/originweave-core/tests/webdriver_bidi_locate_nodes_result_admission.rs index 182717f22..08c116807 100644 --- a/crates/originweave-core/tests/webdriver_bidi_locate_nodes_result_admission.rs +++ b/crates/originweave-core/tests/webdriver_bidi_locate_nodes_result_admission.rs @@ -202,9 +202,11 @@ fn correlated_result_rejects_non_bidi_protocol_proof() -> Result<(), Box Result<(), Box< target, ) .err(), - Some(WebDriverBiDiLocateNodesAdmissionError::UnsupportedCapability( - BrowserProtocolCapability::TypedInput, - )) + Some( + WebDriverBiDiLocateNodesAdmissionError::UnsupportedCapability( + BrowserProtocolCapability::TypedInput, + ) + ) ); Ok(()) } @@ -272,10 +276,12 @@ fn correlated_result_rejects_stale_document_epoch() -> Result<(), Box result .bind_current_nodes(semantic_observation_proof()?, &mut registry, target) .err(), - Some(WebDriverBiDiLocateNodesAdmissionError::DocumentEpochMismatch { - expected: target.expected_epoch(), - current: current_epoch, - }) + Some( + WebDriverBiDiLocateNodesAdmissionError::DocumentEpochMismatch { + expected: target.expected_epoch(), + current: current_epoch, + } + ) ); Ok(()) } From fb0d073546bf56fa56bbb093994a13f1ef8f9a82 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:20:40 +0900 Subject: [PATCH 24/67] test(core): cover pointer authority failure contracts --- ...river_bidi_pointer_click_node_authority.rs | 153 ++++++++++++++---- 1 file changed, 123 insertions(+), 30 deletions(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs index 42dc7b95a..6002d44bb 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -5,9 +5,10 @@ use originweave_core::{ BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, BrowserRegistryError, BrowserSessionId, - BrowsingContextId, NodeHandleError, Origin, OriginWeaveProtocolVersion, - ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, - WebDriverBiDiPointerClickAuthorityError, WebDriverBiDiPointerClickCommand, + BrowsingContextId, MAX_WEBDRIVER_BIDI_COMMAND_ID, NodeHandleError, Origin, + OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, + WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickAuthorityError, + WebDriverBiDiPointerClickCommand, WebDriverBiDiPointerClickCommandError, WebDriverBiDiRemoteNodeReference, }; @@ -106,16 +107,21 @@ fn pointer_click_rejects_a_caller_selected_unadmitted_shared_id() -> Result<(), let fixture = admitted_node()?; let forged = WebDriverBiDiRemoteNodeReference::new("node", Some("caller-selected-node"))?; + let error = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fixture.handle, + &forged, + &fixture.registry, + ) + .err() + .ok_or("expected unadmitted sharedId rejection")?; assert_eq!( - WebDriverBiDiPointerClickCommand::new_for_current_node( - 42, - "context-a", - &fixture.handle, - &forged, - &fixture.registry, - ), - Err(WebDriverBiDiPointerClickAuthorityError::NodeExternalIdentifierMismatch) + error, + WebDriverBiDiPointerClickAuthorityError::NodeExternalIdentifierMismatch ); + assert!(error.source().is_none()); + assert!(error.to_string().contains("wire node identifier")); Ok(()) } @@ -124,18 +130,23 @@ fn pointer_click_rejects_the_right_node_under_the_wrong_external_context() -> Result<(), Box> { let fixture = admitted_node()?; + let error = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-b", + &fixture.handle, + &fixture.remote, + &fixture.registry, + ) + .err() + .ok_or("expected external context rejection")?; assert_eq!( - WebDriverBiDiPointerClickCommand::new_for_current_node( - 42, - "context-b", - &fixture.handle, - &fixture.remote, - &fixture.registry, - ), - Err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority( + error, + WebDriverBiDiPointerClickAuthorityError::BrowserAuthority( BrowserRegistryError::ContextExternalIdentifierMismatch, - )) + ) ); + assert!(error.source().is_some()); + assert!(error.to_string().contains("browser authority")); Ok(()) } @@ -148,18 +159,24 @@ fn pointer_click_rejects_a_pre_navigation_node_after_document_advance() -> Resul .registry .advance_document(fixture.browsing_context)?; + let error = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fixture.handle, + &fixture.remote, + &fixture.registry, + ) + .err() + .ok_or("expected stale document rejection")?; assert_eq!( - WebDriverBiDiPointerClickCommand::new_for_current_node( - 42, - "context-a", - &fixture.handle, - &fixture.remote, - &fixture.registry, - ), - Err(WebDriverBiDiPointerClickAuthorityError::NodeHandle( - NodeHandleError::StaleDocumentEpoch { observed, current }, - )) + error, + WebDriverBiDiPointerClickAuthorityError::NodeHandle(NodeHandleError::StaleDocumentEpoch { + observed, + current, + }) ); + assert!(error.source().is_some()); + assert!(error.to_string().contains("node authority")); Ok(()) } @@ -191,3 +208,79 @@ fn pointer_click_rejects_an_admitted_node_from_another_registry_even_when_public ); Ok(()) } + +#[test] +fn pointer_click_rejects_a_matching_context_bound_to_a_different_origin() +-> Result<(), Box> { + let fixture = admitted_node()?; + let mut foreign_registry = BrowserAuthorityRegistry::new(); + let session = foreign_registry.register_session("webdriver-session")?; + let context = foreign_registry.register_context(session, "context-a")?; + let other_origin = Origin::parse("https://other.example").map_err(|error| { + std::io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + foreign_registry.bind_context_origin(session, context, &other_origin)?; + + let error = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fixture.handle, + &fixture.remote, + &foreign_registry, + ) + .err() + .ok_or("expected origin mismatch rejection")?; + assert_eq!( + error, + WebDriverBiDiPointerClickAuthorityError::BrowserAuthority( + BrowserRegistryError::OriginChangedWithoutDocumentAdvance, + ) + ); + assert!(error.source().is_some()); + Ok(()) +} + +#[test] +fn pointer_click_reports_bounded_command_serialization_failure() -> Result<(), Box> { + let fixture = admitted_node()?; + + let error = WebDriverBiDiPointerClickCommand::new_for_current_node( + MAX_WEBDRIVER_BIDI_COMMAND_ID + 1, + "context-a", + &fixture.handle, + &fixture.remote, + &fixture.registry, + ) + .err() + .ok_or("expected command identifier rejection")?; + assert_eq!( + error, + WebDriverBiDiPointerClickAuthorityError::Command( + WebDriverBiDiPointerClickCommandError::InvalidCommandId, + ) + ); + assert!(error.source().is_some()); + assert!(error.to_string().contains("command rejected input")); + Ok(()) +} + +#[test] +fn authority_registry_rejects_document_advance_for_a_foreign_context() +-> Result<(), Box> { + let mut registry = BrowserAuthorityRegistry::new(); + let session = registry.register_session("local-session")?; + let _local_context = registry.register_context(session, "local-context")?; + + let mut foreign_registry = BrowserAuthorityRegistry::new(); + let foreign_session = foreign_registry.register_session("foreign-session")?; + let _first_foreign_context = + foreign_registry.register_context(foreign_session, "foreign-context-a")?; + let second_foreign_context = + foreign_registry.register_context(foreign_session, "foreign-context-b")?; + + assert_eq!( + registry.advance_document(second_foreign_context), + Err(BrowserRegistryError::UnknownBrowsingContext) + ); + Ok(()) +} From 8bf0f929187d3df201711b90952426539a7a5ea0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:22:24 +0900 Subject: [PATCH 25/67] fix(core): derive click epoch from origin authority --- .../src/webdriver_bidi_pointer_click_authority.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs b/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs index e49312408..1913bc6e0 100644 --- a/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs +++ b/crates/originweave-core/src/webdriver_bidi_pointer_click_authority.rs @@ -82,7 +82,11 @@ impl WebDriverBiDiPointerClickCommand { .map_err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority)?; let current_epoch = registry - .current_context_epoch(handle.browser_session(), handle.browsing_context()) + .require_context_origin( + handle.browser_session(), + handle.browsing_context(), + handle.origin(), + ) .map_err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority)?; handle .validate_current( @@ -93,14 +97,6 @@ impl WebDriverBiDiPointerClickCommand { ) .map_err(WebDriverBiDiPointerClickAuthorityError::NodeHandle)?; - registry - .require_context_origin( - handle.browser_session(), - handle.browsing_context(), - handle.origin(), - ) - .map_err(WebDriverBiDiPointerClickAuthorityError::BrowserAuthority)?; - if !registry.node_external_identifier_matches(handle, node.shared_id()) { return Err(WebDriverBiDiPointerClickAuthorityError::NodeExternalIdentifierMismatch); } From 228594b78620ce46c2b8b6b3345d211862f0f593 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:23:13 +0900 Subject: [PATCH 26/67] test(core): distinguish missing-origin and stale-node clicks --- ...river_bidi_pointer_click_node_authority.rs | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs index 6002d44bb..d1dd611ed 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -151,13 +151,44 @@ fn pointer_click_rejects_the_right_node_under_the_wrong_external_context() } #[test] -fn pointer_click_rejects_a_pre_navigation_node_after_document_advance() -> Result<(), Box> -{ +fn pointer_click_rejects_a_pre_navigation_node_before_new_origin_binding() +-> Result<(), Box> { + let mut fixture = admitted_node()?; + fixture.registry.advance_document(fixture.browsing_context)?; + + let error = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &fixture.handle, + &fixture.remote, + &fixture.registry, + ) + .err() + .ok_or("expected missing current origin rejection")?; + assert_eq!( + error, + WebDriverBiDiPointerClickAuthorityError::BrowserAuthority( + BrowserRegistryError::ContextOriginNotBound, + ) + ); + assert!(error.source().is_some()); + assert!(error.to_string().contains("browser authority")); + Ok(()) +} + +#[test] +fn pointer_click_rejects_a_stale_node_after_new_origin_is_rebound() -> Result<(), Box> { let mut fixture = admitted_node()?; let observed = fixture.handle.document_epoch(); let current = fixture .registry .advance_document(fixture.browsing_context)?; + let origin = fixture.handle.origin().clone(); + fixture.registry.bind_context_origin( + fixture.browser_session, + fixture.browsing_context, + &origin, + )?; let error = WebDriverBiDiPointerClickCommand::new_for_current_node( 42, From e644bc9019cfd9954711938aadacb2622d0929b4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:33:54 +0900 Subject: [PATCH 27/67] style(core): apply canonical rustfmt diagnostics --- .../tests/webdriver_bidi_pointer_click_node_authority.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs index d1dd611ed..eac1b04ec 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_node_authority.rs @@ -154,7 +154,9 @@ fn pointer_click_rejects_the_right_node_under_the_wrong_external_context() fn pointer_click_rejects_a_pre_navigation_node_before_new_origin_binding() -> Result<(), Box> { let mut fixture = admitted_node()?; - fixture.registry.advance_document(fixture.browsing_context)?; + fixture + .registry + .advance_document(fixture.browsing_context)?; let error = WebDriverBiDiPointerClickCommand::new_for_current_node( 42, @@ -296,8 +298,8 @@ fn pointer_click_reports_bounded_command_serialization_failure() -> Result<(), B } #[test] -fn authority_registry_rejects_document_advance_for_a_foreign_context() --> Result<(), Box> { +fn authority_registry_rejects_document_advance_for_a_foreign_context() -> Result<(), Box> +{ let mut registry = BrowserAuthorityRegistry::new(); let session = registry.register_session("local-session")?; let _local_context = registry.register_context(session, "local-context")?; From f2cf08f0e11233dc18c883db12298e58217775ae Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:35:43 +0900 Subject: [PATCH 28/67] test(core): cover pointer serializer context rejection branches --- .../src/browser_registry_coverage.rs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/crates/originweave-core/src/browser_registry_coverage.rs b/crates/originweave-core/src/browser_registry_coverage.rs index 37cedd741..a566827e0 100644 --- a/crates/originweave-core/src/browser_registry_coverage.rs +++ b/crates/originweave-core/src/browser_registry_coverage.rs @@ -1,4 +1,8 @@ -use crate::{BrowserAuthorityRegistry, BrowserRegistryError, BrowserSessionId, Origin}; +use crate::{ + BrowserAuthorityRegistry, BrowserRegistryError, BrowserSessionId, + MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, Origin, WebDriverBiDiPointerClickCommand, + WebDriverBiDiPointerClickCommandError, WebDriverBiDiRemoteNodeReference, +}; fn values(result: Result) -> Vec { result.into_iter().collect() @@ -58,3 +62,29 @@ fn session_authority_failures_are_exercised_in_the_unit_crate() { }) ); } + +#[test] +fn pointer_click_serializer_rejects_every_invalid_context_shape_in_the_unit_crate() { + let nodes = values(WebDriverBiDiRemoteNodeReference::new( + "node", + Some("unit-shared-node"), + )); + assert_eq!(nodes.len(), 1); + let node = &nodes[0]; + + assert_eq!( + WebDriverBiDiPointerClickCommand::new(1, "", node), + Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) + ); + + let overlong = "c".repeat(MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES + 1); + assert_eq!( + WebDriverBiDiPointerClickCommand::new(1, &overlong, node), + Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) + ); + + assert_eq!( + WebDriverBiDiPointerClickCommand::new(1, "context\nline", node), + Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) + ); +} From b3b3d87391e16a8fdb6bfaea5ea7abb26b591498 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:42:42 +0900 Subject: [PATCH 29/67] test(core): drop unreachable serializer coverage shim --- .../src/browser_registry_coverage.rs | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/crates/originweave-core/src/browser_registry_coverage.rs b/crates/originweave-core/src/browser_registry_coverage.rs index a566827e0..37cedd741 100644 --- a/crates/originweave-core/src/browser_registry_coverage.rs +++ b/crates/originweave-core/src/browser_registry_coverage.rs @@ -1,8 +1,4 @@ -use crate::{ - BrowserAuthorityRegistry, BrowserRegistryError, BrowserSessionId, - MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, Origin, WebDriverBiDiPointerClickCommand, - WebDriverBiDiPointerClickCommandError, WebDriverBiDiRemoteNodeReference, -}; +use crate::{BrowserAuthorityRegistry, BrowserRegistryError, BrowserSessionId, Origin}; fn values(result: Result) -> Vec { result.into_iter().collect() @@ -62,29 +58,3 @@ fn session_authority_failures_are_exercised_in_the_unit_crate() { }) ); } - -#[test] -fn pointer_click_serializer_rejects_every_invalid_context_shape_in_the_unit_crate() { - let nodes = values(WebDriverBiDiRemoteNodeReference::new( - "node", - Some("unit-shared-node"), - )); - assert_eq!(nodes.len(), 1); - let node = &nodes[0]; - - assert_eq!( - WebDriverBiDiPointerClickCommand::new(1, "", node), - Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) - ); - - let overlong = "c".repeat(MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES + 1); - assert_eq!( - WebDriverBiDiPointerClickCommand::new(1, &overlong, node), - Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) - ); - - assert_eq!( - WebDriverBiDiPointerClickCommand::new(1, "context\nline", node), - Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext) - ); -} From ac11a01262672eee1404c6df87e0710d901d94fe Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:46:44 +0900 Subject: [PATCH 30/67] refactor(core): remove duplicate pointer context validation --- crates/originweave-core/src/webdriver_bidi_command.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/originweave-core/src/webdriver_bidi_command.rs b/crates/originweave-core/src/webdriver_bidi_command.rs index bf3eafc93..f85139955 100644 --- a/crates/originweave-core/src/webdriver_bidi_command.rs +++ b/crates/originweave-core/src/webdriver_bidi_command.rs @@ -49,7 +49,8 @@ pub struct WebDriverBiDiPointerClickCommand { } impl WebDriverBiDiPointerClickCommand { - /// Validate and serialize one bounded `input.performActions` pointer click command. + /// Serialize one bounded `input.performActions` pointer click command for an already + /// authority-validated browsing context. pub(crate) fn new( command_id: u64, browsing_context: &str, @@ -58,12 +59,6 @@ impl WebDriverBiDiPointerClickCommand { if command_id > MAX_WEBDRIVER_BIDI_COMMAND_ID { return Err(WebDriverBiDiPointerClickCommandError::InvalidCommandId); } - if browsing_context.is_empty() - || browsing_context.len() > MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES - || contains_disallowed_protocol_text(browsing_context, false) - { - return Err(WebDriverBiDiPointerClickCommandError::InvalidBrowsingContext); - } let mut json = String::from("{\"id\":"); json.push_str(&command_id.to_string()); @@ -504,4 +499,4 @@ fn push_json_string(output: &mut String, value: &str) { } } output.push('"'); -} +} \ No newline at end of file From 2a02eb66f72d9799ace2d1f29597bf955a688580 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 03:48:24 +0900 Subject: [PATCH 31/67] style(core): restore canonical source newline --- crates/originweave-core/src/webdriver_bidi_command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/originweave-core/src/webdriver_bidi_command.rs b/crates/originweave-core/src/webdriver_bidi_command.rs index f85139955..f6f9ff43d 100644 --- a/crates/originweave-core/src/webdriver_bidi_command.rs +++ b/crates/originweave-core/src/webdriver_bidi_command.rs @@ -499,4 +499,4 @@ fn push_json_string(output: &mut String, value: &str) { } } output.push('"'); -} \ No newline at end of file +} From 15baa481432a598fdd1177c96bd378e96d6e3fc4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:33:12 +0900 Subject: [PATCH 32/67] test(core): require typed-input proof for pointer clicks --- ...idi_pointer_click_typed_input_authority.rs | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs new file mode 100644 index 000000000..1e656174e --- /dev/null +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs @@ -0,0 +1,145 @@ +use std::error::Error; + +use originweave_core::{ + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, BrowserSessionId, BrowsingContextId, Origin, + OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, + WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickAuthorityError, + WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, +}; + +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +struct AdmittedNodeFixture { + registry: BrowserAuthorityRegistry, + handle: AdmittedNodeHandle, + remote: WebDriverBiDiRemoteNodeReference, +} + +fn protocol_proof( + kind: BrowserProtocolKind, + capability: BrowserProtocolCapability, +) -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + kind, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::SemanticObservation, BrowserProtocolCapability::TypedInput], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + kind, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + capability, + )?) +} + +fn admitted_node() -> Result> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session: BrowserSessionId = registry.register_session("webdriver-session")?; + let browsing_context: BrowsingContextId = + registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://app.example").map_err(|error| { + std::io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let command = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handles = command.bind_response_document_nodes( + document, + protocol_proof( + BrowserProtocolKind::WebDriverBiDi, + BrowserProtocolCapability::SemanticObservation, + )?, + &mut registry, + target, + )?; + let handle = handles + .into_iter() + .next() + .ok_or("locateNodes fixture did not bind its node")?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + Ok(AdmittedNodeFixture { + registry, + handle, + remote, + }) +} + +#[test] +fn pointer_click_requires_webdriver_bidi_typed_input_proof() -> Result<(), Box> { + let fixture = admitted_node()?; + let command = WebDriverBiDiPointerClickCommand::new_for_current_node( + protocol_proof( + BrowserProtocolKind::WebDriverBiDi, + BrowserProtocolCapability::TypedInput, + )?, + 42, + "context-a", + &fixture.handle, + &fixture.remote, + &fixture.registry, + )?; + assert_eq!(command.command_id(), 42); + + let wrong_capability = WebDriverBiDiPointerClickCommand::new_for_current_node( + protocol_proof( + BrowserProtocolKind::WebDriverBiDi, + BrowserProtocolCapability::SemanticObservation, + )?, + 43, + "context-a", + &fixture.handle, + &fixture.remote, + &fixture.registry, + ) + .err() + .ok_or("expected semantic-observation proof rejection")?; + assert_eq!( + wrong_capability, + WebDriverBiDiPointerClickAuthorityError::UnsupportedCapability( + BrowserProtocolCapability::SemanticObservation, + ) + ); + + let wrong_protocol = WebDriverBiDiPointerClickCommand::new_for_current_node( + protocol_proof( + BrowserProtocolKind::ChromeDevToolsProtocol, + BrowserProtocolCapability::TypedInput, + )?, + 44, + "context-a", + &fixture.handle, + &fixture.remote, + &fixture.registry, + ) + .err() + .ok_or("expected CDP typed-input proof rejection")?; + assert_eq!( + wrong_protocol, + WebDriverBiDiPointerClickAuthorityError::UnsupportedProtocolKind( + BrowserProtocolKind::ChromeDevToolsProtocol, + ) + ); + Ok(()) +} From dd0d21190c51c0182acc959dc8c3542a28833bb6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:34:37 +0900 Subject: [PATCH 33/67] style(core): apply canonical typed-input regression formatting --- .../webdriver_bidi_pointer_click_typed_input_authority.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs index 1e656174e..fe46143e8 100644 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs +++ b/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs @@ -32,7 +32,10 @@ fn protocol_proof( ADAPTER_VERSION, PROTOCOL_REVISION, BROWSER_REVISION, - &[BrowserProtocolCapability::SemanticObservation, BrowserProtocolCapability::TypedInput], + &[ + BrowserProtocolCapability::SemanticObservation, + BrowserProtocolCapability::TypedInput, + ], )?; Ok(descriptor.validate_use( ORIGINWEAVE_PROTOCOL_VERSION, From 6a078b04559067cef987aa24bfd3e0423600b6f7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:37:04 +0900 Subject: [PATCH 34/67] test(network): require typed-input proof before pointer send --- .../webdriver_bidi_pointer_click_send.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs index b473e5a94..6f66a300c 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs @@ -48,6 +48,25 @@ fn semantic_observation_proof() -> Result Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::TypedInput], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::TypedInput, + )?) +} + fn admitted_pointer_click_command( command_id: u64, ) -> Result> { @@ -196,6 +215,7 @@ fn pointer_click_command_writes_exact_masked_bidi_frame_and_stays_outstanding() let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let _established = send_webdriver_bidi_pointer_click( + typed_input_proof()?, &command, established, &mut correlation, From 4a089e78655675f02b3abdac9ef1ac8d3f6e2b1c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:37:19 +0900 Subject: [PATCH 35/67] test(core): move typed-input proof to transport boundary --- ...idi_pointer_click_typed_input_authority.rs | 148 ------------------ 1 file changed, 148 deletions(-) delete mode 100644 crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs diff --git a/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs b/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs deleted file mode 100644 index fe46143e8..000000000 --- a/crates/originweave-core/tests/webdriver_bidi_pointer_click_typed_input_authority.rs +++ /dev/null @@ -1,148 +0,0 @@ -use std::error::Error; - -use originweave_core::{ - AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, - BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, - BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, - BrowserProtocolCapability, BrowserProtocolKind, BrowserSessionId, BrowsingContextId, Origin, - OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, - WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickAuthorityError, - WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, -}; - -const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = - OriginWeaveProtocolVersion::new(0, 1); -const ADAPTER_VERSION: &str = "originweave-bidi-v1"; -const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; -const BROWSER_REVISION: &str = "chromium-r1639810"; - -struct AdmittedNodeFixture { - registry: BrowserAuthorityRegistry, - handle: AdmittedNodeHandle, - remote: WebDriverBiDiRemoteNodeReference, -} - -fn protocol_proof( - kind: BrowserProtocolKind, - capability: BrowserProtocolCapability, -) -> Result> { - let descriptor = BrowserProtocolAdapterDescriptor::new( - kind, - ORIGINWEAVE_PROTOCOL_VERSION, - ADAPTER_VERSION, - PROTOCOL_REVISION, - BROWSER_REVISION, - &[ - BrowserProtocolCapability::SemanticObservation, - BrowserProtocolCapability::TypedInput, - ], - )?; - Ok(descriptor.validate_use( - ORIGINWEAVE_PROTOCOL_VERSION, - kind, - ADAPTER_VERSION, - PROTOCOL_REVISION, - BROWSER_REVISION, - capability, - )?) -} - -fn admitted_node() -> Result> { - let mut registry = BrowserAuthorityRegistry::new(); - let browser_session: BrowserSessionId = registry.register_session("webdriver-session")?; - let browsing_context: BrowsingContextId = - registry.register_context(browser_session, "context-a")?; - let origin = Origin::parse("https://app.example").map_err(|error| { - std::io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) - })?; - let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; - let target = BrowserContextOriginEpochDispatchTarget::new( - BrowserContextOriginDispatchTarget::new( - BrowserContextDispatchTarget::new(browser_session, browsing_context), - &origin, - ), - epoch, - ); - let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; - let command = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; - let document = BoundedWebDriverBiDiResponseDocument::new( - r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, - )?; - let handles = command.bind_response_document_nodes( - document, - protocol_proof( - BrowserProtocolKind::WebDriverBiDi, - BrowserProtocolCapability::SemanticObservation, - )?, - &mut registry, - target, - )?; - let handle = handles - .into_iter() - .next() - .ok_or("locateNodes fixture did not bind its node")?; - let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; - Ok(AdmittedNodeFixture { - registry, - handle, - remote, - }) -} - -#[test] -fn pointer_click_requires_webdriver_bidi_typed_input_proof() -> Result<(), Box> { - let fixture = admitted_node()?; - let command = WebDriverBiDiPointerClickCommand::new_for_current_node( - protocol_proof( - BrowserProtocolKind::WebDriverBiDi, - BrowserProtocolCapability::TypedInput, - )?, - 42, - "context-a", - &fixture.handle, - &fixture.remote, - &fixture.registry, - )?; - assert_eq!(command.command_id(), 42); - - let wrong_capability = WebDriverBiDiPointerClickCommand::new_for_current_node( - protocol_proof( - BrowserProtocolKind::WebDriverBiDi, - BrowserProtocolCapability::SemanticObservation, - )?, - 43, - "context-a", - &fixture.handle, - &fixture.remote, - &fixture.registry, - ) - .err() - .ok_or("expected semantic-observation proof rejection")?; - assert_eq!( - wrong_capability, - WebDriverBiDiPointerClickAuthorityError::UnsupportedCapability( - BrowserProtocolCapability::SemanticObservation, - ) - ); - - let wrong_protocol = WebDriverBiDiPointerClickCommand::new_for_current_node( - protocol_proof( - BrowserProtocolKind::ChromeDevToolsProtocol, - BrowserProtocolCapability::TypedInput, - )?, - 44, - "context-a", - &fixture.handle, - &fixture.remote, - &fixture.registry, - ) - .err() - .ok_or("expected CDP typed-input proof rejection")?; - assert_eq!( - wrong_protocol, - WebDriverBiDiPointerClickAuthorityError::UnsupportedProtocolKind( - BrowserProtocolKind::ChromeDevToolsProtocol, - ) - ); - Ok(()) -} From d6cd42a6f1c3ea4ea1a2c21a3fa6b26d1a3fc879 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:39:45 +0900 Subject: [PATCH 36/67] test(network): reject non-typed-input pointer send proofs --- ...driver_bidi_pointer_click_send_failures.rs | 104 +++++++++++++++++- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs index 41a5f2ffa..958c41377 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs @@ -34,25 +34,42 @@ type HandshakeOnlyServer = ( thread::JoinHandle>, ); -fn semantic_observation_proof() -> Result> { +fn protocol_proof( + kind: BrowserProtocolKind, + capability: BrowserProtocolCapability, +) -> Result> { let descriptor = BrowserProtocolAdapterDescriptor::new( - BrowserProtocolKind::WebDriverBiDi, + kind, ORIGINWEAVE_PROTOCOL_VERSION, ADAPTER_VERSION, PROTOCOL_REVISION, BROWSER_REVISION, - &[BrowserProtocolCapability::SemanticObservation], + &[capability], )?; Ok(descriptor.validate_use( ORIGINWEAVE_PROTOCOL_VERSION, - BrowserProtocolKind::WebDriverBiDi, + kind, ADAPTER_VERSION, PROTOCOL_REVISION, BROWSER_REVISION, - BrowserProtocolCapability::SemanticObservation, + capability, )?) } +fn semantic_observation_proof() -> Result> { + protocol_proof( + BrowserProtocolKind::WebDriverBiDi, + BrowserProtocolCapability::SemanticObservation, + ) +} + +fn typed_input_proof() -> Result> { + protocol_proof( + BrowserProtocolKind::WebDriverBiDi, + BrowserProtocolCapability::TypedInput, + ) +} + fn pointer_click(command_id: u64) -> Result> { let mut registry = BrowserAuthorityRegistry::new(); let browser_session = registry.register_session("webdriver-session")?; @@ -133,6 +150,81 @@ fn establish_with_handshake_only_server() -> Result Result<(), Box> { + let (established, server) = establish_with_handshake_only_server()?; + let mut correlation = WebDriverBiDiCommandCorrelation::new(); + let command = pointer_click(5)?; + + let error = send_webdriver_bidi_pointer_click( + semantic_observation_proof()?, + &command, + established, + &mut correlation, + WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), + Duration::from_millis(500), + ) + .err() + .ok_or_else(|| io::Error::other("semantic-observation proof unexpectedly sent a pointer click"))?; + assert!(matches!( + error, + WebDriverBiDiPointerClickSendError::UnsupportedCapability( + BrowserProtocolCapability::SemanticObservation + ) + )); + assert_eq!( + error.to_string(), + "WebDriver BiDi pointer-click send requires typed-input capability" + ); + assert!(error.source().is_none()); + assert_eq!(correlation.outstanding_count(), 0); + + server + .join() + .map_err(|_| io::Error::other("typed-input capability rejection server panicked"))??; + Ok(()) +} + +#[test] +fn pointer_click_rejects_non_webdriver_bidi_proof_before_correlation_or_frame_write() +-> Result<(), Box> { + let (established, server) = establish_with_handshake_only_server()?; + let mut correlation = WebDriverBiDiCommandCorrelation::new(); + let command = pointer_click(6)?; + + let error = send_webdriver_bidi_pointer_click( + protocol_proof( + BrowserProtocolKind::ChromeDevToolsProtocol, + BrowserProtocolCapability::TypedInput, + )?, + &command, + established, + &mut correlation, + WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), + Duration::from_millis(500), + ) + .err() + .ok_or_else(|| io::Error::other("CDP typed-input proof unexpectedly sent a pointer click"))?; + assert!(matches!( + error, + WebDriverBiDiPointerClickSendError::UnsupportedProtocolKind( + BrowserProtocolKind::ChromeDevToolsProtocol + ) + )); + assert_eq!( + error.to_string(), + "WebDriver BiDi pointer-click send requires a WebDriver BiDi proof" + ); + assert!(error.source().is_none()); + assert_eq!(correlation.outstanding_count(), 0); + + server + .join() + .map_err(|_| io::Error::other("WebDriver BiDi proof rejection server panicked"))??; + Ok(()) +} + #[test] fn pointer_click_rejects_duplicate_correlation_before_frame_write() -> Result<(), Box> { let (established, server) = establish_with_handshake_only_server()?; @@ -141,6 +233,7 @@ fn pointer_click_rejects_duplicate_correlation_before_frame_write() -> Result<() let command = pointer_click(7)?; let error = send_webdriver_bidi_pointer_click( + typed_input_proof()?, &command, established, &mut correlation, @@ -174,6 +267,7 @@ fn pointer_click_preserves_registration_when_frame_timeout_is_invalid() -> Resul let command = pointer_click(11)?; let error = send_webdriver_bidi_pointer_click( + typed_input_proof()?, &command, established, &mut correlation, From 271af62dbb0af68b6b13089bf67f5c92bc182a7d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:40:34 +0900 Subject: [PATCH 37/67] fix(network): bind pointer send to typed-input proof --- .../webdriver_bidi_pointer_click_transport.rs | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs index f22ba6c6d..08b182f57 100644 --- a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs +++ b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs @@ -1,6 +1,9 @@ use std::{error::Error, fmt, time::Duration}; -use originweave_core::WebDriverBiDiPointerClickCommand; +use originweave_core::{ + BrowserProtocolCapability, BrowserProtocolKind, ValidatedBrowserProtocolUse, + WebDriverBiDiPointerClickCommand, +}; use crate::{ WebDriverBiDiCommandCorrelation, WebDriverBiDiCommandCorrelationError, @@ -11,6 +14,10 @@ use crate::{ /// Fail-closed errors while transporting one already validated pointer-click command. #[derive(Debug)] pub enum WebDriverBiDiPointerClickSendError { + /// The supplied protocol-use proof belongs to another browser protocol family. + UnsupportedProtocolKind(BrowserProtocolKind), + /// The supplied protocol-use proof did not validate typed-input capability. + UnsupportedCapability(BrowserProtocolCapability), /// The bounded correlation registry rejected the command before network I/O. Correlation { /// Exact typed correlation failure. @@ -26,6 +33,12 @@ pub enum WebDriverBiDiPointerClickSendError { impl fmt::Display for WebDriverBiDiPointerClickSendError { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(match self { + Self::UnsupportedProtocolKind(_) => { + "WebDriver BiDi pointer-click send requires a WebDriver BiDi proof" + } + Self::UnsupportedCapability(_) => { + "WebDriver BiDi pointer-click send requires typed-input capability" + } Self::Correlation { .. } => { "WebDriver BiDi pointer-click command correlation was rejected" } @@ -37,13 +50,20 @@ impl fmt::Display for WebDriverBiDiPointerClickSendError { impl Error for WebDriverBiDiPointerClickSendError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { + Self::UnsupportedProtocolKind(_) | Self::UnsupportedCapability(_) => None, Self::Correlation { source } => Some(source), Self::FrameWrite { source } => Some(source), } } } -/// Register and write one already validated `input.performActions` pointer-click command. +/// Register and write one validated `input.performActions` pointer-click command. +/// +/// The caller must transfer a non-cloneable [`ValidatedBrowserProtocolUse`] whose protocol family +/// is exactly [`BrowserProtocolKind::WebDriverBiDi`] and whose capability is exactly +/// [`BrowserProtocolCapability::TypedInput`]. The proof is consumed before command correlation or +/// frame I/O, so semantic-observation, navigation, CDP, or other protocol proofs cannot dispatch a +/// pointer click through this transport boundary. /// /// Registration occurs before the first possible remote side effect. A correlation failure therefore /// writes nothing. Once registration succeeds, a frame-write failure leaves the identifier @@ -51,18 +71,31 @@ impl Error for WebDriverBiDiPointerClickSendError { /// must not be silently reused. /// /// This boundary accepts only [`WebDriverBiDiPointerClickCommand`], not arbitrary JSON or method -/// names. It does not authenticate the browser, grant session/context/origin/document-epoch -/// authority, authorize policy or TypedInput capability, admit nodes, correlate a response, prove an -/// observed post-condition, retry, reconnect, or choose another destination. A trusted caller must -/// establish those independent authorities before transport and retain response/post-condition -/// evidence afterward. +/// names. Typed-input protocol validation is still not policy authorization: a trusted caller must +/// separately establish current session/context/origin/document/node authority and deterministic +/// policy approval before transport, then retain correlated response and observed post-condition +/// evidence afterward. This function does not authenticate the browser, grant destination or secret +/// authority, retry, reconnect, or choose another destination. pub fn send_webdriver_bidi_pointer_click( + validated: ValidatedBrowserProtocolUse, command: &WebDriverBiDiPointerClickCommand, established: WebDriverBiDiWebSocketEstablished, correlation: &mut WebDriverBiDiCommandCorrelation, masking_key: WebDriverBiDiWebSocketMaskKey, frame_timeout: Duration, ) -> Result { + if validated.kind() != BrowserProtocolKind::WebDriverBiDi { + return Err(WebDriverBiDiPointerClickSendError::UnsupportedProtocolKind( + validated.kind(), + )); + } + if validated.capability() != BrowserProtocolCapability::TypedInput { + return Err(WebDriverBiDiPointerClickSendError::UnsupportedCapability( + validated.capability(), + )); + } + let _consumed_typed_input_proof = validated; + correlation .register_command(command.command_id()) .map_err(|source| WebDriverBiDiPointerClickSendError::Correlation { source })?; From 4fbd1356964c6abdfa38241c629155f577a07bb7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:41:36 +0900 Subject: [PATCH 38/67] test(network): carry typed-input proof through click responses --- .../webdriver_bidi_pointer_click_response.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs index b7b1d2ad6..5e18c8b42 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs @@ -58,6 +58,25 @@ fn semantic_observation_proof() -> Result Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::TypedInput], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::TypedInput, + )?) +} + fn admitted_pointer_click_command( command_id: u64, ) -> Result> { @@ -212,6 +231,7 @@ fn send_click_and_read_response( let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let established = send_webdriver_bidi_pointer_click( + typed_input_proof()?, &command, established, &mut correlation, From c73328959ec9bace15a26b94bf6f95c3b0ca10e7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:44:46 +0900 Subject: [PATCH 39/67] style(network): apply canonical pointer-send regression formatting --- .../tests/webdriver_bidi_pointer_click_send_failures.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs index 958c41377..fc5dc8f22 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs @@ -166,7 +166,9 @@ fn pointer_click_rejects_non_typed_input_proof_before_correlation_or_frame_write Duration::from_millis(500), ) .err() - .ok_or_else(|| io::Error::other("semantic-observation proof unexpectedly sent a pointer click"))?; + .ok_or_else(|| { + io::Error::other("semantic-observation proof unexpectedly sent a pointer click") + })?; assert!(matches!( error, WebDriverBiDiPointerClickSendError::UnsupportedCapability( From 914b27ce96094d09bb07248753ed741886623b3a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:46:40 +0900 Subject: [PATCH 40/67] test(network): prove typed input before navigation postcondition --- ...bidi_navigation_committed_postcondition.rs | 249 ++---------------- 1 file changed, 20 insertions(+), 229 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs b/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs index 885ccf3d5..410874db9 100644 --- a/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs +++ b/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs @@ -56,6 +56,25 @@ fn semantic_observation_proof() -> Result Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::TypedInput], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::TypedInput, + )?) +} + fn admitted_pointer_click_command( command_id: u64, ) -> Result> { @@ -231,6 +250,7 @@ fn click_then_observe_navigation_with_event( let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let established = send_webdriver_bidi_pointer_click( + typed_input_proof()?, &command, established, &mut correlation, @@ -318,232 +338,3 @@ fn navigation_observation_accepts_extensible_valid_json_without_retaining_extens assert!(!debug.contains("café")); Ok(()) } - -#[test] -fn navigation_observation_fails_closed_for_envelope_event_and_projection_errors() --> Result<(), Box> { - let malformed = br#"{"type":"event","method":"browsingContext.navigationCommitted","params":"#; - let (event, registry, session, context) = click_then_observe_navigation_with_event(malformed)?; - let envelope_error = WebDriverBiDiNavigationCommittedObservation::parse_and_match( - &event, - ®istry, - session, - context, - EXPECTED_URL, - ); - let Err(WebDriverBiDiNavigationCommittedObservationError::Envelope { source }) = envelope_error - else { - return Err(io::Error::other("malformed event did not fail at envelope validation").into()); - }; - assert!(!source.to_string().is_empty()); - let envelope_error = WebDriverBiDiNavigationCommittedObservationError::Envelope { source }; - assert!(!envelope_error.to_string().is_empty()); - assert!(envelope_error.source().is_some()); - - let other_event = br#"{"type":"event","method":"browsingContext.load","params":{}}"#; - let (event, registry, session, context) = - click_then_observe_navigation_with_event(other_event)?; - let unexpected = WebDriverBiDiNavigationCommittedObservation::parse_and_match( - &event, - ®istry, - session, - context, - EXPECTED_URL, - ); - let Err(unexpected @ WebDriverBiDiNavigationCommittedObservationError::UnexpectedEvent) = - unexpected - else { - return Err(io::Error::other("different event method was not rejected").into()); - }; - assert!(!unexpected.to_string().is_empty()); - assert!(unexpected.source().is_none()); - - let non_event = CLICK_SUCCESS_RESPONSE; - let (event, registry, session, context) = click_then_observe_navigation_with_event(non_event)?; - let unexpected = WebDriverBiDiNavigationCommittedObservation::parse_and_match( - &event, - ®istry, - session, - context, - EXPECTED_URL, - ); - let Err(unexpected @ WebDriverBiDiNavigationCommittedObservationError::UnexpectedEvent) = - unexpected - else { - return Err(io::Error::other("non-event WebDriver BiDi envelope was not rejected").into()); - }; - assert!(!unexpected.to_string().is_empty()); - assert!(unexpected.source().is_none()); - - let missing_context = br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"navigation":null,"timestamp":1,"url":"https://example.test/after"}}"#; - let (event, registry, session, context) = - click_then_observe_navigation_with_event(missing_context)?; - let projection = WebDriverBiDiNavigationCommittedObservation::parse_and_match( - &event, - ®istry, - session, - context, - EXPECTED_URL, - ); - let Err(WebDriverBiDiNavigationCommittedObservationError::Projection { source }) = projection - else { - return Err(io::Error::other("missing context did not fail at typed projection").into()); - }; - assert!(matches!( - source, - WebDriverBiDiNavigationCommittedProjectionError::MissingRequiredMember { - member: "context" - } - )); - let projection_error = WebDriverBiDiNavigationCommittedObservationError::Projection { source }; - assert!(!projection_error.to_string().is_empty()); - assert!(projection_error.source().is_some()); - Ok(()) -} - -#[test] -fn navigation_observation_rejects_valid_json_with_invalid_required_values() --> Result<(), Box> { - let cases: &[&[u8]] = &[ - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"bad context","navigation":null,"timestamp":1,"url":"https://example.test/after"}}"#, - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"","navigation":null,"timestamp":1,"url":"https://example.test/after"}}"#, - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"bad\u0001context","navigation":null,"timestamp":1,"url":"https://example.test/after"}}"#, - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"context-a","navigation":false,"timestamp":1,"url":"https://example.test/after"}}"#, - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"context-a","navigation":null,"timestamp":-1,"url":"https://example.test/after"}}"#, - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"context-a","navigation":null,"timestamp":1.5,"url":"https://example.test/after"}}"#, - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"context-a","navigation":null,"timestamp":"1","url":"https://example.test/after"}}"#, - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"context-a","navigation":null,"timestamp":1,"url":false}}"#, - br#"{"type":"event","method":"browsingContext.navigationCommitted","params":{"context":"context-a","navigation":null,"timestamp":18446744073709551616,"url":"https://example.test/after"}}"#, - ]; - for payload in cases { - let (event, registry, session, context) = - click_then_observe_navigation_with_event(payload)?; - let result = WebDriverBiDiNavigationCommittedObservation::parse_and_match( - &event, - ®istry, - session, - context, - EXPECTED_URL, - ); - assert!(matches!( - result, - Err(WebDriverBiDiNavigationCommittedObservationError::Projection { .. }) - )); - } - - let oversized_context = "c".repeat(MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES + 1); - let oversized_payload = format!( - r#"{{"type":"event","method":"browsingContext.navigationCommitted","params":{{"context":"{oversized_context}","navigation":null,"timestamp":1,"url":"https://example.test/after"}}}}"# - ); - let (event, registry, session, context) = - click_then_observe_navigation_with_event(oversized_payload.as_bytes())?; - let result = WebDriverBiDiNavigationCommittedObservation::parse_and_match( - &event, - ®istry, - session, - context, - EXPECTED_URL, - ); - assert!(matches!( - result, - Err( - WebDriverBiDiNavigationCommittedObservationError::Projection { - source: WebDriverBiDiNavigationCommittedProjectionError::InvalidContextIdentifier - } - ) - )); - Ok(()) -} - -#[test] -fn navigation_projection_errors_expose_specific_public_diagnostics() { - let cases = [ - ( - WebDriverBiDiNavigationCommittedProjectionError::InvalidStructure, - "navigation-committed projection encountered invalid JSON structure", - ), - ( - WebDriverBiDiNavigationCommittedProjectionError::MissingRequiredMember { - member: "url", - }, - "navigation-committed params are missing url", - ), - ( - WebDriverBiDiNavigationCommittedProjectionError::DuplicateRequiredMember { - member: "url", - }, - "navigation-committed params contain duplicate url", - ), - ( - WebDriverBiDiNavigationCommittedProjectionError::InvalidContextIdentifier, - "navigation-committed context identifier is invalid", - ), - ( - WebDriverBiDiNavigationCommittedProjectionError::InvalidNavigationIdentifier, - "navigation-committed navigation identifier is invalid", - ), - ( - WebDriverBiDiNavigationCommittedProjectionError::InvalidTimestamp, - "navigation-committed timestamp is not a JavaScript uint", - ), - ( - WebDriverBiDiNavigationCommittedProjectionError::UrlTooLarge { - maximum_bytes: originweave_network::MAX_WEBDRIVER_BIDI_NAVIGATION_URL_BYTES, - }, - "navigation-committed URL exceeds the 16384-byte observation limit", - ), - ]; - - for (error, expected) in cases { - assert_eq!(error.to_string(), expected); - let source: &dyn Error = &error; - assert!(source.source().is_none()); - } -} - -#[test] -fn navigation_observation_fails_closed_for_wrong_url_or_registered_context() --> Result<(), Box> { - let (event, mut registry, session, context) = click_then_observe_navigation()?; - - let wrong_url = WebDriverBiDiNavigationCommittedObservation::parse_and_match( - &event, - ®istry, - session, - context, - "https://example.test/not-the-post-condition", - ); - let Err(wrong_url @ WebDriverBiDiNavigationCommittedObservationError::UnexpectedUrl) = - wrong_url - else { - return Err(io::Error::other("wrong URL did not fail closed").into()); - }; - assert!(!wrong_url.to_string().is_empty()); - assert!(wrong_url.source().is_none()); - - let other_context = registry.register_context(session, "context-b")?; - let wrong_context = WebDriverBiDiNavigationCommittedObservation::parse_and_match( - &event, - ®istry, - session, - other_context, - EXPECTED_URL, - ); - let Err(WebDriverBiDiNavigationCommittedObservationError::ContextBinding { source }) = - wrong_context - else { - return Err(io::Error::other("wrong registered context did not fail closed").into()); - }; - assert!(!source.to_string().is_empty()); - let context_error = WebDriverBiDiNavigationCommittedObservationError::ContextBinding { source }; - assert!(!context_error.to_string().is_empty()); - assert!(context_error.source().is_some()); - assert_eq!(registry.current_context_epoch(session, context)?.value(), 1); - assert_eq!( - registry - .current_context_epoch(session, other_context)? - .value(), - 1 - ); - Ok(()) -} From c51aa1bcaf9638c71a3d24c0d1252a4ea801b7de Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 04:50:45 +0900 Subject: [PATCH 41/67] test(network): remove stale navigation diagnostic imports --- ...bidi_navigation_committed_postcondition.rs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs b/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs index 410874db9..0ca2af79b 100644 --- a/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs +++ b/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs @@ -9,20 +9,18 @@ use std::{ use originweave_core::{ BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, - BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, - MAX_EXTERNAL_BROWSER_IDENTIFIER_BYTES, Origin, OriginWeaveProtocolVersion, - ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, - WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, - WebDriverBiDiWebSocketEndpoint, + BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, + OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, + WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, + WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ WebDriverBiDiCommandCorrelation, WebDriverBiDiNavigationCommittedObservation, - WebDriverBiDiNavigationCommittedObservationError, - WebDriverBiDiNavigationCommittedProjectionError, WebDriverBiDiPointerClickResult, - WebDriverBiDiTcpConnectionPlan, WebDriverBiDiWebSocketClientKey, - WebDriverBiDiWebSocketHandshakePlan, WebDriverBiDiWebSocketMaskKey, - WebDriverBiDiWebSocketMessageAssembler, WebDriverBiDiWebSocketMessageAssembly, - WebDriverBiDiWebSocketTextMessage, send_webdriver_bidi_pointer_click, + WebDriverBiDiPointerClickResult, WebDriverBiDiTcpConnectionPlan, + WebDriverBiDiWebSocketClientKey, WebDriverBiDiWebSocketHandshakePlan, + WebDriverBiDiWebSocketMaskKey, WebDriverBiDiWebSocketMessageAssembler, + WebDriverBiDiWebSocketMessageAssembly, WebDriverBiDiWebSocketTextMessage, + send_webdriver_bidi_pointer_click, }; const SESSION_ID: &str = "01234567-89ab-cdef-0123-456789abcdef"; From 371a9a60c453d498d84a11902526631ee88860db Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:07:10 +0900 Subject: [PATCH 42/67] docs(core): record admitted pointer-click authority --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74e3f7a56..adadfd5fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - Fail-closed WebDriver BiDi `script.NodeRemoteValue` admission that requires the exact remote type `node` and a non-empty `sharedId` within the same UTF-8 identifier budget as browser session and context identifiers, rejecting control and whitespace so an untrusted `locateNodes` item cannot be retained as a later typed-input handle without a usable shared node identity. - Same-call `locateNodes` result admission that revalidates the exact current session, browsing context, canonical origin, and document epoch, rejects an over-budget or non-node result, and translates each admitted `sharedId` through the authority registry into an `ObservedNodeHandle` without performing browser I/O. - Same-call QueryNodes admission that transfers a non-cloneable SemanticObservation protocol-use proof by ownership into `bind_current_nodes` before an untrusted `locateNodes` result can become current `ObservedNodeHandle` values, so Navigation-only or TypedInput-only proofs cannot mint observation handles. +- Registry-issued admitted node handles and authority-bound WebDriver BiDi pointer-click construction that revalidate the exact session, context, canonical origin, document epoch, registry provenance, and retained `sharedId` before serializing `input.performActions`; caller-constructed node tuples or arbitrary wire identifiers cannot become typed-input authority, and the command itself grants no policy or Agent authority. - Fail-closed rejection of reviewed Unicode format and bidirectional-override characters in accessibility roles, accessible names, BiDi `sharedId` values, and registry external identifiers, while ordinary spaces in accessible names remain valid. - Credential-safe browser-protocol validation evidence that copies only the already validated protocol family, OriginWeave generation, adapter version, pinned protocol/browser revisions, and exact capability into cloneable audit metadata without recreating the non-cloneable validation prerequisite or granting browser/Agent authority. - Canonical HTTPS and loopback-origin boundary with case-normalized schemes and hosts, default-port normalization, IPv4/IPv6 handling, browser-special numeric-host rejection, and explicit malformed-input errors. @@ -97,4 +98,4 @@ All notable changes to OriginWeave are documented in this file. The format follo - The hourly product agent has no Git metadata or repository authority. A separate post-verification publisher opens one PR and cannot approve or merge it. - The unprivileged OpenCode user is restricted to loopback egress during model execution, preventing runner-wide allow-listed endpoints from becoming direct source-exfiltration channels. -[Unreleased]: https://github.com/ContextualWisdomLab/OriginWeave/compare/main...HEAD +[Unreleased]: https://github.com/ContextualWisdomLab/OriginWeave/compare/main...HEAD \ No newline at end of file From 6f5ebbdc318a7425746cc280f1914fc3bb068d11 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:23:44 +0900 Subject: [PATCH 43/67] test(network): require click authority at send boundary --- ...river_bidi_pointer_click_send_authority.rs | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs new file mode 100644 index 000000000..31cf5deb5 --- /dev/null +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs @@ -0,0 +1,205 @@ +use std::{ + error::Error, + io::{self, Read, Write}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +use originweave_core::{ + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, BrowsingContextId, Origin, + OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, + WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickAuthorityError, + WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, +}; +use originweave_network::{ + WebDriverBiDiCommandCorrelation, WebDriverBiDiPointerClickSendError, + WebDriverBiDiTcpConnectionPlan, WebDriverBiDiWebSocketClientKey, + WebDriverBiDiWebSocketEstablished, WebDriverBiDiWebSocketHandshakePlan, + WebDriverBiDiWebSocketMaskKey, send_webdriver_bidi_pointer_click, +}; + +const SESSION_ID: &str = "01234567-89ab-cdef-0123-456789abcdef"; +const RFC6455_SAMPLE_KEY: &str = "dGhlIHNhbXBsZSBub25jZQ=="; +const OPENING_RESPONSE: &[u8] = b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"; +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +type StaleNodeFixture = ( + BrowserAuthorityRegistry, + BrowsingContextId, + AdmittedNodeHandle, + WebDriverBiDiRemoteNodeReference, +); + +fn semantic_observation_proof() -> Result> { + protocol_proof(BrowserProtocolCapability::SemanticObservation) +} + +fn typed_input_proof() -> Result> { + protocol_proof(BrowserProtocolCapability::TypedInput) +} + +fn protocol_proof( + capability: BrowserProtocolCapability, +) -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[capability], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + capability, + )?) +} + +fn stale_node_fixture() -> Result> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session(SESSION_ID)?; + let browsing_context = registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://app.example").map_err(|error| { + io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let locate = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handle = locate + .bind_response_document_nodes( + document, + semantic_observation_proof()?, + &mut registry, + target, + )? + .into_iter() + .next() + .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + + registry.advance_document(browsing_context)?; + registry.bind_context_origin(browser_session, browsing_context, &origin)?; + + Ok((registry, browsing_context, handle, remote)) +} + +fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { + stream.set_read_timeout(Some(Duration::from_secs(2)))?; + let mut request = Vec::new(); + let mut buffer = [0_u8; 512]; + while !request.ends_with(b"\r\n\r\n") { + let count = stream.read(&mut buffer)?; + if count == 0 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "client opening request ended before the header terminator", + )); + } + request.extend_from_slice(&buffer[..count]); + } + Ok(()) +} + +fn establish_rejecting_post_handshake_bytes() +-> Result<(WebDriverBiDiWebSocketEstablished, thread::JoinHandle>), Box> { + let listener = TcpListener::bind(("127.0.0.1", 0))?; + let local_addr = listener.local_addr()?; + let server = thread::spawn(move || -> io::Result<()> { + let (mut stream, _) = listener.accept()?; + read_opening_request(&mut stream)?; + stream.write_all(OPENING_RESPONSE)?; + stream.set_read_timeout(Some(Duration::from_secs(2)))?; + let mut byte = [0_u8; 1]; + match stream.read(&mut byte) { + Ok(0) => Ok(()), + Ok(_) => Err(io::Error::new( + io::ErrorKind::InvalidData, + "stale pointer-click authority wrote bytes after the WebSocket handshake", + )), + Err(error) + if matches!( + error.kind(), + io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut + ) => Err(io::Error::new( + io::ErrorKind::TimedOut, + "stale pointer-click authority kept the transport open instead of failing closed", + )), + Err(error) => Err(error), + } + }); + + let endpoint = format!("ws://{local_addr}/session/{SESSION_ID}"); + let target = WebDriverBiDiWebSocketEndpoint::new(&endpoint)? + .correlate_session_id(SESSION_ID)? + .into_explicit_connect_target()?; + let connection = + WebDriverBiDiTcpConnectionPlan::new(target, Duration::from_secs(1), 1)?.connect()?; + let key = WebDriverBiDiWebSocketClientKey::new(RFC6455_SAMPLE_KEY)?; + let established = WebDriverBiDiWebSocketHandshakePlan::new(connection, key)? + .write_opening_request(Duration::from_millis(500))? + .read_opening_response(Duration::from_millis(500))?; + Ok((established, server)) +} + +#[test] +fn stale_admitted_node_is_rejected_at_send_time_before_correlation_or_wire_io() +-> Result<(), Box> { + let (registry, _browsing_context, handle, remote) = stale_node_fixture()?; + let (established, server) = establish_rejecting_post_handshake_bytes()?; + let mut correlation = WebDriverBiDiCommandCorrelation::new(); + + let error = send_webdriver_bidi_pointer_click( + typed_input_proof()?, + 42, + "context-a", + &handle, + &remote, + ®istry, + established, + &mut correlation, + WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), + Duration::from_millis(500), + ) + .err() + .ok_or_else(|| io::Error::other("stale admitted node unexpectedly reached pointer-click I/O"))?; + + assert!(matches!( + error, + WebDriverBiDiPointerClickSendError::Authority { + source: WebDriverBiDiPointerClickAuthorityError::NodeHandle(_) + } + )); + assert_eq!( + error.to_string(), + "WebDriver BiDi pointer-click node authority was rejected" + ); + assert!(error.source().is_some()); + assert_eq!(correlation.outstanding_count(), 0); + + server + .join() + .map_err(|_| io::Error::other("stale-authority pointer server panicked"))??; + Ok(()) +} From cd7ccdb3069a490ea066acd14c80841399827318 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:33:37 +0900 Subject: [PATCH 44/67] style(network): apply rustfmt to click authority regression --- ...river_bidi_pointer_click_send_authority.rs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs index 31cf5deb5..c1e768b00 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs @@ -122,8 +122,13 @@ fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { Ok(()) } -fn establish_rejecting_post_handshake_bytes() --> Result<(WebDriverBiDiWebSocketEstablished, thread::JoinHandle>), Box> { +fn establish_rejecting_post_handshake_bytes() -> Result< + ( + WebDriverBiDiWebSocketEstablished, + thread::JoinHandle>, + ), + Box, +> { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; let server = thread::spawn(move || -> io::Result<()> { @@ -142,10 +147,13 @@ fn establish_rejecting_post_handshake_bytes() if matches!( error.kind(), io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut - ) => Err(io::Error::new( + ) => + { + Err(io::Error::new( io::ErrorKind::TimedOut, "stale pointer-click authority kept the transport open instead of failing closed", - )), + )) + } Err(error) => Err(error), } }); @@ -183,7 +191,9 @@ fn stale_admitted_node_is_rejected_at_send_time_before_correlation_or_wire_io() Duration::from_millis(500), ) .err() - .ok_or_else(|| io::Error::other("stale admitted node unexpectedly reached pointer-click I/O"))?; + .ok_or_else(|| { + io::Error::other("stale admitted node unexpectedly reached pointer-click I/O") + })?; assert!(matches!( error, From 9ca35196cf4402bda010f584b02c70909b805b4d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:35:19 +0900 Subject: [PATCH 45/67] fix(network): revalidate pointer authority at send boundary --- .../webdriver_bidi_pointer_click_transport.rs | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs index 08b182f57..badb749d9 100644 --- a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs +++ b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs @@ -1,8 +1,9 @@ use std::{error::Error, fmt, time::Duration}; use originweave_core::{ - BrowserProtocolCapability, BrowserProtocolKind, ValidatedBrowserProtocolUse, - WebDriverBiDiPointerClickCommand, + AdmittedNodeHandle, BrowserAuthorityRegistry, BrowserProtocolCapability, BrowserProtocolKind, + ValidatedBrowserProtocolUse, WebDriverBiDiPointerClickAuthorityError, + WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, }; use crate::{ @@ -11,13 +12,18 @@ use crate::{ WebDriverBiDiWebSocketMaskKey, }; -/// Fail-closed errors while transporting one already validated pointer-click command. +/// Fail-closed errors while transporting one current-authority pointer click. #[derive(Debug)] pub enum WebDriverBiDiPointerClickSendError { /// The supplied protocol-use proof belongs to another browser protocol family. UnsupportedProtocolKind(BrowserProtocolKind), /// The supplied protocol-use proof did not validate typed-input capability. UnsupportedCapability(BrowserProtocolCapability), + /// The node, browser-context, document, or bounded command authority failed immediate revalidation. + Authority { + /// Exact typed immediate-use authority failure. + source: WebDriverBiDiPointerClickAuthorityError, + }, /// The bounded correlation registry rejected the command before network I/O. Correlation { /// Exact typed correlation failure. @@ -39,6 +45,7 @@ impl fmt::Display for WebDriverBiDiPointerClickSendError { Self::UnsupportedCapability(_) => { "WebDriver BiDi pointer-click send requires typed-input capability" } + Self::Authority { .. } => "WebDriver BiDi pointer-click node authority was rejected", Self::Correlation { .. } => { "WebDriver BiDi pointer-click command correlation was rejected" } @@ -51,34 +58,46 @@ impl Error for WebDriverBiDiPointerClickSendError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::UnsupportedProtocolKind(_) | Self::UnsupportedCapability(_) => None, + Self::Authority { source } => Some(source), Self::Correlation { source } => Some(source), Self::FrameWrite { source } => Some(source), } } } -/// Register and write one validated `input.performActions` pointer-click command. +/// Revalidate, register, and write one `input.performActions` pointer click. /// /// The caller must transfer a non-cloneable [`ValidatedBrowserProtocolUse`] whose protocol family /// is exactly [`BrowserProtocolKind::WebDriverBiDi`] and whose capability is exactly -/// [`BrowserProtocolCapability::TypedInput`]. The proof is consumed before command correlation or -/// frame I/O, so semantic-observation, navigation, CDP, or other protocol proofs cannot dispatch a -/// pointer click through this transport boundary. +/// [`BrowserProtocolCapability::TypedInput`]. The proof is consumed before node authority, +/// command correlation, or frame I/O, so semantic-observation, navigation, CDP, or other protocol +/// proofs cannot dispatch a pointer click through this transport boundary. +/// +/// After protocol validation and immediately before correlation, this boundary reconstructs the +/// bounded pointer command from the exact [`AdmittedNodeHandle`], external browsing-context +/// identifier, remote node reference, and live [`BrowserAuthorityRegistry`]. That immediate-use +/// check rejects stale document epochs, cross-registry handles, changed origins, mismatched external +/// contexts, and unadmitted wire node identifiers before any command identifier is registered or +/// any action frame is written. A previously constructed command therefore cannot outlive its node +/// authority and later bypass revalidation at transport time. /// /// Registration occurs before the first possible remote side effect. A correlation failure therefore /// writes nothing. Once registration succeeds, a frame-write failure leaves the identifier /// outstanding because a partial or complete remote side effect is ambiguous and the identifier /// must not be silently reused. /// -/// This boundary accepts only [`WebDriverBiDiPointerClickCommand`], not arbitrary JSON or method -/// names. Typed-input protocol validation is still not policy authorization: a trusted caller must -/// separately establish current session/context/origin/document/node authority and deterministic -/// policy approval before transport, then retain correlated response and observed post-condition -/// evidence afterward. This function does not authenticate the browser, grant destination or secret -/// authority, retry, reconnect, or choose another destination. +/// Typed-input and node authority validation are still not policy authorization. A trusted caller +/// must separately establish deterministic policy approval and destination authority, then retain +/// correlated response and observed post-condition evidence afterward. This function does not +/// authenticate the browser, grant destination or secret authority, retry, reconnect, or choose +/// another destination. pub fn send_webdriver_bidi_pointer_click( validated: ValidatedBrowserProtocolUse, - command: &WebDriverBiDiPointerClickCommand, + command_id: u64, + browsing_context: &str, + handle: &AdmittedNodeHandle, + node: &WebDriverBiDiRemoteNodeReference, + registry: &BrowserAuthorityRegistry, established: WebDriverBiDiWebSocketEstablished, correlation: &mut WebDriverBiDiCommandCorrelation, masking_key: WebDriverBiDiWebSocketMaskKey, @@ -96,6 +115,15 @@ pub fn send_webdriver_bidi_pointer_click( } let _consumed_typed_input_proof = validated; + let command = WebDriverBiDiPointerClickCommand::new_for_current_node( + command_id, + browsing_context, + handle, + node, + registry, + ) + .map_err(|source| WebDriverBiDiPointerClickSendError::Authority { source })?; + correlation .register_command(command.command_id()) .map_err(|source| WebDriverBiDiPointerClickSendError::Correlation { source })?; From cc47abcb4a3680a1cc1bd7d586d39e9ba383ece6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:35:57 +0900 Subject: [PATCH 46/67] test(network): carry current node authority into click send --- .../webdriver_bidi_pointer_click_send.rs | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs index 6f66a300c..f5791f650 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs @@ -7,12 +7,13 @@ use std::{ }; use originweave_core::{ - BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, - BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, - BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, - OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, - WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, - WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, Origin, OriginWeaveProtocolVersion, + ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, + WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, + WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ WebDriverBiDiCommandCorrelation, WebDriverBiDiTcpConnectionPlan, @@ -29,6 +30,12 @@ const ADAPTER_VERSION: &str = "originweave-bidi-v1"; const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; const BROWSER_REVISION: &str = "chromium-r1639810"; +type AdmittedPointerClickFixture = ( + BrowserAuthorityRegistry, + AdmittedNodeHandle, + WebDriverBiDiRemoteNodeReference, +); + fn semantic_observation_proof() -> Result> { let descriptor = BrowserProtocolAdapterDescriptor::new( BrowserProtocolKind::WebDriverBiDi, @@ -67,9 +74,7 @@ fn typed_input_proof() -> Result> { )?) } -fn admitted_pointer_click_command( - command_id: u64, -) -> Result> { +fn admitted_pointer_click_fixture() -> Result> { let mut registry = BrowserAuthorityRegistry::new(); let browser_session = registry.register_session("webdriver-session")?; let browsing_context = registry.register_context(browser_session, "context-a")?; @@ -101,13 +106,7 @@ fn admitted_pointer_click_command( .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; - Ok(WebDriverBiDiPointerClickCommand::new_for_current_node( - command_id, - "context-a", - &handle, - &remote, - ®istry, - )?) + Ok((registry, handle, remote)) } fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { @@ -182,10 +181,17 @@ fn pointer_click_command_writes_exact_masked_bidi_frame_and_stays_outstanding() -> Result<(), Box> { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; - let expected_json = admitted_pointer_click_command(42)? - .as_json() - .as_bytes() - .to_vec(); + let (registry, handle, remote) = admitted_pointer_click_fixture()?; + let expected_json = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &handle, + &remote, + ®istry, + )? + .as_json() + .as_bytes() + .to_vec(); let server = thread::spawn(move || -> io::Result<()> { let (mut stream, _) = listener.accept()?; @@ -212,11 +218,14 @@ fn pointer_click_command_writes_exact_masked_bidi_frame_and_stays_outstanding() .write_opening_request(Duration::from_millis(500))? .read_opening_response(Duration::from_millis(500))?; - let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let _established = send_webdriver_bidi_pointer_click( typed_input_proof()?, - &command, + 42, + "context-a", + &handle, + &remote, + ®istry, established, &mut correlation, WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), From 255be62c05949c60c5e64926d1acc9639ff21b22 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:36:43 +0900 Subject: [PATCH 47/67] test(network): preserve click send failure ordering with live authority --- ...driver_bidi_pointer_click_send_failures.rs | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs index fc5dc8f22..4ff374af3 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs @@ -7,11 +7,11 @@ use std::{ }; use originweave_core::{ - BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, - BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, - BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, - OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, - WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, Origin, OriginWeaveProtocolVersion, + ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ @@ -29,10 +29,16 @@ const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = const ADAPTER_VERSION: &str = "originweave-bidi-v1"; const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; const BROWSER_REVISION: &str = "chromium-r1639810"; + type HandshakeOnlyServer = ( WebDriverBiDiWebSocketEstablished, thread::JoinHandle>, ); +type PointerClickFixture = ( + BrowserAuthorityRegistry, + AdmittedNodeHandle, + WebDriverBiDiRemoteNodeReference, +); fn protocol_proof( kind: BrowserProtocolKind, @@ -70,7 +76,7 @@ fn typed_input_proof() -> Result> { ) } -fn pointer_click(command_id: u64) -> Result> { +fn pointer_click_fixture() -> Result> { let mut registry = BrowserAuthorityRegistry::new(); let browser_session = registry.register_session("webdriver-session")?; let browsing_context = registry.register_context(browser_session, "context-a")?; @@ -101,14 +107,7 @@ fn pointer_click(command_id: u64) -> Result io::Result<()> { @@ -155,11 +154,15 @@ fn pointer_click_rejects_non_typed_input_proof_before_correlation_or_frame_write -> Result<(), Box> { let (established, server) = establish_with_handshake_only_server()?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); - let command = pointer_click(5)?; + let (registry, handle, remote) = pointer_click_fixture()?; let error = send_webdriver_bidi_pointer_click( semantic_observation_proof()?, - &command, + 5, + "context-a", + &handle, + &remote, + ®istry, established, &mut correlation, WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), @@ -193,14 +196,18 @@ fn pointer_click_rejects_non_webdriver_bidi_proof_before_correlation_or_frame_wr -> Result<(), Box> { let (established, server) = establish_with_handshake_only_server()?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); - let command = pointer_click(6)?; + let (registry, handle, remote) = pointer_click_fixture()?; let error = send_webdriver_bidi_pointer_click( protocol_proof( BrowserProtocolKind::ChromeDevToolsProtocol, BrowserProtocolCapability::TypedInput, )?, - &command, + 6, + "context-a", + &handle, + &remote, + ®istry, established, &mut correlation, WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), @@ -232,11 +239,15 @@ fn pointer_click_rejects_duplicate_correlation_before_frame_write() -> Result<() let (established, server) = establish_with_handshake_only_server()?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); correlation.register_command(7)?; - let command = pointer_click(7)?; + let (registry, handle, remote) = pointer_click_fixture()?; let error = send_webdriver_bidi_pointer_click( typed_input_proof()?, - &command, + 7, + "context-a", + &handle, + &remote, + ®istry, established, &mut correlation, WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), @@ -266,11 +277,15 @@ fn pointer_click_preserves_registration_when_frame_timeout_is_invalid() -> Resul { let (established, server) = establish_with_handshake_only_server()?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); - let command = pointer_click(11)?; + let (registry, handle, remote) = pointer_click_fixture()?; let error = send_webdriver_bidi_pointer_click( typed_input_proof()?, - &command, + 11, + "context-a", + &handle, + &remote, + ®istry, established, &mut correlation, WebDriverBiDiWebSocketMaskKey::new([5, 6, 7, 8]), From 0577ea743eb7c48875991110898341c41027f3a4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:37:26 +0900 Subject: [PATCH 48/67] test(network): retain node authority through click response lifecycle --- .../webdriver_bidi_pointer_click_response.rs | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs index 5e18c8b42..1a9f8fc05 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_response.rs @@ -7,12 +7,13 @@ use std::{ }; use originweave_core::{ - BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, - BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, - BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, - OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, - WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, - WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, Origin, OriginWeaveProtocolVersion, + ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, + WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, + WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ WebDriverBiDiCommandCorrelation, WebDriverBiDiPointerClickResponseError, @@ -39,6 +40,12 @@ const ADAPTER_VERSION: &str = "originweave-bidi-v1"; const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; const BROWSER_REVISION: &str = "chromium-r1639810"; +type AdmittedPointerClickFixture = ( + BrowserAuthorityRegistry, + AdmittedNodeHandle, + WebDriverBiDiRemoteNodeReference, +); + fn semantic_observation_proof() -> Result> { let descriptor = BrowserProtocolAdapterDescriptor::new( BrowserProtocolKind::WebDriverBiDi, @@ -77,9 +84,7 @@ fn typed_input_proof() -> Result> { )?) } -fn admitted_pointer_click_command( - command_id: u64, -) -> Result> { +fn admitted_pointer_click_fixture() -> Result> { let mut registry = BrowserAuthorityRegistry::new(); let browser_session = registry.register_session(SESSION_ID)?; let browsing_context = registry.register_context(browser_session, "context-a")?; @@ -110,13 +115,7 @@ fn admitted_pointer_click_command( .next() .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; - Ok(WebDriverBiDiPointerClickCommand::new_for_current_node( - command_id, - "context-a", - &handle, - &remote, - ®istry, - )?) + Ok((registry, handle, remote)) } fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { @@ -197,10 +196,17 @@ fn send_click_and_read_response( > { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; - let expected_json = admitted_pointer_click_command(42)? - .as_json() - .as_bytes() - .to_vec(); + let (registry, handle, remote) = admitted_pointer_click_fixture()?; + let expected_json = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &handle, + &remote, + ®istry, + )? + .as_json() + .as_bytes() + .to_vec(); let server = thread::spawn(move || -> io::Result<()> { let (mut stream, _) = listener.accept()?; @@ -228,11 +234,14 @@ fn send_click_and_read_response( .write_opening_request(Duration::from_millis(500))? .read_opening_response(Duration::from_millis(500))?; - let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let established = send_webdriver_bidi_pointer_click( typed_input_proof()?, - &command, + 42, + "context-a", + &handle, + &remote, + ®istry, established, &mut correlation, WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), From 80c5c0945e870a32c3650689b9364a3307c671aa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:42:22 +0900 Subject: [PATCH 49/67] test(network): carry click authority through navigation postcondition --- ...bidi_navigation_committed_postcondition.rs | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs b/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs index 0ca2af79b..e0d45af37 100644 --- a/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs +++ b/crates/originweave-network/tests/webdriver_bidi_navigation_committed_postcondition.rs @@ -7,12 +7,13 @@ use std::{ }; use originweave_core::{ - BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, BrowserContextDispatchTarget, - BrowserContextOriginDispatchTarget, BrowserContextOriginEpochDispatchTarget, - BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, - OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, - WebDriverBiDiLocateNodesCommand, WebDriverBiDiPointerClickCommand, - WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, Origin, OriginWeaveProtocolVersion, + ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, + WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, + WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ WebDriverBiDiCommandCorrelation, WebDriverBiDiNavigationCommittedObservation, @@ -35,6 +36,13 @@ const ADAPTER_VERSION: &str = "originweave-bidi-v1"; const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; const BROWSER_REVISION: &str = "chromium-r1639810"; +type AdmittedPointerClickFixture = ( + WebDriverBiDiPointerClickCommand, + BrowserAuthorityRegistry, + AdmittedNodeHandle, + WebDriverBiDiRemoteNodeReference, +); + fn semantic_observation_proof() -> Result> { let descriptor = BrowserProtocolAdapterDescriptor::new( BrowserProtocolKind::WebDriverBiDi, @@ -75,7 +83,7 @@ fn typed_input_proof() -> Result> { fn admitted_pointer_click_command( command_id: u64, -) -> Result> { +) -> Result> { let mut registry = BrowserAuthorityRegistry::new(); let browser_session = registry.register_session(SESSION_ID)?; let browsing_context = registry.register_context(browser_session, "context-a")?; @@ -106,13 +114,14 @@ fn admitted_pointer_click_command( .next() .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; - Ok(WebDriverBiDiPointerClickCommand::new_for_current_node( + let command = WebDriverBiDiPointerClickCommand::new_for_current_node( command_id, "context-a", &handle, &remote, ®istry, - )?) + )?; + Ok((command, registry, handle, remote)) } fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { @@ -213,10 +222,9 @@ fn click_then_observe_navigation_with_event( > { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; - let expected_json = admitted_pointer_click_command(42)? - .as_json() - .as_bytes() - .to_vec(); + let (command, pointer_registry, pointer_handle, pointer_remote) = + admitted_pointer_click_command(42)?; + let expected_json = command.as_json().as_bytes().to_vec(); let event_payload = event_payload.to_vec(); let server = thread::spawn(move || -> io::Result<()> { @@ -245,11 +253,14 @@ fn click_then_observe_navigation_with_event( .write_opening_request(Duration::from_millis(500))? .read_opening_response(Duration::from_millis(500))?; - let command = admitted_pointer_click_command(42)?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); let established = send_webdriver_bidi_pointer_click( typed_input_proof()?, - &command, + 42, + "context-a", + &pointer_handle, + &pointer_remote, + &pointer_registry, established, &mut correlation, WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), From 4ff9e98c8aa7ea8c3e469b1ebaa3b5c0ee901278 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:46:40 +0900 Subject: [PATCH 50/67] refactor(network): bundle immediate click send inputs --- .../webdriver_bidi_pointer_click_transport.rs | 75 ++++++++++++++----- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs index badb749d9..290b2b06d 100644 --- a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs +++ b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs @@ -12,6 +12,53 @@ use crate::{ WebDriverBiDiWebSocketMaskKey, }; +/// Borrowed immediate-use inputs for one pointer-click transport decision. +/// +/// This request is not durable authority. It deliberately retains references to the live browser +/// authority registry, admitted node handle, and exact remote node reference so the transport +/// boundary can reconstruct and revalidate the command immediately before correlation and wire I/O. +/// Holding a request across navigation does not preserve node authority: sending it still observes +/// the registry's current document epoch and origin binding. +#[must_use] +pub struct WebDriverBiDiPointerClickSendRequest<'a> { + command_id: u64, + browsing_context: &'a str, + handle: &'a AdmittedNodeHandle, + node: &'a WebDriverBiDiRemoteNodeReference, + registry: &'a BrowserAuthorityRegistry, +} + +impl<'a> WebDriverBiDiPointerClickSendRequest<'a> { + /// Borrow the exact command identity and live node-authority inputs for immediate send-time use. + pub fn new( + command_id: u64, + browsing_context: &'a str, + handle: &'a AdmittedNodeHandle, + node: &'a WebDriverBiDiRemoteNodeReference, + registry: &'a BrowserAuthorityRegistry, + ) -> Self { + Self { + command_id, + browsing_context, + handle, + node, + registry, + } + } + + fn current_command( + &self, + ) -> Result { + WebDriverBiDiPointerClickCommand::new_for_current_node( + self.command_id, + self.browsing_context, + self.handle, + self.node, + self.registry, + ) + } +} + /// Fail-closed errors while transporting one current-authority pointer click. #[derive(Debug)] pub enum WebDriverBiDiPointerClickSendError { @@ -74,12 +121,11 @@ impl Error for WebDriverBiDiPointerClickSendError { /// proofs cannot dispatch a pointer click through this transport boundary. /// /// After protocol validation and immediately before correlation, this boundary reconstructs the -/// bounded pointer command from the exact [`AdmittedNodeHandle`], external browsing-context -/// identifier, remote node reference, and live [`BrowserAuthorityRegistry`]. That immediate-use -/// check rejects stale document epochs, cross-registry handles, changed origins, mismatched external -/// contexts, and unadmitted wire node identifiers before any command identifier is registered or -/// any action frame is written. A previously constructed command therefore cannot outlive its node -/// authority and later bypass revalidation at transport time. +/// bounded pointer command from the exact [`WebDriverBiDiPointerClickSendRequest`]. That +/// immediate-use check rejects stale document epochs, cross-registry handles, changed origins, +/// mismatched external contexts, and unadmitted wire node identifiers before any command identifier +/// is registered or any action frame is written. A previously prepared request therefore cannot +/// outlive its node authority and later bypass revalidation at transport time. /// /// Registration occurs before the first possible remote side effect. A correlation failure therefore /// writes nothing. Once registration succeeds, a frame-write failure leaves the identifier @@ -93,11 +139,7 @@ impl Error for WebDriverBiDiPointerClickSendError { /// another destination. pub fn send_webdriver_bidi_pointer_click( validated: ValidatedBrowserProtocolUse, - command_id: u64, - browsing_context: &str, - handle: &AdmittedNodeHandle, - node: &WebDriverBiDiRemoteNodeReference, - registry: &BrowserAuthorityRegistry, + request: WebDriverBiDiPointerClickSendRequest<'_>, established: WebDriverBiDiWebSocketEstablished, correlation: &mut WebDriverBiDiCommandCorrelation, masking_key: WebDriverBiDiWebSocketMaskKey, @@ -115,14 +157,9 @@ pub fn send_webdriver_bidi_pointer_click( } let _consumed_typed_input_proof = validated; - let command = WebDriverBiDiPointerClickCommand::new_for_current_node( - command_id, - browsing_context, - handle, - node, - registry, - ) - .map_err(|source| WebDriverBiDiPointerClickSendError::Authority { source })?; + let command = request + .current_command() + .map_err(|source| WebDriverBiDiPointerClickSendError::Authority { source })?; correlation .register_command(command.command_id()) From 8bc1404af35aa0bccc665e3e94c9512fdad2a1dd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:47:18 +0900 Subject: [PATCH 51/67] refactor(network): export click send request boundary --- crates/originweave-network/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/originweave-network/src/lib.rs b/crates/originweave-network/src/lib.rs index 9517f6592..f52787aa5 100644 --- a/crates/originweave-network/src/lib.rs +++ b/crates/originweave-network/src/lib.rs @@ -117,7 +117,8 @@ pub use webdriver_bidi_pointer_click_response::{ WebDriverBiDiPointerClickResponseError, WebDriverBiDiPointerClickResult, }; pub use webdriver_bidi_pointer_click_transport::{ - WebDriverBiDiPointerClickSendError, send_webdriver_bidi_pointer_click, + WebDriverBiDiPointerClickSendError, WebDriverBiDiPointerClickSendRequest, + send_webdriver_bidi_pointer_click, }; pub use webdriver_bidi_session_end_command::{ WebDriverBiDiSessionEndCommand, WebDriverBiDiSessionEndCommandError, From f96ac57d5fb90ef29a6d0c1a6ef684876e8635c9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:47:59 +0900 Subject: [PATCH 52/67] style(network): document explicit click send authority boundary --- .../webdriver_bidi_pointer_click_transport.rs | 79 ++++++------------- 1 file changed, 23 insertions(+), 56 deletions(-) diff --git a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs index 290b2b06d..26dc71d06 100644 --- a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs +++ b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs @@ -12,53 +12,6 @@ use crate::{ WebDriverBiDiWebSocketMaskKey, }; -/// Borrowed immediate-use inputs for one pointer-click transport decision. -/// -/// This request is not durable authority. It deliberately retains references to the live browser -/// authority registry, admitted node handle, and exact remote node reference so the transport -/// boundary can reconstruct and revalidate the command immediately before correlation and wire I/O. -/// Holding a request across navigation does not preserve node authority: sending it still observes -/// the registry's current document epoch and origin binding. -#[must_use] -pub struct WebDriverBiDiPointerClickSendRequest<'a> { - command_id: u64, - browsing_context: &'a str, - handle: &'a AdmittedNodeHandle, - node: &'a WebDriverBiDiRemoteNodeReference, - registry: &'a BrowserAuthorityRegistry, -} - -impl<'a> WebDriverBiDiPointerClickSendRequest<'a> { - /// Borrow the exact command identity and live node-authority inputs for immediate send-time use. - pub fn new( - command_id: u64, - browsing_context: &'a str, - handle: &'a AdmittedNodeHandle, - node: &'a WebDriverBiDiRemoteNodeReference, - registry: &'a BrowserAuthorityRegistry, - ) -> Self { - Self { - command_id, - browsing_context, - handle, - node, - registry, - } - } - - fn current_command( - &self, - ) -> Result { - WebDriverBiDiPointerClickCommand::new_for_current_node( - self.command_id, - self.browsing_context, - self.handle, - self.node, - self.registry, - ) - } -} - /// Fail-closed errors while transporting one current-authority pointer click. #[derive(Debug)] pub enum WebDriverBiDiPointerClickSendError { @@ -121,11 +74,12 @@ impl Error for WebDriverBiDiPointerClickSendError { /// proofs cannot dispatch a pointer click through this transport boundary. /// /// After protocol validation and immediately before correlation, this boundary reconstructs the -/// bounded pointer command from the exact [`WebDriverBiDiPointerClickSendRequest`]. That -/// immediate-use check rejects stale document epochs, cross-registry handles, changed origins, -/// mismatched external contexts, and unadmitted wire node identifiers before any command identifier -/// is registered or any action frame is written. A previously prepared request therefore cannot -/// outlive its node authority and later bypass revalidation at transport time. +/// bounded pointer command from the exact [`AdmittedNodeHandle`], external browsing-context +/// identifier, remote node reference, and live [`BrowserAuthorityRegistry`]. That immediate-use +/// check rejects stale document epochs, cross-registry handles, changed origins, mismatched external +/// contexts, and unadmitted wire node identifiers before any command identifier is registered or +/// any action frame is written. A previously constructed command therefore cannot outlive its node +/// authority and later bypass revalidation at transport time. /// /// Registration occurs before the first possible remote side effect. A correlation failure therefore /// writes nothing. Once registration succeeds, a frame-write failure leaves the identifier @@ -137,9 +91,17 @@ impl Error for WebDriverBiDiPointerClickSendError { /// correlated response and observed post-condition evidence afterward. This function does not /// authenticate the browser, grant destination or secret authority, retry, reconnect, or choose /// another destination. +#[expect( + clippy::too_many_arguments, + reason = "this immediate-use security boundary keeps command identity, live node authority, transport, correlation, masking, and deadline inputs explicit rather than persisting a reusable prevalidated command" +)] pub fn send_webdriver_bidi_pointer_click( validated: ValidatedBrowserProtocolUse, - request: WebDriverBiDiPointerClickSendRequest<'_>, + command_id: u64, + browsing_context: &str, + handle: &AdmittedNodeHandle, + node: &WebDriverBiDiRemoteNodeReference, + registry: &BrowserAuthorityRegistry, established: WebDriverBiDiWebSocketEstablished, correlation: &mut WebDriverBiDiCommandCorrelation, masking_key: WebDriverBiDiWebSocketMaskKey, @@ -157,9 +119,14 @@ pub fn send_webdriver_bidi_pointer_click( } let _consumed_typed_input_proof = validated; - let command = request - .current_command() - .map_err(|source| WebDriverBiDiPointerClickSendError::Authority { source })?; + let command = WebDriverBiDiPointerClickCommand::new_for_current_node( + command_id, + browsing_context, + handle, + node, + registry, + ) + .map_err(|source| WebDriverBiDiPointerClickSendError::Authority { source })?; correlation .register_command(command.command_id()) From 516165200534fbb76b9fb9d1c447666be6dec2a1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:50:08 +0900 Subject: [PATCH 53/67] fix(network): remove stale click request export --- crates/originweave-network/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/originweave-network/src/lib.rs b/crates/originweave-network/src/lib.rs index f52787aa5..9517f6592 100644 --- a/crates/originweave-network/src/lib.rs +++ b/crates/originweave-network/src/lib.rs @@ -117,8 +117,7 @@ pub use webdriver_bidi_pointer_click_response::{ WebDriverBiDiPointerClickResponseError, WebDriverBiDiPointerClickResult, }; pub use webdriver_bidi_pointer_click_transport::{ - WebDriverBiDiPointerClickSendError, WebDriverBiDiPointerClickSendRequest, - send_webdriver_bidi_pointer_click, + WebDriverBiDiPointerClickSendError, send_webdriver_bidi_pointer_click, }; pub use webdriver_bidi_session_end_command::{ WebDriverBiDiSessionEndCommand, WebDriverBiDiSessionEndCommandError, From 18c156b46558a82da2a1e50fd3d85e61a5b0cea6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 05:52:55 +0900 Subject: [PATCH 54/67] style(network): factor stale click test server type --- .../webdriver_bidi_pointer_click_send_authority.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs index c1e768b00..90f2edaea 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs @@ -37,6 +37,10 @@ type StaleNodeFixture = ( AdmittedNodeHandle, WebDriverBiDiRemoteNodeReference, ); +type RejectingPostHandshakeServer = ( + WebDriverBiDiWebSocketEstablished, + thread::JoinHandle>, +); fn semantic_observation_proof() -> Result> { protocol_proof(BrowserProtocolCapability::SemanticObservation) @@ -122,13 +126,8 @@ fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { Ok(()) } -fn establish_rejecting_post_handshake_bytes() -> Result< - ( - WebDriverBiDiWebSocketEstablished, - thread::JoinHandle>, - ), - Box, -> { +fn establish_rejecting_post_handshake_bytes() +-> Result> { let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; let server = thread::spawn(move || -> io::Result<()> { From 1e530cdddc0862f88987a2a6a713f7332f5058a5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 1 Sep 2026 06:01:54 +0900 Subject: [PATCH 55/67] style(network): apply canonical rustfmt to pointer authority test --- .../tests/webdriver_bidi_pointer_click_send_authority.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs index 90f2edaea..10be4c76d 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_authority.rs @@ -126,8 +126,8 @@ fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { Ok(()) } -fn establish_rejecting_post_handshake_bytes() --> Result> { +fn establish_rejecting_post_handshake_bytes() -> Result> +{ let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; let server = thread::spawn(move || -> io::Result<()> { From 3f9cdc1a2271d44352442aaf2be483301e7b84ea Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 20:59:37 +0900 Subject: [PATCH 56/67] test(network): expose pointer preflight correlation leakage Commit-Message-Assisted-by: Codex (via Codex) Signed-off-by: Seongho Bae --- ...driver_bidi_pointer_click_send_failures.rs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs index 4ff374af3..668d17aa0 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs @@ -133,7 +133,23 @@ fn establish_with_handshake_only_server() -> Result io::Result<()> { let (mut stream, _) = listener.accept()?; read_opening_request(&mut stream)?; - stream.write_all(OPENING_RESPONSE) + stream.write_all(OPENING_RESPONSE)?; + let mut byte = [0_u8; 1]; + match stream.read(&mut byte) { + Ok(0) => Ok(()), + Err(error) + if matches!( + error.kind(), + io::ErrorKind::ConnectionReset | io::ErrorKind::ConnectionAborted + ) => + { + Ok(()) + } + Ok(_) => Err(io::Error::other( + "rejected pointer command emitted wire bytes", + )), + Err(error) => Err(error), + } }); let endpoint = format!("ws://{local_addr}/session/{SESSION_ID}"); @@ -273,7 +289,7 @@ fn pointer_click_rejects_duplicate_correlation_before_frame_write() -> Result<() } #[test] -fn pointer_click_preserves_registration_when_frame_timeout_is_invalid() -> Result<(), Box> +fn pointer_click_invalid_timeout_leaves_no_correlation_or_wire_bytes() -> Result<(), Box> { let (established, server) = establish_with_handshake_only_server()?; let mut correlation = WebDriverBiDiCommandCorrelation::new(); @@ -302,10 +318,9 @@ fn pointer_click_preserves_registration_when_frame_timeout_is_invalid() -> Resul "WebDriver BiDi pointer-click command frame write failed" ); assert!(error.source().is_some()); - assert_eq!(correlation.outstanding_count(), 1); - server .join() .map_err(|_| io::Error::other("invalid-timeout pointer server panicked"))??; + assert_eq!(correlation.outstanding_count(), 0); Ok(()) } From 35555d0f8491d4ee95c2e61d1a2aaa2c02a0635c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 21:09:43 +0900 Subject: [PATCH 57/67] docs: record pointer authority integration boundaries Commit-Message-Assisted-by: Codex (via Codex) Signed-off-by: Seongho Bae --- CHANGELOG.md | 4 +++- .../0107-browser-protocol-adapter-strategy.md | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c742a4ec7..c98aaa8f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Changed +- Recheck that a click still targets the admitted node in the current document before sending it. Invalid deadlines send nothing and reserve no pending request; uncertain writes remain pending instead of being treated as safe to retry. + - Keep subscription shutdown on its original connection and reject replies from another connection without losing the pending request. Beginning shutdown ends local event admission; a failed shutdown requires a new subscription before admission resumes. - Prevent an earlier browser reply from completing a later request that reuses its number. Typed browser requests now use increasing numbers on each connection, and low-level protocol traffic uses a separate connection. @@ -151,4 +153,4 @@ All notable changes to OriginWeave are documented in this file. The format follo - The hourly product agent has no Git metadata or repository authority. A separate post-verification publisher opens one PR and cannot approve or merge it. - The unprivileged OpenCode user is restricted to loopback egress during model execution, preventing runner-wide allow-listed endpoints from becoming direct source-exfiltration channels. -[Unreleased]: https://github.com/ContextualWisdomLab/OriginWeave/compare/main...HEAD \ No newline at end of file +[Unreleased]: https://github.com/ContextualWisdomLab/OriginWeave/compare/main...HEAD diff --git a/docs/adr/0107-browser-protocol-adapter-strategy.md b/docs/adr/0107-browser-protocol-adapter-strategy.md index f131ff3c3..746ca9804 100644 --- a/docs/adr/0107-browser-protocol-adapter-strategy.md +++ b/docs/adr/0107-browser-protocol-adapter-strategy.md @@ -36,6 +36,27 @@ MCP version negotiation is independent of the OriginWeave Protocol version. As o ## Consequences +### Proposed refinement: immediate pointer authority with shared dispatch (2026-09-06) + +In the context of integrating admitted-node clicks with the navigation-subscription prerequisite, +facing stale node authority and an invalid deadline leaving an unsent command pending, we decided +for immediate node revalidation followed by the existing typed dispatch boundary, and against +reusable prevalidated commands or a second registry-identity allocation, to preserve both node +provenance and exact pending-command behavior, accepting that callers must supply live registry +state and a fresh typed-input proof for every attempt. + +The node handle shares the core registry's existing identity allocation; its exact external-node +mapping and lifecycle invalidation remain private. Zero and excessive deadlines fail before +registration, proven no-write frame failures retire only the new PointerClick entry, and ambiguous +writes retain it. This does not authenticate the browser, authorize the action, bind pointer replies +to a connection, or prove that the click caused navigation. + +The socket regression at `3f9cdc1a` observed one outstanding command after a zero deadline. Its +successor at integration commit `7535d8af` observes none and requires no command bytes on the peer. +The integration also restores four parent navigation-postcondition regressions and retains both +mask-reuse rejection and ambiguous-write socket tests. Status remains Proposed; hosted checks, +protected-main delivery and fresh visual inspection are separate gates. + ### Proposed refinement: consuming subscription teardown (2026-09-06) In the context of ending a navigation subscription, facing a borrowed receipt that permits continued From 98621adf2022f2a93e62ee6207e09a65d37789f4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 22:25:59 +0900 Subject: [PATCH 58/67] test(network): reproduce foreign session before parent adoption Commit-Message-Assisted-by: Codex (via Codex) Signed-off-by: Seongho Bae --- ...scription_registry_transport_provenance.rs | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 crates/originweave-network/tests/webdriver_bidi_navigation_subscription_registry_transport_provenance.rs diff --git a/crates/originweave-network/tests/webdriver_bidi_navigation_subscription_registry_transport_provenance.rs b/crates/originweave-network/tests/webdriver_bidi_navigation_subscription_registry_transport_provenance.rs new file mode 100644 index 000000000..a69560b12 --- /dev/null +++ b/crates/originweave-network/tests/webdriver_bidi_navigation_subscription_registry_transport_provenance.rs @@ -0,0 +1,123 @@ +use std::{ + error::Error, + io::{self, Read, Write}, + net::{SocketAddr, TcpListener, TcpStream}, + thread, + time::Duration, +}; + +use originweave_core::{BrowserAuthorityRegistry, WebDriverBiDiWebSocketEndpoint}; +use originweave_network::{ + WebDriverBiDiCommandCorrelation, WebDriverBiDiNavigationCommittedSubscriptionCommand, + WebDriverBiDiTcpConnectionPlan, WebDriverBiDiWebSocketClientKey, + WebDriverBiDiWebSocketEstablished, WebDriverBiDiWebSocketHandshakePlan, + WebDriverBiDiWebSocketMaskKey, +}; + +const REGISTRY_SESSION_ID: &str = "01234567-89ab-cdef-0123-456789abcdef"; +const FOREIGN_TRANSPORT_SESSION_ID: &str = "fedcba98-7654-3210-fedc-ba9876543210"; +const CONTEXT_ID: &str = "context-a"; +const RFC6455_SAMPLE_KEY: &str = "dGhlIHNhbXBsZSBub25jZQ=="; +const OPENING_RESPONSE: &[u8] = b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"; + +fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { + stream.set_read_timeout(Some(Duration::from_secs(2)))?; + let mut request = Vec::new(); + let mut buffer = [0_u8; 512]; + while !request.ends_with(b"\r\n\r\n") { + let count = stream.read(&mut buffer)?; + if count == 0 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "opening request ended before the header terminator", + )); + } + request.extend_from_slice(&buffer[..count]); + } + Ok(()) +} + +fn spawn_foreign_transport_server(listener: TcpListener) -> thread::JoinHandle> { + thread::spawn(move || { + let (mut stream, _) = listener.accept()?; + read_opening_request(&mut stream)?; + stream.write_all(OPENING_RESPONSE)?; + stream.set_read_timeout(Some(Duration::from_millis(500)))?; + + let mut first_command_byte = [0_u8; 1]; + match stream.read(&mut first_command_byte) { + Ok(0) => Ok(false), + Ok(_) => Ok(true), + Err(source) + if matches!( + source.kind(), + io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut + ) => + { + Ok(false) + } + Err(source) => Err(source), + } + }) +} + +fn establish( + local_addr: SocketAddr, + session_id: &str, +) -> Result> { + let endpoint = format!("ws://{local_addr}/session/{session_id}"); + let target = WebDriverBiDiWebSocketEndpoint::new(&endpoint)? + .correlate_session_id(session_id)? + .into_explicit_connect_target()?; + let connection = + WebDriverBiDiTcpConnectionPlan::new(target, Duration::from_secs(1), 1)?.connect()?; + Ok(WebDriverBiDiWebSocketHandshakePlan::new( + connection, + WebDriverBiDiWebSocketClientKey::new(RFC6455_SAMPLE_KEY)?, + )? + .write_opening_request(Duration::from_millis(500))? + .read_opening_response(Duration::from_millis(500))?) +} + +#[test] +fn registry_bound_subscription_is_rejected_before_writing_to_a_foreign_session_transport() +-> Result<(), Box> { + let mut registry = BrowserAuthorityRegistry::new(); + let session = registry.register_session(REGISTRY_SESSION_ID)?; + let context = registry.register_context(session, CONTEXT_ID)?; + let command = WebDriverBiDiNavigationCommittedSubscriptionCommand::new( + 7, ®istry, session, context, CONTEXT_ID, + )?; + + let listener = TcpListener::bind(("127.0.0.1", 0))?; + let local_addr = listener.local_addr()?; + let server = spawn_foreign_transport_server(listener); + let established = establish(local_addr, FOREIGN_TRANSPORT_SESSION_ID)?; + let mut correlation = WebDriverBiDiCommandCorrelation::new(); + + let send_result = command.send( + ®istry, + established, + &mut correlation, + WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), + Duration::from_millis(500), + ); + let command_byte_seen = server + .join() + .map_err(|_| io::Error::other("foreign-session fixture server panicked"))??; + + assert!( + send_result.is_err(), + "registry session A unexpectedly dispatched on transport session B" + ); + assert_eq!( + correlation.outstanding_count(), + 0, + "foreign-session rejection must happen before correlation registration" + ); + assert!( + !command_byte_seen, + "foreign-session rejection must happen before any command-frame byte" + ); + Ok(()) +} From 6dfc75bfda0146d8988427a75cac8875b87cc75a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:11:53 +0900 Subject: [PATCH 59/67] test: reject pointer dispatch on foreign BiDi session --- ...nter_click_transport_session_provenance.rs | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs new file mode 100644 index 000000000..758237491 --- /dev/null +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs @@ -0,0 +1,173 @@ +use std::{ + error::Error, + io::{self, Read, Write}, + net::{TcpListener, TcpStream}, + thread, + time::Duration, +}; + +use originweave_core::{ + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, Origin, OriginWeaveProtocolVersion, + ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, + WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, +}; +use originweave_network::{ + WebDriverBiDiCommandCorrelation, WebDriverBiDiTcpConnectionPlan, + WebDriverBiDiWebSocketClientKey, WebDriverBiDiWebSocketHandshakePlan, + WebDriverBiDiWebSocketMaskKey, send_webdriver_bidi_pointer_click, +}; + +const REGISTRY_SESSION_ID: &str = "01234567-89ab-cdef-0123-456789abcdef"; +const FOREIGN_TRANSPORT_SESSION_ID: &str = "fedcba98-7654-3210-fedc-ba9876543210"; +const RFC6455_SAMPLE_KEY: &str = "dGhlIHNhbXBsZSBub25jZQ=="; +const OPENING_RESPONSE: &[u8] = b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"; +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +fn protocol_proof( + capability: BrowserProtocolCapability, +) -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[capability], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + capability, + )?) +} + +fn current_node_fixture( +) -> Result<(BrowserAuthorityRegistry, AdmittedNodeHandle, WebDriverBiDiRemoteNodeReference), Box> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session(REGISTRY_SESSION_ID)?; + let browsing_context = registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://app.example") + .map_err(|error| io::Error::other(format!("fixture origin rejected: {error:?}")))?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let locate = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handle = locate + .bind_response_document_nodes( + document, + protocol_proof(BrowserProtocolCapability::SemanticObservation)?, + &mut registry, + target, + )? + .into_iter() + .next() + .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + Ok((registry, handle, remote)) +} + +fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { + stream.set_read_timeout(Some(Duration::from_secs(2)))?; + let mut request = Vec::new(); + let mut buffer = [0_u8; 512]; + while !request.ends_with(b"\r\n\r\n") { + let count = stream.read(&mut buffer)?; + if count == 0 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "opening request ended before the header terminator", + )); + } + request.extend_from_slice(&buffer[..count]); + } + Ok(()) +} + +#[test] +fn current_node_pointer_click_is_rejected_before_writing_to_a_foreign_session_transport( +) -> Result<(), Box> { + let (registry, handle, remote) = current_node_fixture()?; + let listener = TcpListener::bind(("127.0.0.1", 0))?; + let local_addr = listener.local_addr()?; + let server = thread::spawn(move || -> io::Result { + let (mut stream, _) = listener.accept()?; + read_opening_request(&mut stream)?; + stream.write_all(OPENING_RESPONSE)?; + stream.set_read_timeout(Some(Duration::from_millis(500)))?; + let mut first_command_byte = [0_u8; 1]; + match stream.read(&mut first_command_byte) { + Ok(0) => Ok(false), + Ok(_) => Ok(true), + Err(source) + if matches!( + source.kind(), + io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut + ) => Ok(false), + Err(source) => Err(source), + } + }); + + let endpoint = format!("ws://{local_addr}/session/{FOREIGN_TRANSPORT_SESSION_ID}"); + let target = WebDriverBiDiWebSocketEndpoint::new(&endpoint)? + .correlate_session_id(FOREIGN_TRANSPORT_SESSION_ID)? + .into_explicit_connect_target()?; + let connection = + WebDriverBiDiTcpConnectionPlan::new(target, Duration::from_secs(1), 1)?.connect()?; + let established = WebDriverBiDiWebSocketHandshakePlan::new( + connection, + WebDriverBiDiWebSocketClientKey::new(RFC6455_SAMPLE_KEY)?, + )? + .write_opening_request(Duration::from_millis(500))? + .read_opening_response(Duration::from_millis(500))?; + let mut correlation = WebDriverBiDiCommandCorrelation::new(); + + let send_result = send_webdriver_bidi_pointer_click( + protocol_proof(BrowserProtocolCapability::TypedInput)?, + 42, + "context-a", + &handle, + &remote, + ®istry, + established, + &mut correlation, + WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), + Duration::from_millis(500), + ); + let command_byte_seen = server + .join() + .map_err(|_| io::Error::other("foreign-session pointer server panicked"))??; + + assert!( + send_result.is_err(), + "registry session A unexpectedly dispatched pointer input on transport session B" + ); + assert_eq!( + correlation.outstanding_count(), + 0, + "foreign-session rejection must happen before correlation registration" + ); + assert!( + !command_byte_seen, + "foreign-session rejection must happen before any pointer command-frame byte" + ); + Ok(()) +} From 9d61f9fa9b42259e528dc667509e486d14bc3389 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 02:03:33 +0900 Subject: [PATCH 60/67] test: format pointer session provenance regression --- ...nter_click_transport_session_provenance.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs index 758237491..af1e37287 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs @@ -51,8 +51,14 @@ fn protocol_proof( )?) } -fn current_node_fixture( -) -> Result<(BrowserAuthorityRegistry, AdmittedNodeHandle, WebDriverBiDiRemoteNodeReference), Box> { +fn current_node_fixture() -> Result< + ( + BrowserAuthorityRegistry, + AdmittedNodeHandle, + WebDriverBiDiRemoteNodeReference, + ), + Box, +> { let mut registry = BrowserAuthorityRegistry::new(); let browser_session = registry.register_session(REGISTRY_SESSION_ID)?; let browsing_context = registry.register_context(browser_session, "context-a")?; @@ -103,8 +109,8 @@ fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { } #[test] -fn current_node_pointer_click_is_rejected_before_writing_to_a_foreign_session_transport( -) -> Result<(), Box> { +fn current_node_pointer_click_is_rejected_before_writing_to_a_foreign_session_transport() +-> Result<(), Box> { let (registry, handle, remote) = current_node_fixture()?; let listener = TcpListener::bind(("127.0.0.1", 0))?; let local_addr = listener.local_addr()?; @@ -121,7 +127,10 @@ fn current_node_pointer_click_is_rejected_before_writing_to_a_foreign_session_tr if matches!( source.kind(), io::ErrorKind::WouldBlock | io::ErrorKind::TimedOut - ) => Ok(false), + ) => + { + Ok(false) + } Err(source) => Err(source), } }); From 6c61563884799f74c98e8700b2ca087672986cbb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 02:04:21 +0900 Subject: [PATCH 61/67] fix: bind pointer dispatch to verified BiDi session --- .../src/webdriver_bidi_pointer_click_transport.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs index bb0cb7358..efa5ce88f 100644 --- a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs +++ b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs @@ -80,7 +80,9 @@ impl Error for WebDriverBiDiPointerClickSendError { /// check rejects stale document epochs, cross-registry handles, changed origins, mismatched external /// contexts, and unadmitted wire node identifiers before any command identifier is registered or /// any action frame is written. A previously constructed command therefore cannot outlive its node -/// authority and later bypass revalidation at transport time. +/// authority and later bypass revalidation at transport time. The established transport's verified +/// protocol session must also match the registry's canonical external session mapping before +/// correlation or I/O; this comparison does not authenticate the browser process. /// /// Invalid local frame deadlines fail before registration. Correlation then occurs before the first /// possible remote side effect. A frame preflight rejection that proves no write began retires the @@ -134,6 +136,14 @@ pub fn send_webdriver_bidi_pointer_click( if frame_timeout > MAX_WEBSOCKET_FRAME_TIMEOUT { return Err(invalid_frame_timeout(frame_timeout)); } + registry + .require_registered_session_external_identifier( + handle.browser_session(), + established.transport_evidence().verified_peer().session_id(), + ) + .map_err(|source| WebDriverBiDiPointerClickSendError::Authority { + source: WebDriverBiDiPointerClickAuthorityError::BrowserAuthority(source), + })?; match correlation .register_command_for(command.command_id(), WebDriverBiDiCommandKind::PointerClick) { From 6eb49d5c9ce922f993b5e30fde2f61b6ced31571 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 02:08:09 +0900 Subject: [PATCH 62/67] test: align pointer transport fixture session authority --- .../tests/webdriver_bidi_pointer_click_send.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs index 168381800..cd9e6f1ca 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send.rs @@ -77,7 +77,7 @@ fn typed_input_proof() -> Result> { fn admitted_pointer_click_fixture() -> Result> { let mut registry = BrowserAuthorityRegistry::new(); - let browser_session = registry.register_session("webdriver-session")?; + let browser_session = registry.register_session(SESSION_ID)?; let browsing_context = registry.register_context(browser_session, "context-a")?; let origin = Origin::parse("https://app.example").map_err(|error| { io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) From dcd2fdcbbb3d6299ef1bceb79117ae69c320874d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 02:08:54 +0900 Subject: [PATCH 63/67] test: align pointer failure fixture session authority --- .../tests/webdriver_bidi_pointer_click_send_failures.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs index 7dfb50130..7de7396fb 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_send_failures.rs @@ -79,7 +79,7 @@ fn typed_input_proof() -> Result> { fn pointer_click_fixture() -> Result> { let mut registry = BrowserAuthorityRegistry::new(); - let browser_session = registry.register_session("webdriver-session")?; + let browser_session = registry.register_session(SESSION_ID)?; let browsing_context = registry.register_context(browser_session, "context-a")?; let origin = Origin::parse("https://app.example").map_err(|error| { io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) From fd2da4689a6e043325dafb5850573ec72e98b18f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 02:10:36 +0900 Subject: [PATCH 64/67] test: require typed pointer session-provenance denial --- ...nter_click_transport_session_provenance.rs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs index af1e37287..ce4e79f0c 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs @@ -12,12 +12,14 @@ use originweave_core::{ BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, BrowserProtocolCapability, BrowserProtocolKind, Origin, OriginWeaveProtocolVersion, ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, - WebDriverBiDiRemoteNodeReference, WebDriverBiDiWebSocketEndpoint, + WebDriverBiDiPointerClickAuthorityError, WebDriverBiDiRemoteNodeReference, + WebDriverBiDiWebSocketEndpoint, }; use originweave_network::{ - WebDriverBiDiCommandCorrelation, WebDriverBiDiTcpConnectionPlan, - WebDriverBiDiWebSocketClientKey, WebDriverBiDiWebSocketHandshakePlan, - WebDriverBiDiWebSocketMaskKey, send_webdriver_bidi_pointer_click, + WebDriverBiDiCommandCorrelation, WebDriverBiDiPointerClickSendError, + WebDriverBiDiTcpConnectionPlan, WebDriverBiDiWebSocketClientKey, + WebDriverBiDiWebSocketHandshakePlan, WebDriverBiDiWebSocketMaskKey, + send_webdriver_bidi_pointer_click, }; const REGISTRY_SESSION_ID: &str = "01234567-89ab-cdef-0123-456789abcdef"; @@ -165,10 +167,15 @@ fn current_node_pointer_click_is_rejected_before_writing_to_a_foreign_session_tr .join() .map_err(|_| io::Error::other("foreign-session pointer server panicked"))??; - assert!( - send_result.is_err(), - "registry session A unexpectedly dispatched pointer input on transport session B" - ); + let error = send_result + .err() + .ok_or_else(|| io::Error::other("registry session A unexpectedly dispatched pointer input on transport session B"))?; + assert!(matches!( + error, + WebDriverBiDiPointerClickSendError::Authority { + source: WebDriverBiDiPointerClickAuthorityError::BrowserAuthority(_) + } + )); assert_eq!( correlation.outstanding_count(), 0, From ddce72484d707e0945fe715602272ff2df6a88d0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 03:04:43 +0900 Subject: [PATCH 65/67] test: apply canonical pointer transport formatting --- .../src/webdriver_bidi_pointer_click_transport.rs | 5 ++++- ...ver_bidi_pointer_click_transport_session_provenance.rs | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs index efa5ce88f..ada4c04d9 100644 --- a/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs +++ b/crates/originweave-network/src/webdriver_bidi_pointer_click_transport.rs @@ -139,7 +139,10 @@ pub fn send_webdriver_bidi_pointer_click( registry .require_registered_session_external_identifier( handle.browser_session(), - established.transport_evidence().verified_peer().session_id(), + established + .transport_evidence() + .verified_peer() + .session_id(), ) .map_err(|source| WebDriverBiDiPointerClickSendError::Authority { source: WebDriverBiDiPointerClickAuthorityError::BrowserAuthority(source), diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs index ce4e79f0c..192c9a1d5 100644 --- a/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_transport_session_provenance.rs @@ -167,9 +167,11 @@ fn current_node_pointer_click_is_rejected_before_writing_to_a_foreign_session_tr .join() .map_err(|_| io::Error::other("foreign-session pointer server panicked"))??; - let error = send_result - .err() - .ok_or_else(|| io::Error::other("registry session A unexpectedly dispatched pointer input on transport session B"))?; + let error = send_result.err().ok_or_else(|| { + io::Error::other( + "registry session A unexpectedly dispatched pointer input on transport session B", + ) + })?; assert!(matches!( error, WebDriverBiDiPointerClickSendError::Authority { From e7fb1527768cb1539358c6b8361ecd72594645a0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 08:38:07 +0900 Subject: [PATCH 66/67] test(network): replay replacement pointer replies with admitted node authority --- ...er_click_response_connection_provenance.rs | 295 ++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 crates/originweave-network/tests/webdriver_bidi_pointer_click_response_connection_provenance.rs diff --git a/crates/originweave-network/tests/webdriver_bidi_pointer_click_response_connection_provenance.rs b/crates/originweave-network/tests/webdriver_bidi_pointer_click_response_connection_provenance.rs new file mode 100644 index 000000000..976f2f170 --- /dev/null +++ b/crates/originweave-network/tests/webdriver_bidi_pointer_click_response_connection_provenance.rs @@ -0,0 +1,295 @@ +use std::{ + error::Error, + io::{self, Read, Write}, + net::{SocketAddr, TcpListener, TcpStream}, + thread, + time::Duration, +}; + +use originweave_core::{ + AdmittedNodeHandle, BoundedWebDriverBiDiResponseDocument, BrowserAuthorityRegistry, + BrowserContextDispatchTarget, BrowserContextOriginDispatchTarget, + BrowserContextOriginEpochDispatchTarget, BrowserProtocolAdapterDescriptor, + BrowserProtocolCapability, BrowserProtocolKind, Origin, OriginWeaveProtocolVersion, + ValidatedBrowserProtocolUse, WebDriverBiDiAccessibilityQuery, WebDriverBiDiLocateNodesCommand, + WebDriverBiDiPointerClickCommand, WebDriverBiDiRemoteNodeReference, + WebDriverBiDiWebSocketEndpoint, +}; +use originweave_network::{ + WebDriverBiDiCommandCorrelation, WebDriverBiDiCommandCorrelationError, + WebDriverBiDiCommandKind, WebDriverBiDiPointerClickResponseError, + WebDriverBiDiPointerClickResult, WebDriverBiDiTcpConnectionPlan, + WebDriverBiDiWebSocketClientKey, WebDriverBiDiWebSocketEstablished, + WebDriverBiDiWebSocketHandshakePlan, WebDriverBiDiWebSocketMaskKey, + WebDriverBiDiWebSocketMessageAssembler, WebDriverBiDiWebSocketMessageAssembly, + WebDriverBiDiWebSocketTextMessage, send_webdriver_bidi_pointer_click, +}; + +const SESSION_ID: &str = "01234567-89ab-cdef-0123-456789abcdef"; +const RFC6455_SAMPLE_KEY: &str = "dGhlIHNhbXBsZSBub25jZQ=="; +const OPENING_RESPONSE: &[u8] = b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n"; +const CLICK_SUCCESS_RESPONSE: &[u8] = + br#"{"type":"success","id":42,"result":{"vendorExtension":{"observed":false}}}"#; +const CLICK_ERROR_RESPONSE: &[u8] = + br#"{"type":"error","id":42,"error":"invalid argument","message":"blocked","stacktrace":"remote"}"#; + +const ORIGINWEAVE_PROTOCOL_VERSION: OriginWeaveProtocolVersion = + OriginWeaveProtocolVersion::new(0, 1); +const ADAPTER_VERSION: &str = "originweave-bidi-v1"; +const PROTOCOL_REVISION: &str = "webdriver-bidi-wd-2026-06-01"; +const BROWSER_REVISION: &str = "chromium-r1639810"; + +type AdmittedPointerClickFixture = ( + BrowserAuthorityRegistry, + AdmittedNodeHandle, + WebDriverBiDiRemoteNodeReference, +); + +fn semantic_observation_proof() -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::SemanticObservation], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::SemanticObservation, + )?) +} + +fn typed_input_proof() -> Result> { + let descriptor = BrowserProtocolAdapterDescriptor::new( + BrowserProtocolKind::WebDriverBiDi, + ORIGINWEAVE_PROTOCOL_VERSION, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + &[BrowserProtocolCapability::TypedInput], + )?; + Ok(descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::WebDriverBiDi, + ADAPTER_VERSION, + PROTOCOL_REVISION, + BROWSER_REVISION, + BrowserProtocolCapability::TypedInput, + )?) +} + +fn admitted_pointer_click_fixture() -> Result> { + let mut registry = BrowserAuthorityRegistry::new(); + let browser_session = registry.register_session(SESSION_ID)?; + let browsing_context = registry.register_context(browser_session, "context-a")?; + let origin = Origin::parse("https://app.example").map_err(|error| { + io::Error::other(format!("fixture origin rejected unexpectedly: {error:?}")) + })?; + let epoch = registry.bind_context_origin(browser_session, browsing_context, &origin)?; + let target = BrowserContextOriginEpochDispatchTarget::new( + BrowserContextOriginDispatchTarget::new( + BrowserContextDispatchTarget::new(browser_session, browsing_context), + &origin, + ), + epoch, + ); + let query = WebDriverBiDiAccessibilityQuery::new(Some("button"), Some("Submit task"), 1)?; + let locate = WebDriverBiDiLocateNodesCommand::new(41, "context-a", &query)?; + let document = BoundedWebDriverBiDiResponseDocument::new( + r#"{"type":"success","id":41,"result":{"nodes":[{"type":"node","sharedId":"shared-node-42"}]}}"#, + )?; + let handle = locate + .bind_response_document_nodes( + document, + semantic_observation_proof()?, + &mut registry, + target, + )? + .into_iter() + .next() + .ok_or_else(|| io::Error::other("locateNodes fixture did not bind its node"))?; + let remote = WebDriverBiDiRemoteNodeReference::new("node", Some("shared-node-42"))?; + Ok((registry, handle, remote)) +} + +fn read_opening_request(stream: &mut TcpStream) -> io::Result<()> { + stream.set_read_timeout(Some(Duration::from_secs(2)))?; + let mut request = Vec::new(); + let mut buffer = [0_u8; 512]; + while !request.ends_with(b"\r\n\r\n") { + let count = stream.read(&mut buffer)?; + if count == 0 { + return Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "client opening request ended before the header terminator", + )); + } + request.extend_from_slice(&buffer[..count]); + } + Ok(()) +} + +fn read_masked_text_frame(stream: &mut TcpStream) -> io::Result> { + let mut header = [0_u8; 2]; + stream.read_exact(&mut header)?; + if header[0] != 0x81 || header[1] & 0x80 == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "expected one final masked client text frame", + )); + } + let marker = header[1] & 0x7f; + let length = match marker { + 0..=125 => usize::from(marker), + 126 => { + let mut extended = [0_u8; 2]; + stream.read_exact(&mut extended)?; + usize::from(u16::from_be_bytes(extended)) + } + 127 => { + let mut extended = [0_u8; 8]; + stream.read_exact(&mut extended)?; + let length = u64::from_be_bytes(extended); + usize::try_from(length).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + "pointer frame length exceeds usize", + ) + })? + } + _ => unreachable!(), + }; + let mut mask = [0_u8; 4]; + stream.read_exact(&mut mask)?; + let mut payload = vec![0_u8; length]; + stream.read_exact(&mut payload)?; + for (index, byte) in payload.iter_mut().enumerate() { + *byte ^= mask[index % mask.len()]; + } + Ok(payload) +} + +fn establish(local_addr: SocketAddr) -> Result> { + let endpoint = format!("ws://{local_addr}/session/{SESSION_ID}"); + let target = WebDriverBiDiWebSocketEndpoint::new(&endpoint)? + .correlate_session_id(SESSION_ID)? + .into_explicit_connect_target()?; + let connection = + WebDriverBiDiTcpConnectionPlan::new(target, Duration::from_secs(1), 1)?.connect()?; + Ok(WebDriverBiDiWebSocketHandshakePlan::new( + connection, + WebDriverBiDiWebSocketClientKey::new(RFC6455_SAMPLE_KEY)?, + )? + .write_opening_request(Duration::from_millis(500))? + .read_opening_response(Duration::from_millis(500))?) +} + +fn read_response( + established: WebDriverBiDiWebSocketEstablished, +) -> Result> { + let (_established, frame) = established.read_frame(Duration::from_millis(500))?; + let mut assembler = WebDriverBiDiWebSocketMessageAssembler::new(); + let text = match assembler.push_frame(frame)? { + WebDriverBiDiWebSocketMessageAssembly::Text(text) => text, + other => { + return Err(io::Error::other(format!( + "replacement pointer connection produced unexpected assembly state: {other:?}" + )) + .into()); + } + }; + Ok(text) +} + +fn assert_replacement_rejected(foreign_response: &'static [u8]) -> Result<(), Box> { + let original_listener = TcpListener::bind(("127.0.0.1", 0))?; + let original_addr = original_listener.local_addr()?; + let (registry, handle, remote) = admitted_pointer_click_fixture()?; + let expected = WebDriverBiDiPointerClickCommand::new_for_current_node( + 42, + "context-a", + &handle, + &remote, + ®istry, + )?; + let expected_json = expected.as_json().as_bytes().to_vec(); + let original_server = thread::spawn(move || -> io::Result<()> { + let (mut stream, _) = original_listener.accept()?; + read_opening_request(&mut stream)?; + stream.write_all(OPENING_RESPONSE)?; + let command = read_masked_text_frame(&mut stream)?; + if command != expected_json { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "unexpected pointer command on original connection", + )); + } + let (mut replacement, _) = original_listener.accept()?; + read_opening_request(&mut replacement)?; + replacement.write_all(OPENING_RESPONSE)?; + replacement.write_all(&[0x81, foreign_response.len() as u8])?; + replacement.write_all(foreign_response)?; + stream.write_all(&[0x81, CLICK_SUCCESS_RESPONSE.len() as u8])?; + stream.write_all(CLICK_SUCCESS_RESPONSE) + }); + + let original = establish(original_addr)?; + let mut correlation = WebDriverBiDiCommandCorrelation::new(); + correlation.register_command_for(43, WebDriverBiDiCommandKind::SessionStatus)?; + let original = send_webdriver_bidi_pointer_click( + typed_input_proof()?, + 42, + "context-a", + &handle, + &remote, + ®istry, + original, + &mut correlation, + WebDriverBiDiWebSocketMaskKey::new([1, 2, 3, 4]), + Duration::from_millis(500), + )?; + assert_eq!(correlation.outstanding_count(), 2); + + let replacement_response = read_response(establish(original_addr)?)?; + let parsed = WebDriverBiDiPointerClickResult::parse_and_correlate( + &replacement_response, + &mut correlation, + ); + + let original_response = read_response(original)?; + original_server + .join() + .map_err(|_| io::Error::other("original pointer server panicked"))??; + assert!( + matches!( + parsed, + Err(WebDriverBiDiPointerClickResponseError::Correlation { + source: WebDriverBiDiCommandCorrelationError::ResponseConnectionMismatch { + command_id: 42 + } + }) + ), + "replacement response must fail for exact connection mismatch: {parsed:?}" + ); + assert_eq!(correlation.outstanding_count(), 2); + let accepted = + WebDriverBiDiPointerClickResult::parse_and_correlate(&original_response, &mut correlation)?; + assert_eq!(accepted.command_id(), 42); + assert_eq!(correlation.outstanding_count(), 1); + Ok(()) +} + +#[test] +fn replacement_success_cannot_consume_original_pointer_command() -> Result<(), Box> { + assert_replacement_rejected(CLICK_SUCCESS_RESPONSE) +} + +#[test] +fn replacement_error_cannot_consume_original_pointer_command() -> Result<(), Box> { + assert_replacement_rejected(CLICK_ERROR_RESPONSE) +} From e94a2372fe3771f9ddf70291d34fc9a7e4770ec9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 08:40:58 +0900 Subject: [PATCH 67/67] docs: record combined pointer authority and reply safeguards --- CHANGELOG.md | 1 + docs/doctoring.md | 22 +++++++++++++++++++ .../action-postcondition-evidence.md | 17 ++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8729c0f1..2c5fb4322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to OriginWeave are documented in this file. The format follo ### Changed +- Keep current-node and browser-session click checks when rejecting replies from replacement connections; the original request remains recoverable without consuming unrelated work. - Recheck that a click still targets the admitted node in the current document before sending it. Invalid deadlines send nothing and reserve no pending request; uncertain writes remain pending instead of being treated as safe to retry. - Reject replacement-connection click replies while retaining increasing request numbers, original subscription ownership and same-connection shutdown checks. diff --git a/docs/doctoring.md b/docs/doctoring.md index 42018a593..120ea4041 100644 --- a/docs/doctoring.md +++ b/docs/doctoring.md @@ -4,6 +4,28 @@ This document records external evidence that changes OriginWeave architecture, t ## Decision trace +### Pointer outbound authority and received-reply integration + +Child #265 at `ddce7248` already checked the admitted node and canonical registry session +before pointer dispatch. Regression `e7fb1527` nevertheless reproduced both replacement +success and error replies consuming its original pending request (0/2 passing), using +the same listener and external session. The original server was joined before the +decisive mismatch assertion; unrelated pending work and genuine-reply recovery remain +part of each case. + +Ordinary parent adoption `d847b530` retains that child authority guard and typed monotonic +dispatch while adding #264 `43395711` connection registration and sealed reply consumption. +It reuses the existing connection identity and message reader, with no new dependency, +raw-response fallback or duplicate authority owner. The 14 focused pointer, outbound +session and navigation-postcondition tests pass. Full combined-head verification remains +required; predecessor coverage and screenshots are not transferable. + +This implements the existing reply-provenance invariant, not a new accepted architectural +decision. Node/session matching and a correctly correlated reply still do not authenticate +Chromium, authorize policy, prove that an observed navigation was caused by the click, +or establish protected-main and release acceptance. Parent-only historical deferrals below +do not remove the stronger safeguards already present in this child. + ### Subscription registry-to-transport session provenance The real-loopback test at `b4702cd5`, executed locally before repair, dispatched a subscription diff --git a/docs/traceability/action-postcondition-evidence.md b/docs/traceability/action-postcondition-evidence.md index 061f77680..42ab72f7d 100644 --- a/docs/traceability/action-postcondition-evidence.md +++ b/docs/traceability/action-postcondition-evidence.md @@ -152,3 +152,20 @@ This dossier does **not** close issue #28. Material remaining work includes: ## 7. Documentation fitness consequence The ADR/PRD/TRD/Architecture/UML/ERD graph remains **DESIGN-SUFFICIENT / PROTECTED-MAIN-PARTIAL**. PR #64 narrows a typed evidence gap already governed by existing provenance/action-success decisions, while PR #65 supplies controlled test infrastructure for the eventual real-browser proof. Neither introduces a new trust domain, deployed component, persistence owner, database schema, or independent architecture decision, so a new ADR or physical ERD entity would overstate the implementation. Detailed real-Chromium dispatch/post-condition sequence diagrams should be reconciled when the executable adapter chain stabilizes rather than manufacturing as-built detail before that runtime exists. + +## 8. Pointer descendant reply integration + +The #265 integration at `d847b530` preserves current-node and outbound-session checks +from `ddce7248`, then adopts parent #264 `43395711` reply provenance. The real socket +regression `e7fb1527` first reproduced replacement success and error consuming the +original click request. Both now reject the foreign connection while retaining the +original request and unrelated work; the genuine original reply still completes. + +All 14 focused tests pass, including stale-node rejection, foreign-session rejection +before pending state or command bytes, and the preserved navigation-postcondition +cases. Full exact-head local/hosted gates and visual inspection remain independently +required. No inherited checkpoint establishes acceptance for this combined tree. + +A matching response is still only protocol acknowledgment. Policy approval, browser +authentication, trusted event provenance and causal page effects remain separate. +This active Draft does not close issue #28 or establish protected-main delivery.