Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2687529
test(core): require bounded BiDi WebSocket endpoint admission
seonghobae Aug 19, 2026
a1c4cb5
style(core): canonicalize BiDi WebSocket endpoint RED
seonghobae Aug 19, 2026
dbdff70
style(core): apply canonical rustfmt to endpoint RED
seonghobae Aug 19, 2026
3d2c408
feat(core): admit bounded BiDi WebSocket endpoints
seonghobae Aug 19, 2026
e0ec37e
style(core): apply canonical endpoint rustfmt
seonghobae Aug 19, 2026
bebb1e1
test(core): cover malformed BiDi endpoint authorities
seonghobae Aug 19, 2026
8054128
fix(core): remove unreachable IPv4 endpoint branch
seonghobae Aug 19, 2026
42bfbed
test(core): cover empty BiDi endpoint authority
seonghobae Aug 19, 2026
bc042ee
test(core): format BiDi WebSocket endpoint regressions
seonghobae Aug 19, 2026
5d7906a
test(core): require exact BiDi endpoint session correlation
seonghobae Aug 19, 2026
96fdd9d
style(core): apply canonical BiDi session-correlation formatting
seonghobae Aug 19, 2026
c4d2e4d
feat(core): correlate BiDi endpoint with exact session
seonghobae Aug 19, 2026
45935f5
feat(core): export correlated BiDi endpoint contract
seonghobae Aug 19, 2026
70461f1
style(core): apply canonical session-correlation formatting
seonghobae Aug 19, 2026
4047cef
docs(changelog): record BiDi session correlation
seonghobae Aug 19, 2026
48ad304
test(core): require explicit BiDi connect target
seonghobae Aug 19, 2026
3cc6711
test(core): format explicit BiDi connect target regressions
seonghobae Aug 19, 2026
2e9898d
feat(core): derive explicit BiDi loopback connect targets
seonghobae Aug 19, 2026
aace3a8
feat(core): export explicit BiDi connect target contract
seonghobae Aug 19, 2026
2e43ad6
style(core): format explicit BiDi connect target
seonghobae Aug 19, 2026
6a21bed
test(core): require BiDi connect target changelog evidence
seonghobae Aug 19, 2026
7e30360
docs(core): record explicit BiDi connect target boundary
seonghobae Aug 19, 2026
dc5cfd8
test(core): preserve BiDi endpoint across resolver handoff
seonghobae Aug 19, 2026
3a9506c
fix(core): retain correlated BiDi endpoint on resolver handoff
seonghobae Aug 19, 2026
d34c528
test(core): require exact BiDi socket peer verification
seonghobae Aug 19, 2026
d7cf226
feat(core): verify exact BiDi socket peer
seonghobae Aug 19, 2026
89fc9c3
feat(core): export verified BiDi peer contract
seonghobae Aug 19, 2026
f62d204
test(core): require BiDi socket-peer release evidence
seonghobae Aug 19, 2026
d52cdfe
docs(changelog): record BiDi socket-peer verification
seonghobae Aug 19, 2026
fa510a0
fix(changelog): preserve direct TCP release wording
seonghobae Aug 19, 2026
2495b5a
test(core): cover ChromeDriver session id compatibility
seonghobae Aug 19, 2026
baf917c
fix(core): accept canonical ChromeDriver session ids
seonghobae Aug 19, 2026
ba38e2c
merge: carry ChromeDriver session compatibility into correlation
seonghobae Aug 19, 2026
d8f45f8
merge: carry ChromeDriver session compatibility into connect targets
seonghobae Aug 21, 2026
036412a
merge: carry ChromeDriver session compatibility into socket peer veri…
seonghobae Aug 21, 2026
1db9faa
Merge pull request #191 from ContextualWisdomLab/feat/webdriver-bidi-…
seonghobae Aug 26, 2026
37e5688
Merge pull request #189 from ContextualWisdomLab/feat/webdriver-bidi-…
seonghobae Aug 26, 2026
09736e2
Merge pull request #190 from ContextualWisdomLab/feat/webdriver-bidi-…
seonghobae Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to OriginWeave are documented in this file. The format follo

