Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
b2fa94b
test(browser): require protocol identity registry
seonghobae Aug 9, 2026
0e5917a
style(browser): apply canonical rustfmt
seonghobae Aug 9, 2026
bcc8f10
feat(browser): implement authority registry
seonghobae Aug 9, 2026
2e4c359
test(browser): close registry coverage gaps
seonghobae Aug 9, 2026
1a1027f
test(browser): satisfy strict clippy contracts
seonghobae Aug 9, 2026
938ae8c
style(browser): apply canonical registry test formatting
seonghobae Aug 9, 2026
76cddbc
test(browser): keep fixture origin errors local
seonghobae Aug 9, 2026
4ea14a1
ci: surface exact browser-registry coverage gaps
seonghobae Aug 9, 2026
b298111
test(browser): require bounded authority identifier capacity
seonghobae Aug 10, 2026
f528f6a
feat(browser): bound registry identifier capacity
seonghobae Aug 10, 2026
eaadbf5
test(browser): cover exact registry denial branches
seonghobae Aug 10, 2026
235fd1f
test(browser): keep origin mismatch fixture deterministic
seonghobae Aug 10, 2026
2276b4c
style(browser): format authority registry regression
seonghobae Aug 10, 2026
930b3a8
refactor(browser): preserve fallible invariants without uncovered pro…
seonghobae Aug 10, 2026
c78dc70
fix(browser): satisfy strict clippy on invariant side effects
seonghobae Aug 10, 2026
39966aa
test(browser): cover repeated node binding in unit crate
seonghobae Aug 10, 2026
f3130ea
test(browser): run repeated binding in unit crate
seonghobae Aug 10, 2026
45ed9bc
test(browser): use non-panicking unit origin fixture
seonghobae Aug 10, 2026
c30e2e4
test(browser): fix unit origin fixture conversion
seonghobae Aug 10, 2026
78ba71b
test(browser): exercise public default and error contracts
seonghobae Aug 10, 2026
504f8cf
test(browser): cover unit-crate session authority failures
seonghobae Aug 10, 2026
b539539
test(core): remove synthetic coverage-only error branches
seonghobae Aug 10, 2026
c30c76e
Merge branch 'main' into feat/browser-authority-registry
opencode-agent[bot] Aug 10, 2026
9e635e8
test(browser): derive identifier boundary from public contract
seonghobae Aug 10, 2026
c0da8bd
test(core): require versioned browser protocol capability descriptor
seonghobae Aug 12, 2026
d7ad355
feat(core): add browser protocol capability descriptor
seonghobae Aug 12, 2026
c1e6404
feat(core): export browser protocol descriptor
seonghobae Aug 12, 2026
698218f
test(core): cover non-ASCII protocol metadata rejection
seonghobae Aug 12, 2026
723a092
docs(changelog): record browser protocol descriptor
seonghobae Aug 12, 2026
61164ce
test(core): require explicit browser protocol revision
seonghobae Aug 12, 2026
e1cc2c8
feat(core): bind browser protocol revision explicitly
seonghobae Aug 12, 2026
8d2549c
test(core): require canonical browser capability ordering
seonghobae Aug 12, 2026
7a3e8f4
fix(core): canonicalize browser capability set ordering
seonghobae Aug 12, 2026
5dd9635
test(browser): require registry authority teardown
seonghobae Aug 14, 2026
cf9581a
fix(browser): retire registry authority state
seonghobae Aug 14, 2026
f577a78
style(browser): apply canonical rustfmt
seonghobae Aug 14, 2026
2abb5ff
Merge branch 'main' into feat/browser-authority-registry
github-actions[bot] Aug 14, 2026
0c4ae8d
test(core): remove redundant registry coverage module wiring
seonghobae Aug 15, 2026
ab9e16f
test(core): remove redundant registry coverage module
seonghobae Aug 15, 2026
03dac28
test(core): restore unit-binary registry coverage
seonghobae Aug 15, 2026
9fc407b
merge: align browser protocol capabilities with current authority
seonghobae Aug 15, 2026
a0986fe
test(core): require standard public validation errors
seonghobae Aug 15, 2026
5c6d0ed
fix(core): expose standard validation errors
seonghobae Aug 15, 2026
15048b1
fix(core): wire public validation error contracts
seonghobae Aug 15, 2026
2f1e7cd
test(core): use standard validation errors without expect
seonghobae Aug 15, 2026
8bd82c4
refactor(core): make contract exports explicit
seonghobae Aug 15, 2026
d242b23
test(core): require explicit sha256 digest prefix
seonghobae Aug 15, 2026
90227c5
fix(core): clarify sha256 digest prefix
seonghobae Aug 15, 2026
c7ab7a1
merge: align protocol capability descriptor with current browser auth…
seonghobae Aug 15, 2026
2203d60
test(core): preserve origin-bound expiring extension grants
seonghobae Aug 17, 2026
14cc3f5
fix(core): preserve protected extension grant authority
seonghobae Aug 17, 2026
8ab27e5
merge(main): reconcile extension authority baseline
seonghobae Aug 17, 2026
c146e3e
fix(core): delegate missing extension grant evaluation
seonghobae Aug 17, 2026
8a4b4c2
style(core): apply canonical extension authority formatting
seonghobae Aug 17, 2026
e62c0ce
chore(core): align browser protocol descriptor with current authority…
seonghobae Aug 17, 2026
b55716d
test(core): require extension grant task binding
seonghobae Aug 17, 2026
d043af9
fix(core): bind extension grants to Agent Task identity
seonghobae Aug 17, 2026
a4830c2
style(core): apply canonical task-authority formatting
seonghobae Aug 17, 2026
b45ab7b
docs(adr): record active task-bound extension authority
seonghobae Aug 17, 2026
692cf58
docs(changelog): record task-bound extension grants
seonghobae Aug 17, 2026
74b16b0
merge(browser): align protocol descriptor with current authority regi…
seonghobae Aug 17, 2026
1a13007
docs(changelog): restore protocol descriptor entry after stack alignment
seonghobae Aug 17, 2026
1c6a208
test(browser): keep failed node allocation transactional
seonghobae Aug 20, 2026
8d51ea3
fix(browser): keep failed node binding transactional
seonghobae Aug 20, 2026
b9e9f3a
style(browser): format transactional node binding
seonghobae Aug 20, 2026
0428a97
test(browser): cover fail-closed registry invariants
seonghobae Aug 20, 2026
8bf7803
test(browser): reject forged or retired node handles
seonghobae Aug 20, 2026
d5705f8
fix(browser): revalidate live node authority in registry
seonghobae Aug 20, 2026
0d12ad5
style(browser): apply canonical node validation formatting
seonghobae Aug 20, 2026
1248ccf
test(browser): close registry coverage gaps
seonghobae Aug 20, 2026
1d9e7c5
test(browser): reject cross-registry node-handle reuse
seonghobae Aug 22, 2026
1fa6fec
test(browser): reject forged matching registry handles
seonghobae Aug 22, 2026
4a828ed
fix(browser): bind node handles to registry instances
seonghobae Aug 22, 2026
2dbcb03
test(browser): cover registry authority invariants
seonghobae Aug 22, 2026
76651a5
style(browser): apply canonical Rust formatting
seonghobae Aug 22, 2026
724305f
test(browser): cover corrupt zero node authority
seonghobae Aug 22, 2026
94de6c2
test(browser): require same-document node retirement
seonghobae Aug 22, 2026
2c08bca
style(browser): apply canonical rustfmt to node retirement regression
seonghobae Aug 22, 2026
8587c8f
feat(browser): revoke same-document node authority
seonghobae Aug 22, 2026
77df987
test(core): cover node retirement and epoch exhaustion
seonghobae Aug 22, 2026
e8c105c
fix(core): purge duplicate node aliases on retirement
seonghobae Aug 22, 2026
1e5f85a
test(core): cover session retirement unit branches
seonghobae Aug 22, 2026
28e417a
fix(core): restore canonical browser registry source
seonghobae Aug 22, 2026
42068e7
test(core): cover session retirement unit branches
seonghobae Aug 22, 2026
32f058f
test(core): cover duplicate context and identifier bounds
seonghobae Aug 22, 2026
755cb39
test(core): cover direct registry fail-closed branches
seonghobae Aug 22, 2026
0edb86a
test(core): cover allocation and rotation error regions
seonghobae Aug 22, 2026
556c986
test(core): close remaining registry coverage region
seonghobae Aug 22, 2026
da4ccc8
docs(adr): cite primary MV3 authority sources
seonghobae Aug 22, 2026
eb29cad
test(browser): hide session membership from unissued node handles
seonghobae Aug 23, 2026
a8b03e0
fix(browser): authenticate node issuance before registry lookup
seonghobae Aug 23, 2026
c0ef5d5
docs(changelog): record node-handle oracle hardening
seonghobae Aug 23, 2026
eb992a4
test(browser): cover authenticated retired-authority failures
seonghobae Aug 23, 2026
8093169
test(browser): apply canonical formatting
seonghobae Aug 23, 2026
7020dbe
test(browser): cover issued context-session corruption
seonghobae Aug 23, 2026
3a29725
test(browser): cover context-origin corruption
seonghobae Aug 23, 2026
281737b
repair(browser): restore registry source after transport corruption
seonghobae Aug 23, 2026
c8a357c
test(browser): cover corrupted origin authority rejection
seonghobae Aug 23, 2026
562b4b7
style(browser): apply canonical Rust formatting
seonghobae Aug 23, 2026
8452f7d
fix(browser): restore current registry branch before coverage repair
seonghobae Aug 23, 2026
4cbcb03
test(browser): cover missing pinned origin authority
seonghobae Aug 23, 2026
f71c1ca
test(core): reject empty-hex browser IPv4 authorities
seonghobae Aug 23, 2026
0a73431
fix(core): reject empty-hex browser IPv4 authorities
seonghobae Aug 23, 2026
5157366
docs(changelog): record empty-hex origin hardening
seonghobae Aug 23, 2026
4e9b92b
chore(stack): realign protocol descriptor on browser authority
seonghobae Aug 23, 2026
3c88d51
docs(changelog): preserve protocol descriptor after stack realignment
seonghobae Aug 23, 2026
3795b5b
merge(main): converge browser authority registry onto protected main
seonghobae Aug 24, 2026
cfa82c6
fix(core): disambiguate private contract types after main convergence
seonghobae Aug 24, 2026
344233d
style(core): apply canonical import formatting
seonghobae Aug 24, 2026
93ce1e7
fix(core): avoid module-inception lint after main convergence
seonghobae Aug 24, 2026
56e7b5e
chore(browser): restack protocol descriptor on authority registry
seonghobae Aug 24, 2026
4943b82
docs(browser): restore protocol descriptor changelog
seonghobae Aug 24, 2026
01fd68f
fix(browser): export protocol descriptor at crate boundary
seonghobae Aug 24, 2026
378a9f2
style(browser): apply canonical protocol export formatting
seonghobae Aug 24, 2026
1a79f3b
Merge pull request #106 from ContextualWisdomLab/feat/browser-protoco…
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: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ All notable changes to OriginWeave are documented in this file. The format follo
- Refreshed the product and technical gap baseline with the 2026-08-24 live inventory: 158 open pull requests (44 ready, 114 draft), refreshed exact base/head evidence for the #208–#222 release, enterprise-approval, BAP, and WARC/PROV chains, the governance issue additions #212 and #215, and a required-check provider-failure record for the fail-closed Strix re-dispatches on #208/#218/#220.
- Added a dated product and technical gap baseline that separates protected-main implementation truth, active pull-request evidence, live review/check blockers, and the next buyer-visible Phase 1 acceptance work.
- Refreshed the product and technical gap baseline with the current open-PR inventory and exact base/head evidence for the newest Chromium, BAP, extraction, WARC, and idempotency slices.
- Bound explicit extension-to-Agent grants to a nonzero host-assigned Agent Task identity, so a grant that otherwise matches extension, session, browsing context, origin, expiry, and capability fails closed when reused by a different task.
- Bound explicit extension-to-Agent grants to exclusive trusted-time expiry in addition to extension identity, session, browsing context, and canonical origin, so a same-origin grant cannot be reused at or after the deadline.
- Bound explicit extension-to-Agent grants to the exact canonical origin in addition to extension identity, session, and browsing context, so a same-session navigation or port change cannot reuse the grant.
- Added a bounded browser-protocol authority registry that maps opaque session, browsing-context, and node identifiers to registry-local identities, rotates document epochs, and revalidates live node handles before actions.
- 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, and grants no browser, action, network, or secret authority by protocol kind alone.
- Rust workspace for independently reusable core, policy, destination, network, TLS, resource, and evidence modules.
- 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.
Expand Down Expand Up @@ -59,12 +62,14 @@ All notable changes to OriginWeave are documented in this file. The format follo

