diff --git a/CHANGELOG.md b/CHANGELOG.md index f747adeae..cd59f53fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to OriginWeave are documented in this file. The format follo - Refreshed the product-gap queue to 126 open pull requests (54 ready, 72 draft) after #190, #188, #185, #192, #182, #184, #115, #181, #116, #117, #118, #183, #114, #127, #112, #109, #186, #110, #108, #111, #174, and #113 were merged into their immediate stacked prerequisites. PRs #147, #146, #145, #144, #143, #142, #141, #139, #136, #132, #129, and #128 moved to ready after exact-head checks and thread review; these are queue-consolidation results, not protected-main shipment. ### Added +- Added a fail-closed Chrome-permission separation boundary so reviewed Manifest V3 compatibility permissions, including `downloads` and `nativeMessaging`, can never mint any OriginWeave Agent action authority. - Corrected the 2026-08-26 product-gap snapshot with current #229 presentation-identity evidence, stacked-only #205 integration evidence, current base/head pairs, the 126-PR queue count, explicit root-versus-child merge ordering, and the active GitHub counted-approval gate. - Refreshed the product and technical gap baseline onto the 2026-08-26 live inventory: 126 open pull requests (54 ready, 72 draft), protected-main promotion of #168/#194/#196/#216/#151, a verified maintenance-loop record (supersession closure of #153, conflict reconciliations on #37/#149/#152/#173/#175, issue #212 option-(b) authorization on #43, Strix vuln-0001 homoglyph remediation on #124), provider-rerun outcome evidence, an organization review-pipeline congestion record, and refreshed merge-order queue guidance. Documentation evidence contracts were aligned to the same snapshot so the baseline, its dated markers, and the pinned exact-head rows cannot silently diverge. diff --git a/crates/originweave-core/Cargo.toml b/crates/originweave-core/Cargo.toml index dcda2a6c4..48a17e08e 100644 --- a/crates/originweave-core/Cargo.toml +++ b/crates/originweave-core/Cargo.toml @@ -11,7 +11,7 @@ homepage.workspace = true publish = false [lib] -path = "src/root.rs" +path = "src/crate_root.rs" [dependencies] unicode-normalization = "=0.1.25" diff --git a/crates/originweave-core/src/chrome_permission_authority.rs b/crates/originweave-core/src/chrome_permission_authority.rs new file mode 100644 index 000000000..264c45f1f --- /dev/null +++ b/crates/originweave-core/src/chrome_permission_authority.rs @@ -0,0 +1,73 @@ +//! Separation between Chrome extension compatibility permissions and Agent authority. + +use crate::ActionKind; +use std::fmt; + +/// Why a Chrome extension permission cannot authorize an OriginWeave Agent action. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ChromePermissionAuthorityError { + /// The permission names a reviewed Chrome compatibility surface, not Agent authority. + CompatibilitySurfaceOnly, + /// The permission is not a reviewed Chrome surface and still grants no Agent capability. + UnrecognizedPermission, +} + +impl fmt::Display for ChromePermissionAuthorityError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let message = match self { + Self::CompatibilitySurfaceOnly => { + "Chrome compatibility permission cannot authorize an OriginWeave Agent action" + } + Self::UnrecognizedPermission => { + "Chrome permission is not a reviewed compatibility surface and cannot authorize an OriginWeave Agent action" + } + }; + formatter.write_str(message) + } +} + +impl std::error::Error for ChromePermissionAuthorityError {} + +const REVIEWED_CHROME_COMPATIBILITY_PERMISSIONS: &[&str] = &[ + "bookmarks", + "commands", + "declarativeNetRequest", + "declarativeNetRequestWithHostAccess", + "downloads", + "history", + "nativeMessaging", + "scripting", + "sidePanel", + "storage", + "tabs", + "windows", +]; + +/// Refuse to treat a Chrome extension permission as OriginWeave Agent authority. +/// +/// A successful Chrome compatibility proof never becomes an OriginWeave Agent +/// capability. Adapters must keep browser compatibility evidence and explicit +/// OriginWeave grants separate and call this boundary before exposing a typed +/// action to policy. The action is accepted only to make that separation +/// explicit at the adapter boundary; no action kind can make this function +/// return success. +pub fn chrome_permission_authorizes_agent_action( + permission: &str, + _action: ActionKind, +) -> Result<(), ChromePermissionAuthorityError> { + if !is_exact_chrome_permission_token(permission) { + return Err(ChromePermissionAuthorityError::UnrecognizedPermission); + } + if REVIEWED_CHROME_COMPATIBILITY_PERMISSIONS.contains(&permission) { + return Err(ChromePermissionAuthorityError::CompatibilitySurfaceOnly); + } + Err(ChromePermissionAuthorityError::UnrecognizedPermission) +} + +fn is_exact_chrome_permission_token(permission: &str) -> bool { + let mut characters = permission.chars(); + let Some(first) = characters.next() else { + return false; + }; + first.is_ascii_lowercase() && characters.all(|character| character.is_ascii_alphabetic()) +} diff --git a/crates/originweave-core/src/crate_root.rs b/crates/originweave-core/src/crate_root.rs new file mode 100644 index 000000000..f2419c7de --- /dev/null +++ b/crates/originweave-core/src/crate_root.rs @@ -0,0 +1,22 @@ +//! OriginWeave core contracts plus narrowly scoped adapter authority boundaries. +//! +//! The existing deterministic core remains implemented in `lib.rs`; this crate +//! root re-exports that protected-main API and adds the independently reviewed +//! Chrome-permission separation boundary without weakening existing authority. + +#![forbid(unsafe_code)] +#![deny(missing_docs)] + +#[path = "lib.rs"] +mod base; +pub use base::*; + +mod chrome_permission_authority; +pub use chrome_permission_authority::{ + ChromePermissionAuthorityError, chrome_permission_authorizes_agent_action, +}; + +/// Stateless MCP routing validation that maps only explicit tools to typed actions. +pub mod mcp; +/// Deterministic fail-closed release benchmark acceptance aggregation. +pub mod release_acceptance; diff --git a/crates/originweave-core/src/root.rs b/crates/originweave-core/src/root.rs deleted file mode 100644 index c47a136d4..000000000 --- a/crates/originweave-core/src/root.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Shared security and governance contracts for OriginWeave. -//! -//! The historical core contracts remain source-compatible while adapter-specific -//! boundaries can live in focused modules without changing their authority model. - -#![forbid(unsafe_code)] -#![deny(missing_docs)] - -#[path = "lib.rs"] -mod contracts; - -pub use contracts::*; - -/// Stateless MCP routing validation that maps only explicit tools to typed actions. -pub mod mcp; -/// Deterministic fail-closed release benchmark acceptance aggregation. -pub mod release_acceptance; diff --git a/crates/originweave-core/tests/chrome_permission_authority.rs b/crates/originweave-core/tests/chrome_permission_authority.rs new file mode 100644 index 000000000..c78bf7331 --- /dev/null +++ b/crates/originweave-core/tests/chrome_permission_authority.rs @@ -0,0 +1,61 @@ +use originweave_core::{ + ActionKind, ChromePermissionAuthorityError, chrome_permission_authorizes_agent_action, +}; + +#[test] +fn chrome_compatibility_permissions_never_mint_agent_authority() { + for permission in [ + "downloads", + "bookmarks", + "history", + "storage", + "tabs", + "windows", + "scripting", + "commands", + "sidePanel", + "declarativeNetRequest", + "declarativeNetRequestWithHostAccess", + "nativeMessaging", + ] { + assert_eq!( + chrome_permission_authorizes_agent_action(permission, ActionKind::Download), + Err(ChromePermissionAuthorityError::CompatibilitySurfaceOnly) + ); + } +} + +#[test] +fn malformed_or_unreviewed_chrome_permissions_remain_unrecognized() { + for permission in [ + "", + "DOWNLOADS", + "downloads\nhttps://example.invalid", + "cookies", + "downloads ", + ] { + assert_eq!( + chrome_permission_authorizes_agent_action(permission, ActionKind::Download), + Err(ChromePermissionAuthorityError::UnrecognizedPermission) + ); + } +} + +#[test] +fn chrome_permission_authority_errors_are_standard_credential_safe_errors() { + let cases = [ + ( + ChromePermissionAuthorityError::CompatibilitySurfaceOnly, + "Chrome compatibility permission cannot authorize an OriginWeave Agent action", + ), + ( + ChromePermissionAuthorityError::UnrecognizedPermission, + "Chrome permission is not a reviewed compatibility surface and cannot authorize an OriginWeave Agent action", + ), + ]; + + for (error, expected_message) in cases { + assert_eq!(error.to_string(), expected_message); + assert!(std::error::Error::source(&error).is_none()); + } +}