### Added

- Exact WebDriver BiDi socket-peer verification that consumes an approved no-DNS connect target, requires the observed IP address and port to match exactly, preserves the TLS requirement and exact correlated session id, and remains inert metadata that does not authenticate an OS process, does not negotiate TLS, perform a WebSocket handshake, or grant Agent authority.
- Explicit no-DNS WebDriver BiDi loopback connection targets that derive exact IPv4/IPv6 loopback `SocketAddr` metadata from a session-correlated endpoint, reject `localhost` as requiring separately trusted name resolution, preserve the TLS requirement and exact session id, perform no socket I/O, and grant no Agent authority.
- Rust workspace for independently reusable core, policy, destination, network, TLS, resource, and evidence modules.
- Versioned browser-protocol adapter metadata that distinguishes WebDriver BiDi from pinned CDP, binds bounded adapter/browser revision tokens to an explicit duplicate-free capability set, normalizes capability-set identity independently of caller ordering, and exposes typed fail-closed capability requirements without granting browser, action, network, or secret authority by protocol kind alone.
- Canonical OriginWeave protocol-version parsing for exact `originweave/<major>.<minor>` syntax, with typed fail-closed rejection of malformed, ambiguous, overflowed, or noncanonical serialized generations; parsing does not negotiate compatibility or grant adapter authority.
Expand All @@ -20,6 +22,7 @@ All notable changes to OriginWeave are documented in this file. The format follo
- Typed WebDriver BiDi response-envelope correlation that preserves success/error classification, rejects success responses with absent ids, treats nullable error ids as explicitly uncorrelatable, and prevents correlated error envelopes from becoming success correlation evidence without raw JSON parsing or browser/Agent authority grants.
- Bounded raw WebDriver BiDi response-document admission before JSON parsing, with a 65,536-byte product safety budget, exact wire-text retention, JSON-whitespace-aware top-level object-boundary checks, and typed fail-closed errors; this coarse boundary deliberately does not claim JSON validity, response correlation, browser authenticity, or Agent authority.
- One consuming bounded WebDriver BiDi response-document correlation boundary that parses the exact admitted JSON document and feeds only its typed response kind and protocol-range id into the existing exact command-correlation gate, preserving nested parser/correlation error sources without authenticating transport or granting browser/Agent authority.
- Consuming WebDriver BiDi WebSocket endpoint/session correlation that validates one caller-supplied canonical session UUID and rejects exact session mismatches before later transport use; the correlated type preserves only bounded endpoint metadata and does not authenticate Chromium, ChromeDriver, the caller, or the socket peer.
- 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.
Expand Down
11 changes: 11 additions & 0 deletions crates/originweave-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ mod webdriver_bidi_response_document;
mod webdriver_bidi_response_document_correlation;
mod webdriver_bidi_response_envelope;
mod webdriver_bidi_result;
mod webdriver_bidi_websocket_connect_target;
mod webdriver_bidi_websocket_endpoint;