### Security

- Registry-issued node validation authenticates registry-instance issuance before session or context lookup, so caller-constructed and cross-registry handles cannot probe whether numeric browser-session identifiers are currently registered.
- Raw page content cannot become a trusted instruction.
- Raw secrets are rejected and secret-capable actions require an opaque broker handle.
- Crawler mode is read-only, must pair with the public-crawl purpose, and fails closed without an applicable robots-policy decision.
- State-changing actions are same-origin by default.
- R3 and R4 approvals are bound to the exact action, target origin, and immutable digest of the complete canonical action intent; R5 legal consent is non-delegable.
- Shortened, integer, hexadecimal, and legacy octal-looking IPv4 host spellings are rejected so the policy origin cannot diverge from Chromium host interpretation.
- Bare hexadecimal-prefix host labels such as `0x`, including terminal `.0x` and `.0X` spellings, are rejected as browser-special numeric hosts rather than admitted as DNS authorities.
- IPv4-mapped IPv6 is canonicalized before destination classification and pin comparison so mapped private or loopback addresses cannot bypass IPv4 policy.
- The default destination policy permits only public addresses and denies unspecified, loopback, private, shared, link-local, metadata, documentation, benchmarking, multicast, broadcast, transition, and protocol-reserved destinations.
- Azure platform IP `168.63.129.16` and Amazon EKS Pod Identity endpoints `169.254.170.23` and `fd00:ec2::23` are classified as metadata or platform services before broader public, link-local, or unique-local rules.
Expand Down
187 changes: 187 additions & 0 deletions crates/originweave-core/src/browser_protocol.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
use std::fmt;

