Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2ba56af
test(core): require BiDi response envelope semantics
seonghobae Aug 17, 2026
4bf783e
style(core): canonicalize BiDi envelope regression
seonghobae Aug 17, 2026
45ed6b0
feat(core): retain WebDriver BiDi response envelope kind
seonghobae Aug 17, 2026
b088f23
feat(core): export BiDi response envelope contract
seonghobae Aug 17, 2026
640289b
style(core): canonicalize BiDi envelope exports
seonghobae Aug 17, 2026
67e57de
docs(changelog): record BiDi response envelope correlation
seonghobae Aug 17, 2026
4690320
test(core): require fail-closed BiDi success evidence conversion
seonghobae Aug 17, 2026
c6fac94
feat(core): reject BiDi error envelopes from success evidence
seonghobae Aug 17, 2026
13a8a1a
style(core): apply canonical BiDi error formatting
seonghobae Aug 17, 2026
d251ca5
test(core): bind locateNodes result budget to correlated response
seonghobae Aug 17, 2026
c11f46b
style(core): apply canonical rustfmt to result-budget regression
seonghobae Aug 17, 2026
429b02b
fix(core): bind locateNodes result budget to correlated success
seonghobae Aug 17, 2026
9a53a5c
test(core): require correlated locateNodes result admission
seonghobae Aug 17, 2026
86a18c5
style(core): apply canonical locateNodes result test formatting
seonghobae Aug 17, 2026
e9a47b6
feat(core): admit correlated locateNodes result batches
seonghobae Aug 17, 2026
fe8bbf2
feat(core): export correlated locateNodes result admission
seonghobae Aug 17, 2026
2c535b8
test(core): require correlated result authority binding
seonghobae Aug 17, 2026
3488eea
style(core): apply canonical correlated result test formatting
seonghobae Aug 17, 2026
44ddc32
test(core): isolate correlated result authority RED
seonghobae Aug 17, 2026
61508eb
feat(core): bind correlated context identifiers fail-closed
seonghobae Aug 17, 2026
8ce29ea
feat(core): expose crate-private context identity check
seonghobae Aug 17, 2026
cfee13a
feat(core): bind correlated locateNodes results to current authority
seonghobae Aug 17, 2026
31faaac
test(core): cover missing origin binding rejection
seonghobae Aug 17, 2026
67d0a3b
test(core): require bounded BiDi response documents
seonghobae Aug 18, 2026
192b6f2
test(core): format BiDi response document RED
seonghobae Aug 18, 2026
810c762
feat(core): bound raw BiDi response documents
seonghobae Aug 18, 2026
f4cf87b
feat(core): export BiDi response document boundary
seonghobae Aug 18, 2026
afe81c4
docs: record bounded BiDi response documents
seonghobae Aug 18, 2026
7a08c38
Merge pull request #185 from ContextualWisdomLab/feat/webdriver-bidi-…
seonghobae Aug 26, 2026
82f9be4
docs: record correlated BiDi result admission
seonghobae Aug 26, 2026
fd8aec7
Merge pull request #184 from ContextualWisdomLab/feat/webdriver-bidi-…
seonghobae Aug 26, 2026
2d7d25f
fix(core): require BiDi response classification
seonghobae Aug 26, 2026
889659e
docs(core): avoid private correlation links
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ All notable changes to OriginWeave are documented in this file. The format follo
- Bounded WebDriver BiDi accessibility-node query parameters for `browsingContext.locateNodes`, with reviewed selector/result budgets, exact-token role and control-free accessible-name admission, minimal remote-value serialization options, and fail-closed revalidation that rejects an untrusted adapter response whose returned node count exceeds the exact request budget before node normalization or retention.
- Deterministic WebDriver BiDi `browsingContext.locateNodes` command serialization that accepts only protocol-range `js-uint` identifiers and bounded injection-safe browsing-context identifiers, JSON-escapes reviewed query text, and emits the exact accessibility locator, finite node budget, and minimal serialization options without performing transport I/O or granting browser or Agent authority.
- Fail-closed WebDriver BiDi `locateNodes` response correlation that consumes the exact serialized command, rejects out-of-range or mismatched response `id` values, and returns non-cloneable correlation evidence carrying only the matched command identifier and browsing context without parsing the response or granting browser/Agent authority.
- 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.
- Correlated WebDriver BiDi `locateNodes` result admission that consumes exact response-correlation evidence, revalidates the serialized browsing-context identifier against the registered browsing-context identity, enforces the command's node budget, and binds admitted nodes atomically without granting browser or Agent authority.
- 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.
- 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 Expand Up @@ -88,4 +91,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
14 changes: 14 additions & 0 deletions crates/originweave-core/src/browser_authority_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ impl BrowserAuthorityRegistry {
.current_context_epoch(browser_session, browsing_context)
}

/// Require an opaque external browsing-context identifier to name this exact context.
pub(crate) fn require_context_external_identifier(
&self,
browser_session: BrowserSessionId,
browsing_context: BrowsingContextId,
external_identifier: &str,
) -> Result<(), BrowserRegistryError> {
self.inner.require_context_external_identifier(
browser_session,
browsing_context,
external_identifier,
)
}

