From 3059fead1ef0b6cf2f7df765b03c4b00a669b9cf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 07:05:56 +0900 Subject: [PATCH 1/2] test(policy): prove extension grants cannot disclose secrets --- .../tests/extension_secret_isolation.rs | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 crates/originweave-policy/tests/extension_secret_isolation.rs diff --git a/crates/originweave-policy/tests/extension_secret_isolation.rs b/crates/originweave-policy/tests/extension_secret_isolation.rs new file mode 100644 index 00000000..2ddd754f --- /dev/null +++ b/crates/originweave-policy/tests/extension_secret_isolation.rs @@ -0,0 +1,141 @@ +#![allow(clippy::expect_used)] + +use std::collections::BTreeSet; + +use originweave_core::{ + ActionIntentDigest, ActionKind, ActionRequest, ApprovalEvidence, BrowserSessionId, + BrowsingContextId, Capability, ExecutionPurpose, ExtensionAccessDecision, + ExtensionAccessRequest, ExtensionAgentCapability, ExtensionAgentGrant, ExtensionId, + InstructionSource, Origin, PolicyContext, RiskClass, RobotsDecision, SecretDelivery, + SessionMode, evaluate_extension_access, +}; +use originweave_policy::{Decision, DenialReason, evaluate}; + +const VALID_INTENT: &str = + "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + +fn extension_id() -> ExtensionId { + ExtensionId::parse("abcdefghijklmnopabcdefghijklmnop").expect("valid extension id") +} + +fn browser_session() -> BrowserSessionId { + BrowserSessionId::new(7).expect("nonzero browser session") +} + +fn browsing_context() -> BrowsingContextId { + BrowsingContextId::new(11).expect("nonzero browsing context") +} + +fn origin() -> Origin { + Origin::parse("https://login.example").expect("valid test origin") +} + +fn intent() -> ActionIntentDigest { + ActionIntentDigest::parse(VALID_INTENT).expect("valid intent digest") +} + +fn action_proposal_grant() -> ExtensionAgentGrant { + ExtensionAgentGrant::new( + extension_id(), + browser_session(), + browsing_context(), + [ExtensionAgentCapability::ProposeTypedAction], + ) +} + +fn assert_extension_can_propose(grant: &ExtensionAgentGrant) { + let request = ExtensionAccessRequest::new( + extension_id(), + browser_session(), + browsing_context(), + ExtensionAgentCapability::ProposeTypedAction, + ); + assert_eq!( + evaluate_extension_access(&request, Some(grant)), + ExtensionAccessDecision::Allow + ); +} + +fn secret_context(site: &Origin) -> PolicyContext { + PolicyContext::new( + SessionMode::AgentTask, + ExecutionPurpose::UserDelegatedTask, + BTreeSet::from([Capability::FillSecret]), + BTreeSet::from([site.clone()]), + BTreeSet::from([site.clone()]), + RobotsDecision::Allowed, + ApprovalEvidence::None, + ) +} + +#[test] +fn extension_action_grant_cannot_turn_raw_secret_delivery_into_authority() { + let grant = action_proposal_grant(); + assert_extension_can_propose(&grant); + + let site = origin(); + let proposed = ActionRequest::new( + ActionKind::FillSecret, + site.clone(), + site.clone(), + InstructionSource::User, + SecretDelivery::RawValue, + intent(), + ); + + assert_eq!( + evaluate(&proposed, &secret_context(&site)), + Decision::Deny(DenialReason::SecretBrokerRequired) + ); +} + +#[test] +fn extension_action_grant_cannot_skip_secret_broker_approval() { + let grant = action_proposal_grant(); + assert_extension_can_propose(&grant); + + let site = origin(); + let proposed = ActionRequest::new( + ActionKind::FillSecret, + site.clone(), + site.clone(), + InstructionSource::User, + SecretDelivery::BrokerHandle, + intent(), + ); + + assert_eq!( + evaluate(&proposed, &secret_context(&site)), + Decision::RequireApproval(RiskClass::R3) + ); +} + +#[test] +fn extension_action_grant_cannot_attach_broker_material_to_nonsecret_actions() { + let grant = action_proposal_grant(); + assert_extension_can_propose(&grant); + + let site = origin(); + let context = PolicyContext::new( + SessionMode::AgentTask, + ExecutionPurpose::UserDelegatedTask, + BTreeSet::from([Capability::Observe]), + BTreeSet::from([site.clone()]), + BTreeSet::new(), + RobotsDecision::Allowed, + ApprovalEvidence::None, + ); + let proposed = ActionRequest::new( + ActionKind::Observe, + site.clone(), + site, + InstructionSource::User, + SecretDelivery::BrokerHandle, + intent(), + ); + + assert_eq!( + evaluate(&proposed, &context), + Decision::Deny(DenialReason::UnexpectedSecretMaterial) + ); +} From e83749acd1cf5a0b778ba38eb9d6ed5a9bd1e68f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 07:22:02 +0900 Subject: [PATCH 2/2] test(policy): keep only unique extension approval boundary --- .../tests/extension_secret_isolation.rs | 53 +------------------ 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/crates/originweave-policy/tests/extension_secret_isolation.rs b/crates/originweave-policy/tests/extension_secret_isolation.rs index 2ddd754f..ad4293d0 100644 --- a/crates/originweave-policy/tests/extension_secret_isolation.rs +++ b/crates/originweave-policy/tests/extension_secret_isolation.rs @@ -9,7 +9,7 @@ use originweave_core::{ InstructionSource, Origin, PolicyContext, RiskClass, RobotsDecision, SecretDelivery, SessionMode, evaluate_extension_access, }; -use originweave_policy::{Decision, DenialReason, evaluate}; +use originweave_policy::{Decision, evaluate}; const VALID_INTENT: &str = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; @@ -68,27 +68,6 @@ fn secret_context(site: &Origin) -> PolicyContext { ) } -#[test] -fn extension_action_grant_cannot_turn_raw_secret_delivery_into_authority() { - let grant = action_proposal_grant(); - assert_extension_can_propose(&grant); - - let site = origin(); - let proposed = ActionRequest::new( - ActionKind::FillSecret, - site.clone(), - site.clone(), - InstructionSource::User, - SecretDelivery::RawValue, - intent(), - ); - - assert_eq!( - evaluate(&proposed, &secret_context(&site)), - Decision::Deny(DenialReason::SecretBrokerRequired) - ); -} - #[test] fn extension_action_grant_cannot_skip_secret_broker_approval() { let grant = action_proposal_grant(); @@ -109,33 +88,3 @@ fn extension_action_grant_cannot_skip_secret_broker_approval() { Decision::RequireApproval(RiskClass::R3) ); } - -#[test] -fn extension_action_grant_cannot_attach_broker_material_to_nonsecret_actions() { - let grant = action_proposal_grant(); - assert_extension_can_propose(&grant); - - let site = origin(); - let context = PolicyContext::new( - SessionMode::AgentTask, - ExecutionPurpose::UserDelegatedTask, - BTreeSet::from([Capability::Observe]), - BTreeSet::from([site.clone()]), - BTreeSet::new(), - RobotsDecision::Allowed, - ApprovalEvidence::None, - ); - let proposed = ActionRequest::new( - ActionKind::Observe, - site.clone(), - site, - InstructionSource::User, - SecretDelivery::BrokerHandle, - intent(), - ); - - assert_eq!( - evaluate(&proposed, &context), - Decision::Deny(DenialReason::UnexpectedSecretMaterial) - ); -}