/// Maximum UTF-8 byte length for browser protocol adapter metadata tokens.
pub const MAX_BROWSER_PROTOCOL_METADATA_BYTES: usize = 128;

/// Browser automation protocol family used by one versioned adapter.
///
/// The protocol family is descriptive metadata only. Selecting a kind does not
/// grant any OriginWeave capability or imply that a particular protocol
/// feature is available.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserProtocolKind {
/// Standards-track WebDriver BiDi adapter.
WebDriverBiDi,
/// Chromium-specific Chrome DevTools Protocol adapter.
ChromeDevToolsProtocol,
}

/// One browser operation surface explicitly implemented by an adapter.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserProtocolCapability {
/// Navigate a controlled browser context through the adapter.
Navigation,
/// Produce bounded semantic browser observations.
SemanticObservation,
/// Dispatch typed browser input after OriginWeave policy authorization.
TypedInput,
/// Observe bounded network evidence needed by higher-level provenance.
NetworkObservation,
}

/// Immutable version and capability metadata for one browser protocol adapter.
///
/// This value is deliberately not browser authority. It contains no browser
/// session, context, origin, node handle, action grant, credential, or network
/// permission. Higher layers may use it to fail closed when a required adapter
/// capability is absent, while all OriginWeave authority remains separately
/// validated.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BrowserProtocolAdapterDescriptor {
kind: BrowserProtocolKind,
adapter_version: String,
protocol_revision: String,
browser_revision: String,
capabilities: Vec<BrowserProtocolCapability>,
}