pub use browser_authority_registry::BrowserAuthorityRegistry;
pub use browser_protocol::{
Expand Down Expand Up @@ -87,3 +89,12 @@ pub use webdriver_bidi_response_envelope::{
pub use webdriver_bidi_result::{
ValidatedWebDriverBiDiLocateNodesResult, WebDriverBiDiLocateNodesResultAdmissionError,
};
pub use webdriver_bidi_websocket_connect_target::{
VerifiedWebDriverBiDiSocketPeer, WebDriverBiDiSocketPeerVerificationError,
WebDriverBiDiWebSocketConnectTarget, WebDriverBiDiWebSocketConnectTargetError,
};
pub use webdriver_bidi_websocket_endpoint::{
CorrelatedWebDriverBiDiWebSocketEndpoint, MAX_WEBDRIVER_BIDI_WEBSOCKET_ENDPOINT_BYTES,
WebDriverBiDiWebSocketEndpoint, WebDriverBiDiWebSocketEndpointAdmissionError,
WebDriverBiDiWebSocketEndpointCorrelationError,
};
206 changes: 206 additions & 0 deletions crates/originweave-core/src/webdriver_bidi_websocket_connect_target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
//! Explicit no-DNS connection targets for correlated WebDriver BiDi endpoints.
//!
//! This boundary converts only literal loopback listener identities into exact socket metadata.
//! It deliberately refuses `localhost` so a later connector cannot silently inherit ambient DNS
//! authority from an admitted WebDriver endpoint. When explicit trusted name resolution is needed,
//! the typed error preserves the correlated endpoint instead of discarding its session evidence.
//! A separately observed connected peer must also match the approved socket destination exactly
//! before it becomes verified transport metadata. These values do not open a socket, authenticate
//! a process, negotiate TLS, perform a WebSocket handshake, or grant Agent authority.

use std::{
fmt,
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
};

use crate::CorrelatedWebDriverBiDiWebSocketEndpoint;

/// An exact loopback socket destination derived from one correlated WebDriver BiDi endpoint.
///
/// The destination is inert connection metadata. It proves only that the already-admitted endpoint
/// named a literal loopback IP address, retained an explicit nonzero port, and was correlated to the
/// expected WebDriver session id. A runtime connector must independently establish a connection and
/// verify its observed peer before treating that transport as the approved destination. TLS,
/// WebSocket, process, policy, and browser authority remain separate boundaries.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WebDriverBiDiWebSocketConnectTarget {
socket_addr: SocketAddr,
requires_tls: bool,
session_id: String,
}

impl WebDriverBiDiWebSocketConnectTarget {
/// Return the exact loopback socket destination without performing name resolution.
#[must_use]
pub const fn socket_addr(&self) -> SocketAddr {
self.socket_addr
}

/// Return whether the admitted endpoint requires a TLS-protected WebSocket transport.
#[must_use]
pub const fn requires_tls(&self) -> bool {
self.requires_tls
}

/// Return the exact WebDriver session id established by the preceding correlation boundary.
#[must_use]
pub fn session_id(&self) -> &str {
&self.session_id
}

/// Consume this approved destination and verify one observed connected socket peer exactly.
///
/// Matching requires the complete [`SocketAddr`]—IP address and port—to equal the approved
/// no-DNS destination. A mismatch consumes the target and fails closed, preventing a connector
/// from accidentally reusing the same authority after observing a different peer. Success
/// produces inert verified-peer metadata only; it does not authenticate an OS process,
/// negotiate TLS, perform a WebSocket handshake, or grant browser/Agent authority.
pub fn verify_connected_peer(
self,
observed_peer: SocketAddr,
) -> Result<VerifiedWebDriverBiDiSocketPeer, WebDriverBiDiSocketPeerVerificationError> {
let expected = self.socket_addr;
if observed_peer != expected {
return Err(WebDriverBiDiSocketPeerVerificationError::PeerMismatch {
expected,
actual: observed_peer,
});
}

Ok(VerifiedWebDriverBiDiSocketPeer {
connect_target: self,
})
}
}

/// Inert metadata proving that a connected peer exactly matched the approved BiDi destination.
///
/// This value carries only the destination, TLS requirement, and correlated WebDriver session id
/// already established by preceding boundaries. It does not prove process identity, TLS peer
/// identity, WebSocket protocol state, browser authenticity, policy authorization, or Agent action
/// authority.
#[derive(Debug, PartialEq, Eq)]
pub struct VerifiedWebDriverBiDiSocketPeer {
connect_target: WebDriverBiDiWebSocketConnectTarget,
}

impl VerifiedWebDriverBiDiSocketPeer {
/// Return the exact approved and observed socket peer address.
#[must_use]
pub const fn socket_addr(&self) -> SocketAddr {
self.connect_target.socket_addr()
}

/// Return whether the correlated endpoint still requires TLS before WebSocket use.
#[must_use]
pub const fn requires_tls(&self) -> bool {
self.connect_target.requires_tls()
}

/// Return the exact correlated WebDriver session id.
#[must_use]
pub fn session_id(&self) -> &str {
self.connect_target.session_id()
}
}

/// Fail-closed errors while verifying an observed BiDi socket peer.
#[derive(Debug, PartialEq, Eq)]
pub enum WebDriverBiDiSocketPeerVerificationError {
/// The connected peer differed from the exact destination approved before connection.
PeerMismatch {
/// Exact socket address that the connector was authorized to reach.
expected: SocketAddr,
/// Socket peer address observed after connection.
actual: SocketAddr,
},
}

impl fmt::Display for WebDriverBiDiSocketPeerVerificationError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PeerMismatch { .. } => formatter.write_str(
"connected WebDriver BiDi socket peer does not match the approved destination",
),
}
}
}

