diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d197d67e..2241b0dea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - 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/.` syntax, with typed fail-closed rejection of malformed, ambiguous, overflowed, or noncanonical serialized generations; parsing does not negotiate compatibility or grant adapter authority. +- Atomic browser-protocol use validation that requires the exact OriginWeave protocol generation, caller-supplied runtime protocol family, exact pinned runtime protocol/browser revisions, and an explicitly declared capability in deterministic fail-closed order before producing non-cloneable validation evidence; this metadata proof does not authenticate the adapter or grant 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. - Typed browser actions, capabilities, risk classes, execution modes, robots decisions, secret-delivery contracts, immutable canonical action-intent digests, and intent-bound approval scopes. - Deterministic fail-closed policy evaluation for untrusted instructions, origin grants, crawler restrictions, execution-mode and purpose consistency, approvals, and brokered secrets. diff --git a/crates/originweave-core/src/browser_protocol.rs b/crates/originweave-core/src/browser_protocol.rs index 369666ece..d4650ac30 100644 --- a/crates/originweave-core/src/browser_protocol.rs +++ b/crates/originweave-core/src/browser_protocol.rs @@ -293,21 +293,29 @@ impl BrowserProtocolAdapterDescriptor { /// Validate all adapter metadata prerequisites for one immediate browser operation. /// /// Validation is intentionally ordered and fail closed: the exact - /// OriginWeave Protocol generation is checked first, then the supplied - /// runtime protocol/browser revisions, then the required adapter - /// capability. Success returns a non-cloneable value that a later trusted - /// transport can consume as proof that these metadata prerequisites were - /// checked together. It is not browser or Agent authority and does not - /// authenticate the caller supplying runtime revision evidence. + /// OriginWeave Protocol generation is checked first, then the caller-supplied + /// runtime protocol family, then the supplied runtime protocol/browser + /// revisions, and finally the required adapter capability. Success returns + /// a non-cloneable value that a later trusted transport can consume as proof + /// that these metadata prerequisites were checked together. It is not + /// browser or Agent authority and does not authenticate or attest the caller + /// supplying runtime metadata. pub fn validate_use( &self, required_originweave_protocol_version: OriginWeaveProtocolVersion, + runtime_kind: BrowserProtocolKind, runtime_protocol_revision: &str, runtime_browser_revision: &str, required_capability: BrowserProtocolCapability, ) -> Result { self.require_originweave_protocol_version(required_originweave_protocol_version) .map_err(BrowserProtocolUseValidationError::ProtocolVersion)?; + if self.kind != runtime_kind { + return Err(BrowserProtocolUseValidationError::ProtocolKindMismatch { + descriptor_kind: self.kind, + runtime_kind, + }); + } self.require_runtime_revisions(runtime_protocol_revision, runtime_browser_revision) .map_err(BrowserProtocolUseValidationError::RuntimeRevision)?; self.require_capability(required_capability) @@ -484,6 +492,13 @@ impl std::error::Error for BrowserProtocolCapabilityRequirementError {} pub enum BrowserProtocolUseValidationError { /// The descriptor targets the wrong OriginWeave Protocol generation. ProtocolVersion(BrowserProtocolVersionRequirementError), + /// The runtime transport reports a different protocol family than the descriptor. + ProtocolKindMismatch { + /// Browser protocol family pinned by the adapter descriptor. + descriptor_kind: BrowserProtocolKind, + /// Browser protocol family reported by the runtime transport. + runtime_kind: BrowserProtocolKind, + }, /// The supplied runtime protocol or browser revision is invalid or has drifted. RuntimeRevision(BrowserProtocolRuntimeRequirementError), /// The descriptor does not explicitly declare the required capability. @@ -494,6 +509,8 @@ impl fmt::Display for BrowserProtocolUseValidationError { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::ProtocolVersion(error) => error.fmt(formatter), + Self::ProtocolKindMismatch { .. } => formatter + .write_str("runtime browser protocol kind does not match the pinned adapter kind"), Self::RuntimeRevision(error) => error.fmt(formatter), Self::Capability(error) => error.fmt(formatter), } @@ -504,6 +521,7 @@ impl std::error::Error for BrowserProtocolUseValidationError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::ProtocolVersion(error) => Some(error), + Self::ProtocolKindMismatch { .. } => None, Self::RuntimeRevision(error) => Some(error), Self::Capability(error) => Some(error), } diff --git a/crates/originweave-core/tests/browser_protocol_use_validation.rs b/crates/originweave-core/tests/browser_protocol_use_validation.rs index cf7c8ad0b..8994253ec 100644 --- a/crates/originweave-core/tests/browser_protocol_use_validation.rs +++ b/crates/originweave-core/tests/browser_protocol_use_validation.rs @@ -32,6 +32,7 @@ fn validated_use_binds_all_required_adapter_metadata() -> Result<(), Box Result<(), Box> { + let descriptor = descriptor()?; + + let error = descriptor.validate_use( + ORIGINWEAVE_PROTOCOL_VERSION, + BrowserProtocolKind::ChromeDevToolsProtocol, + "runtime revision with spaces", + "browser/revision", + BrowserProtocolCapability::NetworkObservation, + ); + + assert_eq!( + error, + Err(BrowserProtocolUseValidationError::ProtocolKindMismatch { + descriptor_kind: BrowserProtocolKind::WebDriverBiDi, + runtime_kind: BrowserProtocolKind::ChromeDevToolsProtocol, + }) + ); + let error = error.err().ok_or("expected protocol kind mismatch")?; + assert_eq!( + error.to_string(), + "runtime browser protocol kind does not match the pinned adapter kind" + ); + assert!(error.source().is_none()); + Ok(()) +} + #[test] fn runtime_revision_validation_precedes_capability_check() -> Result<(), Box> { let descriptor = descriptor()?; @@ -82,6 +113,7 @@ fn runtime_revision_validation_precedes_capability_check() -> Result<(), Box Result<(), Box