impl BrowserProtocolAdapterDescriptor {
/// Construct one explicit adapter descriptor.
///
/// Adapter version, upstream protocol revision, and browser revision are
/// separate bounded ASCII metadata tokens. This prevents an OriginWeave
/// adapter release from being mistaken for the WebDriver BiDi/CDP revision
/// or the pinned browser build it was validated against. The declared
/// capability list must be non-empty and duplicate-free and is normalized
/// into one stable order so caller ordering cannot change descriptor identity.
pub fn new(
kind: BrowserProtocolKind,
adapter_version: &str,
protocol_revision: &str,
browser_revision: &str,
capabilities: &[BrowserProtocolCapability],
) -> Result<Self, BrowserProtocolDescriptorError> {
if !metadata_token_is_valid(adapter_version) {
return Err(BrowserProtocolDescriptorError::InvalidAdapterVersion);
}
if !metadata_token_is_valid(protocol_revision) {
return Err(BrowserProtocolDescriptorError::InvalidProtocolRevision);
}
if !metadata_token_is_valid(browser_revision) {
return Err(BrowserProtocolDescriptorError::InvalidBrowserRevision);
}
if capabilities.is_empty() {
return Err(BrowserProtocolDescriptorError::EmptyCapabilities);
}

let mut canonical_capabilities = Vec::with_capacity(capabilities.len());
for capability in capabilities {
if canonical_capabilities.contains(capability) {
return Err(BrowserProtocolDescriptorError::DuplicateCapability);
}
canonical_capabilities.push(*capability);
}
canonical_capabilities.sort_unstable_by_key(|capability| capability_rank(*capability));

Ok(Self {
kind,
adapter_version: adapter_version.to_owned(),
protocol_revision: protocol_revision.to_owned(),
browser_revision: browser_revision.to_owned(),
capabilities: canonical_capabilities,
})
}

/// Return the explicitly declared browser protocol family.
#[must_use]
pub const fn kind(&self) -> BrowserProtocolKind {
self.kind
}

/// Return the bounded OriginWeave adapter-version metadata token.
#[must_use]
pub fn adapter_version(&self) -> &str {
&self.adapter_version
}

/// Return the bounded upstream browser-protocol revision metadata token.
#[must_use]
pub fn protocol_revision(&self) -> &str {
&self.protocol_revision
}

/// Return the bounded pinned browser-revision metadata token.
#[must_use]
pub fn browser_revision(&self) -> &str {
&self.browser_revision
}

/// Return the number of explicitly declared capabilities.
#[must_use]
pub fn capability_count(&self) -> usize {
self.capabilities.len()
}

/// Return whether this descriptor explicitly declares one capability.
#[must_use]
pub fn supports(&self, capability: BrowserProtocolCapability) -> bool {
self.capabilities.contains(&capability)
}
}