impl std::error::Error for WebDriverBiDiSocketPeerVerificationError {}

impl CorrelatedWebDriverBiDiWebSocketEndpoint {
/// Consume this correlated endpoint and derive one exact no-DNS loopback socket destination.
///
/// Literal IPv4 and IPv6 loopback hosts become an exact [`SocketAddr`]. Any admitted host that
/// is not an IP literal—including `localhost`—fails closed so the caller must perform an
/// explicit, separately trusted name-resolution step rather than inheriting ambient resolver
/// authority. The name-resolution-required error retains this correlated endpoint so that
/// trusted resolver handoff does not require reconstructing or recorrelation of session evidence.
/// This method performs no DNS lookup, socket I/O, peer authentication, TLS, or WebSocket
/// handshake.
pub fn into_explicit_connect_target(
self,
) -> Result<WebDriverBiDiWebSocketConnectTarget, WebDriverBiDiWebSocketConnectTargetError> {
let socket_addr = if let Ok(ipv4) = self.host().parse::<Ipv4Addr>() {
SocketAddr::from((ipv4, self.port()))
} else if let Ok(ipv6) = self.host().parse::<Ipv6Addr>() {
SocketAddr::from((ipv6, self.port()))
} else {
return Err(
WebDriverBiDiWebSocketConnectTargetError::NameResolutionRequired {
correlated_endpoint: self,
},
);
};

Ok(WebDriverBiDiWebSocketConnectTarget {
socket_addr,
requires_tls: self.is_secure(),
session_id: self.session_id().to_owned(),
})
}
}

/// Fail-closed errors while deriving an explicit WebDriver BiDi socket destination.
#[derive(Debug, PartialEq, Eq)]
pub enum WebDriverBiDiWebSocketConnectTargetError {
/// The admitted endpoint used a host name and therefore requires explicit trusted resolution.
NameResolutionRequired {
/// The still-correlated endpoint that must be handed to a separately trusted resolver.
correlated_endpoint: CorrelatedWebDriverBiDiWebSocketEndpoint,
},
}

impl WebDriverBiDiWebSocketConnectTargetError {
/// Borrow the correlated endpoint preserved for an explicit trusted resolver handoff.
#[must_use]
pub const fn correlated_endpoint(&self) -> &CorrelatedWebDriverBiDiWebSocketEndpoint {
match self {
Self::NameResolutionRequired {
correlated_endpoint,
} => correlated_endpoint,
}
}

/// Recover the correlated endpoint for an explicit trusted resolver handoff.
#[must_use]
pub fn into_correlated_endpoint(self) -> CorrelatedWebDriverBiDiWebSocketEndpoint {
match self {
Self::NameResolutionRequired {
correlated_endpoint,
} => correlated_endpoint,
}
}
}

impl fmt::Display for WebDriverBiDiWebSocketConnectTargetError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NameResolutionRequired { .. } => formatter.write_str(
"WebDriver BiDi WebSocket endpoint requires explicit trusted name resolution",
),
}
}
}

impl std::error::Error for WebDriverBiDiWebSocketConnectTargetError {}
Loading
Loading