| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::{ |
| capability::CapabilitySource, |
| config::{CapabilityAllowlistKey, CapabilityAllowlistSource, RuntimeConfig}, |
| model::error::ModelError, |
| }, |
| fuchsia_zircon as zx, |
| log::{error, warn}, |
| moniker::{AbsoluteMoniker, ChildMoniker, ExtendedMoniker}, |
| std::sync::{Arc, Weak}, |
| thiserror::Error, |
| }; |
| /// Errors returned by the PolicyChecker and the ScopedPolicyChecker. |
| #[derive(Debug, Clone, Error)] |
| pub enum PolicyError { |
| #[error("security policy was unavailable to check")] |
| PolicyUnavailable, |
| |
| #[error("security policy disallows \"{policy}\" job policy for \"{moniker}\"")] |
| JobPolicyDisallowed { policy: String, moniker: AbsoluteMoniker }, |
| |
| #[error("security policy was unable to extract the source from the routed capability")] |
| InvalidCapabilitySource, |
| |
| #[error("security policy disallows \"{cap}\" from \"{source_moniker}\" being used at \"{target_moniker}\"")] |
| CapabilityUseDisallowed { |
| cap: String, |
| source_moniker: ExtendedMoniker, |
| target_moniker: AbsoluteMoniker, |
| }, |
| } |
| |
| impl PolicyError { |
| fn job_policy_disallowed(policy: impl Into<String>, moniker: &AbsoluteMoniker) -> Self { |
| PolicyError::JobPolicyDisallowed { policy: policy.into(), moniker: moniker.clone() } |
| } |
| |
| fn capability_use_disallowed( |
| cap: impl Into<String>, |
| source_moniker: &ExtendedMoniker, |
| target_moniker: &AbsoluteMoniker, |
| ) -> Self { |
| PolicyError::CapabilityUseDisallowed { |
| cap: cap.into(), |
| source_moniker: source_moniker.clone(), |
| target_moniker: target_moniker.clone(), |
| } |
| } |
| |
| /// Convert this error into its approximate `zx::Status` equivalent. |
| pub fn as_zx_status(&self) -> zx::Status { |
| zx::Status::ACCESS_DENIED |
| } |
| } |
| |
| /// Evaluates security policy globally across the entire Model and all realms. |
| /// This is used to enforce runtime capability routing restrictions across all |
| /// realms to prevent high privilleged capabilities from being routed to |
| /// components outside of the list defined in the runtime configs security |
| /// policy. |
| pub struct GlobalPolicyChecker { |
| /// The runtime configuration containing the security policy to apply. |
| config: Arc<RuntimeConfig>, |
| } |
| |
| impl GlobalPolicyChecker { |
| /// Constructs a new PolicyChecker object configured by the |
| /// RuntimeConfig::SecurityPolicy. |
| pub fn new(config: Arc<RuntimeConfig>) -> Self { |
| Self { config: config } |
| } |
| |
| /// Absolute monikers contain instance_id. This change normalizes all |
| /// incoming instance identifiers to 0 so for example |
| /// /foo:1/bar:0 -> /foo:0/bar:0. |
| fn strip_moniker_instance_id(moniker: &AbsoluteMoniker) -> AbsoluteMoniker { |
| let mut normalized_children = Vec::with_capacity(moniker.path().len()); |
| for child in moniker.path().iter() { |
| normalized_children.push(ChildMoniker::new( |
| child.name().to_string(), |
| child.collection().map(String::from), |
| 0, |
| )); |
| } |
| AbsoluteMoniker::new(normalized_children) |
| } |
| |
| fn get_policy_key<'a>( |
| capability_source: &'a CapabilitySource, |
| ) -> Result<CapabilityAllowlistKey, ModelError> { |
| Ok(match &capability_source { |
| CapabilitySource::Namespace { capability } => CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: capability |
| .source_name() |
| .ok_or(PolicyError::InvalidCapabilitySource)? |
| .clone(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: capability.type_name(), |
| }, |
| CapabilitySource::Component { capability, realm } => CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance( |
| realm.upgrade()?.abs_moniker.clone(), |
| ), |
| source_name: capability |
| .source_name() |
| .ok_or(PolicyError::InvalidCapabilitySource)? |
| .clone(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: capability.type_name(), |
| }, |
| CapabilitySource::Builtin { capability } => CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: capability.source_name().clone(), |
| source: CapabilityAllowlistSource::Self_, |
| capability: capability.type_name(), |
| }, |
| CapabilitySource::Framework { capability, scope_moniker } => CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance(scope_moniker.clone()), |
| source_name: capability.source_name().clone(), |
| source: CapabilityAllowlistSource::Framework, |
| capability: capability.type_name(), |
| }, |
| CapabilitySource::Capability { source_capability, realm } => CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance( |
| realm.upgrade()?.abs_moniker.clone(), |
| ), |
| source_name: source_capability |
| .source_name() |
| .ok_or(PolicyError::InvalidCapabilitySource)? |
| .clone(), |
| source: CapabilityAllowlistSource::Capability, |
| capability: source_capability.type_name(), |
| }, |
| }) |
| } |
| |
| /// Returns Ok(()) if the provided capability source can be routed to the |
| /// given target_moniker, else a descriptive PolicyError. |
| pub fn can_route_capability<'a>( |
| &self, |
| capability_source: &'a CapabilitySource, |
| target_moniker: &'a AbsoluteMoniker, |
| ) -> Result<(), ModelError> { |
| let target_moniker = Self::strip_moniker_instance_id(&target_moniker); |
| let policy_key = Self::get_policy_key(capability_source).map_err(|e| { |
| error!("Security policy could not generate a policy key for `{}`", capability_source); |
| e |
| })?; |
| |
| match self.config.security_policy.capability_policy.get(&policy_key) { |
| Some(allowed_monikers) => match allowed_monikers.get(&target_moniker) { |
| Some(_) => Ok(()), |
| None => { |
| warn!( |
| "Security policy prevented `{}` from `{}` being routed to `{}`.", |
| policy_key.source_name, policy_key.source_moniker, target_moniker |
| ); |
| Err(ModelError::PolicyError { |
| err: PolicyError::capability_use_disallowed( |
| policy_key.source_name.str(), |
| &policy_key.source_moniker, |
| &target_moniker, |
| ), |
| }) |
| } |
| }, |
| None => Ok(()), |
| } |
| } |
| } |
| |
| /// Evaluates security policy relative to a specific Realm (based on that Realm's AbsoluteMoniker). |
| pub struct ScopedPolicyChecker { |
| /// The runtime configuration containing the security policy to apply. |
| config: Weak<RuntimeConfig>, |
| |
| /// The absolute moniker of the realm that policy will be evaluated for. |
| moniker: AbsoluteMoniker, |
| } |
| |
| impl ScopedPolicyChecker { |
| pub fn new(config: Weak<RuntimeConfig>, moniker: AbsoluteMoniker) -> Self { |
| ScopedPolicyChecker { config, moniker } |
| } |
| |
| // This interface is super simple for now since there's only two allowlists. In the future |
| // we'll probably want a different interface than an individual function per policy item. |
| |
| pub fn ambient_mark_vmo_exec_allowed(&self) -> Result<(), PolicyError> { |
| let config = self.config.upgrade().ok_or(PolicyError::PolicyUnavailable)?; |
| if config.security_policy.job_policy.ambient_mark_vmo_exec.contains(&self.moniker) { |
| Ok(()) |
| } else { |
| Err(PolicyError::job_policy_disallowed("ambient_mark_vmo_exec", &self.moniker)) |
| } |
| } |
| |
| pub fn main_process_critical_allowed(&self) -> Result<(), PolicyError> { |
| let config = self.config.upgrade().ok_or(PolicyError::PolicyUnavailable)?; |
| if config.security_policy.job_policy.main_process_critical.contains(&self.moniker) { |
| Ok(()) |
| } else { |
| Err(PolicyError::job_policy_disallowed("main_process_critical", &self.moniker)) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, |
| crate::{ |
| capability::{ComponentCapability, InternalCapability}, |
| config::{JobPolicyAllowlists, SecurityPolicy}, |
| model::{ |
| context::WeakModelContext, |
| environment::{Environment, RunnerRegistry}, |
| hooks::Hooks, |
| realm::{Realm, WeakExtendedRealm, WeakRealm}, |
| resolver::ResolverRegistry, |
| }, |
| }, |
| cm_rust::*, |
| fidl_fuchsia_sys2 as fsys, |
| matches::assert_matches, |
| moniker::ChildMoniker, |
| std::{collections::HashMap, collections::HashSet, iter::FromIterator, sync::Arc}, |
| }; |
| |
| /// Creates a RuntimeConfig based on the capability allowlist entries provided during |
| /// construction. |
| struct CapabilityAllowlistConfigBuilder { |
| capability_policy: HashMap<CapabilityAllowlistKey, HashSet<AbsoluteMoniker>>, |
| } |
| |
| impl CapabilityAllowlistConfigBuilder { |
| pub fn new() -> Self { |
| Self { capability_policy: HashMap::new() } |
| } |
| |
| /// Add a new entry to the configuration. |
| pub fn add<'a>( |
| &'a mut self, |
| key: CapabilityAllowlistKey, |
| value: Vec<AbsoluteMoniker>, |
| ) -> &'a mut Self { |
| let value_set = HashSet::from_iter(value.iter().cloned()); |
| self.capability_policy.insert(key, value_set); |
| self |
| } |
| |
| /// Creates a configuration from the provided policies. |
| pub fn build(&self) -> Arc<RuntimeConfig> { |
| let config = Arc::new(RuntimeConfig { |
| security_policy: SecurityPolicy { |
| job_policy: JobPolicyAllowlists { |
| ambient_mark_vmo_exec: vec![], |
| main_process_critical: vec![], |
| }, |
| capability_policy: self.capability_policy.clone(), |
| }, |
| ..Default::default() |
| }); |
| config |
| } |
| } |
| |
| #[test] |
| fn scoped_policy_checker_vmex() { |
| macro_rules! assert_vmex_allowed_matches { |
| ($config:expr, $moniker:expr, $expected:pat) => { |
| let result = ScopedPolicyChecker::new($config.clone(), $moniker.clone()) |
| .ambient_mark_vmo_exec_allowed(); |
| assert_matches!(result, $expected); |
| }; |
| } |
| macro_rules! assert_vmex_disallowed { |
| ($config:expr, $moniker:expr) => { |
| assert_vmex_allowed_matches!( |
| $config, |
| $moniker, |
| Err(PolicyError::JobPolicyDisallowed { .. }) |
| ); |
| }; |
| } |
| let strong_config = Arc::new(RuntimeConfig::default()); |
| let config = Arc::downgrade(&strong_config); |
| assert_vmex_disallowed!(config, AbsoluteMoniker::root()); |
| assert_vmex_disallowed!(config, AbsoluteMoniker::from(vec!["foo:0"])); |
| |
| let allowed1 = AbsoluteMoniker::from(vec!["foo:0", "bar:0"]); |
| let allowed2 = AbsoluteMoniker::from(vec!["baz:0", "fiz:0"]); |
| let strong_config = Arc::new(RuntimeConfig { |
| security_policy: SecurityPolicy { |
| job_policy: JobPolicyAllowlists { |
| ambient_mark_vmo_exec: vec![allowed1.clone(), allowed2.clone()], |
| main_process_critical: vec![allowed1.clone(), allowed2.clone()], |
| }, |
| capability_policy: HashMap::new(), |
| }, |
| ..Default::default() |
| }); |
| let config = Arc::downgrade(&strong_config); |
| assert_vmex_allowed_matches!(config, allowed1, Ok(())); |
| assert_vmex_allowed_matches!(config, allowed2, Ok(())); |
| assert_vmex_disallowed!(config, AbsoluteMoniker::root()); |
| assert_vmex_disallowed!(config, allowed1.parent().unwrap()); |
| assert_vmex_disallowed!(config, allowed1.child(ChildMoniker::from("baz:0"))); |
| |
| drop(strong_config); |
| assert_vmex_allowed_matches!(config, allowed1, Err(PolicyError::PolicyUnavailable)); |
| assert_vmex_allowed_matches!(config, allowed2, Err(PolicyError::PolicyUnavailable)); |
| } |
| |
| #[test] |
| fn scoped_policy_checker_critical_allowed() { |
| macro_rules! assert_critical_allowed_matches { |
| ($config:expr, $moniker:expr, $expected:pat) => { |
| let result = ScopedPolicyChecker::new($config.clone(), $moniker.clone()) |
| .main_process_critical_allowed(); |
| assert_matches!(result, $expected); |
| }; |
| } |
| macro_rules! assert_critical_disallowed { |
| ($config:expr, $moniker:expr) => { |
| assert_critical_allowed_matches!( |
| $config, |
| $moniker, |
| Err(PolicyError::JobPolicyDisallowed { .. }) |
| ); |
| }; |
| } |
| let strong_config = Arc::new(RuntimeConfig::default()); |
| let config = Arc::downgrade(&strong_config); |
| assert_critical_disallowed!(config, AbsoluteMoniker::root()); |
| assert_critical_disallowed!(config, AbsoluteMoniker::from(vec!["foo:0"])); |
| |
| let allowed1 = AbsoluteMoniker::from(vec!["foo:0", "bar:0"]); |
| let allowed2 = AbsoluteMoniker::from(vec!["baz:0", "fiz:0"]); |
| let strong_config = Arc::new(RuntimeConfig { |
| security_policy: SecurityPolicy { |
| job_policy: JobPolicyAllowlists { |
| ambient_mark_vmo_exec: vec![allowed1.clone(), allowed2.clone()], |
| main_process_critical: vec![allowed1.clone(), allowed2.clone()], |
| }, |
| capability_policy: HashMap::new(), |
| }, |
| ..Default::default() |
| }); |
| let config = Arc::downgrade(&strong_config); |
| assert_critical_allowed_matches!(config, allowed1, Ok(())); |
| assert_critical_allowed_matches!(config, allowed2, Ok(())); |
| assert_critical_disallowed!(config, AbsoluteMoniker::root()); |
| assert_critical_disallowed!(config, allowed1.parent().unwrap()); |
| assert_critical_disallowed!(config, allowed1.child(ChildMoniker::from("baz:0"))); |
| |
| drop(strong_config); |
| assert_critical_allowed_matches!(config, allowed1, Err(PolicyError::PolicyUnavailable)); |
| assert_critical_allowed_matches!(config, allowed2, Err(PolicyError::PolicyUnavailable)); |
| } |
| |
| #[test] |
| fn global_policy_checker_can_route_capability_framework_cap() { |
| let mut config_builder = CapabilityAllowlistConfigBuilder::new(); |
| config_builder.add( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::from(vec![ |
| "foo:0", "bar:0", |
| ])), |
| source_name: CapabilityName::from("running"), |
| source: CapabilityAllowlistSource::Framework, |
| capability: CapabilityTypeName::Event, |
| }, |
| vec![ |
| AbsoluteMoniker::from(vec!["foo:0", "bar:0"]), |
| AbsoluteMoniker::from(vec!["foo:0", "bar:0", "baz:0"]), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(config_builder.build()); |
| |
| let event_capability = CapabilitySource::Framework { |
| capability: InternalCapability::Event(CapabilityName::from("running")), |
| scope_moniker: AbsoluteMoniker::from(vec!["foo:0", "bar:0"]), |
| }; |
| let valid_path_0 = AbsoluteMoniker::from(vec!["foo:0", "bar:0"]); |
| let valid_path_1 = AbsoluteMoniker::from(vec!["foo:0", "bar:0", "baz:0"]); |
| let invalid_path_0 = AbsoluteMoniker::from(vec!["foobar:0"]); |
| let invalid_path_1 = AbsoluteMoniker::from(vec!["foo:0", "bar:0", "foobar:0"]); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&event_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&event_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&event_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&event_capability, &invalid_path_1), |
| Err(_) |
| ); |
| } |
| |
| #[test] |
| fn global_policy_checker_can_route_capability_namespace_cap() { |
| let mut config_builder = CapabilityAllowlistConfigBuilder::new(); |
| config_builder.add( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: CapabilityName::from("fuchsia.kernel.RootResource"), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| }, |
| vec![ |
| AbsoluteMoniker::from(vec!["root:0"]), |
| AbsoluteMoniker::from(vec!["root:0", "bootstrap:0"]), |
| AbsoluteMoniker::from(vec!["root:0", "core:0"]), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(config_builder.build()); |
| |
| let protocol_capability = CapabilitySource::Namespace { |
| capability: ComponentCapability::Protocol(ProtocolDecl { |
| name: "fuchsia.kernel.RootResource".into(), |
| source_path: "/svc/fuchsia.kernel.RootResource".parse().unwrap(), |
| }), |
| }; |
| let valid_path_0 = AbsoluteMoniker::from(vec!["root:0"]); |
| let valid_path_1 = AbsoluteMoniker::from(vec!["root:0", "bootstrap:0"]); |
| let valid_path_2 = AbsoluteMoniker::from(vec!["root:0", "core:0"]); |
| let invalid_path_0 = AbsoluteMoniker::from(vec!["foobar:0"]); |
| let invalid_path_1 = AbsoluteMoniker::from(vec!["foo:0", "bar:0", "foobar:0"]); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_2), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1), |
| Err(_) |
| ); |
| } |
| |
| #[test] |
| fn global_policy_checker_can_route_capability_component_cap() { |
| let mut config_builder = CapabilityAllowlistConfigBuilder::new(); |
| config_builder.add( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::from(vec![ |
| "foo:0", |
| ])), |
| source_name: CapabilityName::from("fuchsia.foo.FooBar"), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Protocol, |
| }, |
| vec![ |
| AbsoluteMoniker::from(vec!["foo:0"]), |
| AbsoluteMoniker::from(vec!["root:0", "bootstrap:0"]), |
| AbsoluteMoniker::from(vec!["root:0", "core:0"]), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(config_builder.build()); |
| |
| // Create a fake realm. |
| let resolver = ResolverRegistry::new(); |
| let realm = Realm::new( |
| Arc::new(Environment::new_root(RunnerRegistry::default(), resolver)), |
| vec!["foo:0"].into(), |
| "test:///foo".into(), |
| fsys::StartupMode::Lazy, |
| WeakModelContext::default(), |
| WeakExtendedRealm::Component(WeakRealm::default()), |
| Arc::new(Hooks::new(None)), |
| ); |
| let weak_realm = realm.as_weak(); |
| |
| let protocol_capability = CapabilitySource::Component { |
| capability: ComponentCapability::Protocol(ProtocolDecl { |
| name: "fuchsia.foo.FooBar".into(), |
| source_path: "/svc/fuchsia.foo.FooBar".parse().unwrap(), |
| }), |
| realm: weak_realm, |
| }; |
| let valid_path_0 = AbsoluteMoniker::from(vec!["root:0", "bootstrap:0"]); |
| let valid_path_1 = AbsoluteMoniker::from(vec!["root:0", "core:0"]); |
| let invalid_path_0 = AbsoluteMoniker::from(vec!["foobar:0"]); |
| let invalid_path_1 = AbsoluteMoniker::from(vec!["foo:0", "bar:0", "foobar:0"]); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1), |
| Err(_) |
| ); |
| } |
| |
| #[test] |
| fn global_policy_checker_can_route_capability_capability_cap() { |
| let mut config_builder = CapabilityAllowlistConfigBuilder::new(); |
| config_builder.add( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentInstance(AbsoluteMoniker::from(vec![ |
| "foo:0", |
| ])), |
| source_name: CapabilityName::from("cache"), |
| source: CapabilityAllowlistSource::Capability, |
| capability: CapabilityTypeName::Storage, |
| }, |
| vec![ |
| AbsoluteMoniker::from(vec!["foo:0"]), |
| AbsoluteMoniker::from(vec!["root:0", "bootstrap:0"]), |
| AbsoluteMoniker::from(vec!["root:0", "core:0"]), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(config_builder.build()); |
| |
| // Create a fake realm. |
| let resolver = ResolverRegistry::new(); |
| let realm = Realm::new( |
| Arc::new(Environment::new_root(RunnerRegistry::default(), resolver)), |
| vec!["foo:0"].into(), |
| "test:///foo".into(), |
| fsys::StartupMode::Lazy, |
| WeakModelContext::default(), |
| WeakExtendedRealm::Component(WeakRealm::default()), |
| Arc::new(Hooks::new(None)), |
| ); |
| let weak_realm = realm.as_weak(); |
| |
| let protocol_capability = CapabilitySource::Capability { |
| source_capability: ComponentCapability::Storage(StorageDecl { |
| backing_dir: "/cache".into(), |
| name: "cache".into(), |
| source: StorageDirectorySource::Parent, |
| subdir: None, |
| }), |
| realm: weak_realm, |
| }; |
| let valid_path_0 = AbsoluteMoniker::from(vec!["root:0", "bootstrap:0"]); |
| let valid_path_1 = AbsoluteMoniker::from(vec!["root:0", "core:0"]); |
| let invalid_path_0 = AbsoluteMoniker::from(vec!["foobar:0"]); |
| let invalid_path_1 = AbsoluteMoniker::from(vec!["foo:0", "bar:0", "foobar:0"]); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1), |
| Err(_) |
| ); |
| } |
| |
| #[test] |
| fn global_policy_checker_can_route_capability_builtin_cap() { |
| let mut config_builder = CapabilityAllowlistConfigBuilder::new(); |
| config_builder.add( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: CapabilityName::from("hub"), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Directory, |
| }, |
| vec![ |
| AbsoluteMoniker::from(vec!["root:0"]), |
| AbsoluteMoniker::from(vec!["root:0", "core:0"]), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(config_builder.build()); |
| |
| let dir_capability = CapabilitySource::Builtin { |
| capability: InternalCapability::Directory(CapabilityName::from("hub")), |
| }; |
| let valid_path_0 = AbsoluteMoniker::from(vec!["root:0"]); |
| let valid_path_1 = AbsoluteMoniker::from(vec!["root:0", "core:0"]); |
| let invalid_path_0 = AbsoluteMoniker::from(vec!["foobar:0"]); |
| let invalid_path_1 = AbsoluteMoniker::from(vec!["foo:0", "bar:0", "foobar:0"]); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &invalid_path_1), |
| Err(_) |
| ); |
| } |
| |
| #[test] |
| fn global_policy_checker_can_route_capability_with_instance_ids_cap() { |
| let mut config_builder = CapabilityAllowlistConfigBuilder::new(); |
| config_builder.add( |
| CapabilityAllowlistKey { |
| source_moniker: ExtendedMoniker::ComponentManager, |
| source_name: CapabilityName::from("hub"), |
| source: CapabilityAllowlistSource::Self_, |
| capability: CapabilityTypeName::Directory, |
| }, |
| vec![ |
| AbsoluteMoniker::from(vec!["root:0"]), |
| AbsoluteMoniker::from(vec!["root:0", "core:0"]), |
| ], |
| ); |
| let global_policy_checker = GlobalPolicyChecker::new(config_builder.build()); |
| |
| let dir_capability = CapabilitySource::Builtin { |
| capability: InternalCapability::Directory(CapabilityName::from("hub")), |
| }; |
| let valid_path_0 = AbsoluteMoniker::from(vec!["root:1"]); |
| let valid_path_1 = AbsoluteMoniker::from(vec!["root:5", "core:3"]); |
| let invalid_path_0 = AbsoluteMoniker::from(vec!["foobar:0"]); |
| let invalid_path_1 = AbsoluteMoniker::from(vec!["foo:0", "bar:2", "foobar:0"]); |
| |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &valid_path_0), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &valid_path_1), |
| Ok(()) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &invalid_path_0), |
| Err(_) |
| ); |
| assert_matches!( |
| global_policy_checker.can_route_capability(&dir_capability, &invalid_path_1), |
| Err(_) |
| ); |
| } |
| } |