const fn capability_rank(capability: BrowserProtocolCapability) -> u8 {
match capability {
BrowserProtocolCapability::Navigation => 0,
BrowserProtocolCapability::SemanticObservation => 1,
BrowserProtocolCapability::TypedInput => 2,
BrowserProtocolCapability::NetworkObservation => 3,
}
}

fn metadata_token_is_valid(value: &str) -> bool {
!value.is_empty()
&& value.len() <= MAX_BROWSER_PROTOCOL_METADATA_BYTES
&& value.is_ascii()
&& value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'-'))
&& value.bytes().any(|byte| byte.is_ascii_alphanumeric())
}

/// Failure to construct canonical browser protocol adapter metadata.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserProtocolDescriptorError {
/// The adapter-version token was empty, oversized, non-ASCII, or malformed.
InvalidAdapterVersion,
/// The upstream protocol-revision token was empty, oversized, non-ASCII, or malformed.
InvalidProtocolRevision,
/// The browser-revision token was empty, oversized, non-ASCII, or malformed.
InvalidBrowserRevision,
/// The adapter declared no supported browser capability.
EmptyCapabilities,
/// The adapter declared the same capability more than once.
DuplicateCapability,
}

impl fmt::Display for BrowserProtocolDescriptorError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidAdapterVersion => formatter.write_str(
"browser protocol adapter version must be a bounded ASCII metadata token",
),
Self::InvalidProtocolRevision => formatter
.write_str("browser protocol revision must be a bounded ASCII metadata token"),
Self::InvalidBrowserRevision => {
formatter.write_str("browser revision must be a bounded ASCII metadata token")
}
Self::EmptyCapabilities => {
formatter.write_str("browser protocol adapter must declare at least one capability")
}
Self::DuplicateCapability => {
formatter.write_str("browser protocol adapter capabilities must be unique")
}
}
}
}

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