/// Bind the canonical origin observed for the exact current browser document.
pub fn bind_context_origin(
&mut self,
Expand Down
46 changes: 46 additions & 0 deletions crates/originweave-core/src/browser_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,26 @@ impl BrowserAuthorityRegistry {
self.current_epoch(browsing_context)
}

/// Require an opaque external browsing-context identifier to name this exact context.
///
/// This read-only check binds transport-level context text back to the already-registered
/// OriginWeave session/context pair. It never registers a new external context as a side effect,
/// so an untrusted result cannot create authority merely by presenting a different identifier.
pub(crate) fn require_context_external_identifier(
&self,
browser_session: BrowserSessionId,
browsing_context: BrowsingContextId,
external_identifier: &str,
) -> Result<(), BrowserRegistryError> {
validate_external_identifier(external_identifier)?;
self.current_context_epoch(browser_session, browsing_context)?;
let key = (browser_session, external_identifier.to_owned());
if self.context_by_external.get(&key).copied() != Some(browsing_context) {
return Err(BrowserRegistryError::ContextExternalIdentifierMismatch);
}
Ok(())
}

/// Bind the canonical origin observed for the exact current browser document.
///
/// This boundary lets a trusted browser adapter establish current document-origin state before
Expand Down Expand Up @@ -420,6 +440,8 @@ pub enum BrowserRegistryError {
/// Session supplied by the current caller.
actual: BrowserSessionId,
},
/// The transport-level browsing-context identifier does not name the supplied registered context.
ContextExternalIdentifierMismatch,
/// The current document has no canonical origin bound to the browsing context.
ContextOriginNotBound,
/// The context origin changed without first rotating the document epoch.
Expand Down Expand Up @@ -450,6 +472,9 @@ impl fmt::Display for BrowserRegistryError {
expected.value(),
actual.value()
),
Self::ContextExternalIdentifierMismatch => formatter.write_str(
"browsing context external identifier does not match the registered context",
),
Self::ContextOriginNotBound => formatter.write_str(
"browsing context has no canonical origin bound for the current document",
),
Expand Down Expand Up @@ -627,6 +652,18 @@ mod tests {
let contexts = values(registry.register_context(session, "context-a"));
assert_eq!(contexts.len(), 1);
let context = contexts[0];
assert_eq!(
registry.require_context_external_identifier(session, context, "context-a"),
Ok(())
);
assert_eq!(
registry.require_context_external_identifier(session, context, "context-b"),
Err(BrowserRegistryError::ContextExternalIdentifierMismatch)
);
assert_eq!(
registry.require_context_external_identifier(session, context, ""),
Err(BrowserRegistryError::InvalidExternalIdentifier)
);

let maximum_epochs = values(DocumentEpoch::new(u64::MAX));
assert_eq!(maximum_epochs.len(), 1);
Expand All @@ -641,6 +678,14 @@ mod tests {
let unknown_contexts = values(BrowsingContextId::new(999));
assert_eq!(unknown_sessions.len(), 1);
assert_eq!(unknown_contexts.len(), 1);
assert_eq!(
registry.require_context_external_identifier(unknown_sessions[0], context, "context-a"),
Err(BrowserRegistryError::UnknownBrowserSession)
);
assert_eq!(
registry.require_context_external_identifier(session, unknown_contexts[0], "context-a"),
Err(BrowserRegistryError::UnknownBrowsingContext)
);
assert_eq!(
registry.bind_node(unknown_sessions[0], context, origin, "node"),
Err(BrowserRegistryError::UnknownBrowserSession)
Expand Down Expand Up @@ -803,6 +848,7 @@ mod tests {
expected: expected_values[0],
actual: actual_values[0],
},
BrowserRegistryError::ContextExternalIdentifierMismatch,
BrowserRegistryError::ContextOriginNotBound,
BrowserRegistryError::OriginChangedWithoutDocumentAdvance,
BrowserRegistryError::IdentifierSpaceExhausted,
Expand Down
13 changes: 12 additions & 1 deletion crates/originweave-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ mod browser_registry;
mod browser_registry_coverage;
mod contracts;
mod webdriver_bidi_command;
mod webdriver_bidi_response_document;
mod webdriver_bidi_result;

pub use browser_authority_registry::BrowserAuthorityRegistry;
pub use browser_protocol::{
Expand Down Expand Up @@ -63,7 +65,16 @@ pub use browser_registry::{
};
pub use contracts::*;
pub use webdriver_bidi_command::{
MAX_WEBDRIVER_BIDI_COMMAND_ID, ValidatedWebDriverBiDiLocateNodesResponse,
CorrelatedWebDriverBiDiLocateNodesResponse, MAX_WEBDRIVER_BIDI_COMMAND_ID,
ValidatedWebDriverBiDiLocateNodesResponse, WebDriverBiDiCommandResponseKind,
WebDriverBiDiLocateNodesCommand, WebDriverBiDiLocateNodesCommandError,
WebDriverBiDiLocateNodesResponseCorrelationError,
WebDriverBiDiLocateNodesResponseEnvelopeError,
};
pub use webdriver_bidi_response_document::{
BoundedWebDriverBiDiResponseDocument, MAX_WEBDRIVER_BIDI_RESPONSE_DOCUMENT_BYTES,
WebDriverBiDiResponseDocumentAdmissionError,
};
pub use webdriver_bidi_result::{
ValidatedWebDriverBiDiLocateNodesResult, WebDriverBiDiLocateNodesResultAdmissionError,
};
Loading
Loading