routing_test_helpers/
policy.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::Error;
6use assert_matches::assert_matches;
7use async_trait::async_trait;
8use cm_config::{
9    AllowlistEntry, AllowlistEntryBuilder, CapabilityAllowlistKey, CapabilityAllowlistSource,
10    ChildPolicyAllowlists, DebugCapabilityAllowlistEntry, DebugCapabilityKey, JobPolicyAllowlists,
11    SecurityPolicy,
12};
13use cm_rust::{CapabilityTypeName, ProtocolDecl, StorageDecl, StorageDirectorySource};
14use cm_types::Name;
15use fidl_fuchsia_component_decl as fdecl;
16use moniker::{ExtendedMoniker, Moniker};
17use routing::capability_source::{
18    BuiltinSource, CapabilitySource, CapabilityToCapabilitySource, ComponentCapability,
19    ComponentSource, FrameworkSource, InternalCapability, NamespaceSource,
20};
21use routing::component_instance::ComponentInstanceInterface;
22use routing::policy::GlobalPolicyChecker;
23use std::collections::{HashMap, HashSet};
24use std::sync::Arc;
25
26/// These GlobalPolicyChecker tests are run under multiple contexts, e.g. both on Fuchsia under
27/// component_manager and on the build host under cm_fidl_analyzer. This macro helps ensure that all
28/// tests are run in each context.
29#[macro_export]
30macro_rules! instantiate_global_policy_checker_tests {
31    ($fixture_impl:path) => {
32        // New GlobalPolicyCheckerTest tests must be added to this list to run.
33        instantiate_global_policy_checker_tests! {
34            $fixture_impl,
35            global_policy_checker_can_route_capability_framework_cap,
36            global_policy_checker_can_route_capability_namespace_cap,
37            global_policy_checker_can_route_capability_component_cap,
38            global_policy_checker_can_route_capability_capability_cap,
39            global_policy_checker_can_route_debug_capability_capability_cap,
40            global_policy_checker_can_route_debug_capability_with_realm_allowlist_entry,
41            global_policy_checker_can_route_debug_capability_with_collection_allowlist_entry,
42            global_policy_checker_can_route_capability_builtin_cap,
43            global_policy_checker_can_route_capability_with_realm_allowlist_entry,
44            global_policy_checker_can_route_capability_with_collection_allowlist_entry,
45        }
46    };
47    ($fixture_impl:path, $test:ident, $($remaining:ident),+ $(,)?) => {
48        instantiate_global_policy_checker_tests! { $fixture_impl, $test }
49        instantiate_global_policy_checker_tests! { $fixture_impl, $($remaining),+ }
50    };
51    ($fixture_impl:path, $test:ident) => {
52        fn $test() -> Result<(), Error> {
53            let mut executor = fuchsia_async::LocalExecutor::new();
54            executor.run_singlethreaded(<$fixture_impl as Default>::default().$test())
55        }
56    };
57}
58
59// Tests `GlobalPolicyChecker` for implementations of `ComponentInstanceInterface`.
60#[async_trait]
61pub trait GlobalPolicyCheckerTest<C>
62where
63    C: ComponentInstanceInterface + 'static,
64{
65    // Creates a `ComponentInstanceInterface` with the given `Moniker`.
66    async fn make_component(&self, moniker: Moniker) -> Arc<C>;
67
68    // Tests `GlobalPolicyChecker::can_route_capability()` for framework capability sources.
69    async fn global_policy_checker_can_route_capability_framework_cap(&self) -> Result<(), Error> {
70        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
71        policy_builder.add_capability_policy(
72            CapabilityAllowlistKey {
73                source_moniker: ExtendedMoniker::ComponentInstance(
74                    Moniker::try_from(vec!["foo", "bar"]).unwrap(),
75                ),
76                source_name: "fuchsia.component.Realm".parse().unwrap(),
77                source: CapabilityAllowlistSource::Framework,
78                capability: CapabilityTypeName::Protocol,
79            },
80            vec![
81                AllowlistEntryBuilder::new().exact("foo").exact("bar").build(),
82                AllowlistEntryBuilder::new().exact("foo").exact("bar").exact("baz").build(),
83            ],
84        );
85        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
86        let component = self.make_component(vec!["foo:0", "bar:0"].try_into().unwrap()).await;
87
88        let protocol_capability = CapabilitySource::Framework(FrameworkSource {
89            capability: InternalCapability::Protocol("fuchsia.component.Realm".parse().unwrap()),
90            moniker: component.moniker().clone(),
91        });
92        let valid_path_0 = Moniker::try_from(vec!["foo", "bar"]).unwrap();
93        let valid_path_1 = Moniker::try_from(vec!["foo", "bar", "baz"]).unwrap();
94        let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap();
95        let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap();
96
97        assert_matches!(
98            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0),
99            Ok(())
100        );
101        assert_matches!(
102            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1),
103            Ok(())
104        );
105        assert_matches!(
106            global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0),
107            Err(_)
108        );
109        assert_matches!(
110            global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1),
111            Err(_)
112        );
113        Ok(())
114    }
115
116    // Tests `GlobalPolicyChecker::can_route_capability()` for namespace capability sources.
117    async fn global_policy_checker_can_route_capability_namespace_cap(&self) -> Result<(), Error> {
118        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
119        policy_builder.add_capability_policy(
120            CapabilityAllowlistKey {
121                source_moniker: ExtendedMoniker::ComponentManager,
122                source_name: "fuchsia.kernel.MmioResource".parse().unwrap(),
123                source: CapabilityAllowlistSource::Self_,
124                capability: CapabilityTypeName::Protocol,
125            },
126            vec![
127                AllowlistEntryBuilder::new().exact("root").build(),
128                AllowlistEntryBuilder::new().exact("root").exact("bootstrap").build(),
129                AllowlistEntryBuilder::new().exact("root").exact("core").build(),
130            ],
131        );
132        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
133
134        let protocol_capability = CapabilitySource::Namespace(NamespaceSource {
135            capability: ComponentCapability::Protocol(ProtocolDecl {
136                name: "fuchsia.kernel.MmioResource".parse().unwrap(),
137                source_path: Some("/svc/fuchsia.kernel.MmioResource".parse().unwrap()),
138                delivery: Default::default(),
139            }),
140        });
141        let valid_path_0 = Moniker::try_from(vec!["root"]).unwrap();
142        let valid_path_2 = Moniker::try_from(vec!["root", "core"]).unwrap();
143        let valid_path_1 = Moniker::try_from(vec!["root", "bootstrap"]).unwrap();
144        let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap();
145        let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap();
146
147        assert_matches!(
148            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0),
149            Ok(())
150        );
151        assert_matches!(
152            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1),
153            Ok(())
154        );
155        assert_matches!(
156            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_2),
157            Ok(())
158        );
159        assert_matches!(
160            global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0),
161            Err(_)
162        );
163        assert_matches!(
164            global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1),
165            Err(_)
166        );
167        Ok(())
168    }
169
170    // Tests `GlobalPolicyChecker::can_route_capability()` for component capability sources.
171    async fn global_policy_checker_can_route_capability_component_cap(&self) -> Result<(), Error> {
172        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
173        policy_builder.add_capability_policy(
174            CapabilityAllowlistKey {
175                source_moniker: ExtendedMoniker::ComponentInstance(
176                    Moniker::try_from(vec!["foo"]).unwrap(),
177                ),
178                source_name: "fuchsia.foo.FooBar".parse().unwrap(),
179                source: CapabilityAllowlistSource::Self_,
180                capability: CapabilityTypeName::Protocol,
181            },
182            vec![
183                AllowlistEntryBuilder::new().exact("foo").build(),
184                AllowlistEntryBuilder::new().exact("root").exact("bootstrap").build(),
185                AllowlistEntryBuilder::new().exact("root").exact("core").build(),
186            ],
187        );
188        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
189        let component = self.make_component(vec!["foo:0"].try_into().unwrap()).await;
190
191        let protocol_capability = CapabilitySource::Component(ComponentSource {
192            capability: ComponentCapability::Protocol(ProtocolDecl {
193                name: "fuchsia.foo.FooBar".parse().unwrap(),
194                source_path: Some("/svc/fuchsia.foo.FooBar".parse().unwrap()),
195                delivery: Default::default(),
196            }),
197            moniker: component.moniker().clone(),
198        });
199        let valid_path_0 = Moniker::try_from(vec!["root", "bootstrap"]).unwrap();
200        let valid_path_1 = Moniker::try_from(vec!["root", "core"]).unwrap();
201        let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap();
202        let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap();
203
204        assert_matches!(
205            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0),
206            Ok(())
207        );
208        assert_matches!(
209            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1),
210            Ok(())
211        );
212        assert_matches!(
213            global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0),
214            Err(_)
215        );
216        assert_matches!(
217            global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1),
218            Err(_)
219        );
220        Ok(())
221    }
222
223    // Tests `GlobalPolicyChecker::can_route_capability()` for capability sources of type `Capability`.
224    async fn global_policy_checker_can_route_capability_capability_cap(&self) -> Result<(), Error> {
225        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
226        policy_builder.add_capability_policy(
227            CapabilityAllowlistKey {
228                source_moniker: ExtendedMoniker::ComponentInstance(
229                    Moniker::try_from(vec!["foo"]).unwrap(),
230                ),
231                source_name: "cache".parse().unwrap(),
232                source: CapabilityAllowlistSource::Capability,
233                capability: CapabilityTypeName::Storage,
234            },
235            vec![
236                AllowlistEntryBuilder::new().exact("foo").build(),
237                AllowlistEntryBuilder::new().exact("root").exact("bootstrap").build(),
238                AllowlistEntryBuilder::new().exact("root").exact("core").build(),
239            ],
240        );
241        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
242        let component = self.make_component(vec!["foo:0"].try_into().unwrap()).await;
243
244        let protocol_capability = CapabilitySource::Capability(CapabilityToCapabilitySource {
245            source_capability: ComponentCapability::Storage(StorageDecl {
246                backing_dir: "cache".parse().unwrap(),
247                name: "cache".parse().unwrap(),
248                source: StorageDirectorySource::Parent,
249                subdir: Default::default(),
250                storage_id: fdecl::StorageId::StaticInstanceIdOrMoniker,
251            }),
252            moniker: component.moniker().clone(),
253        });
254        let valid_path_0 = Moniker::try_from(vec!["root", "bootstrap"]).unwrap();
255        let valid_path_1 = Moniker::try_from(vec!["root", "core"]).unwrap();
256        let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap();
257        let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap();
258
259        assert_matches!(
260            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_0),
261            Ok(())
262        );
263        assert_matches!(
264            global_policy_checker.can_route_capability(&protocol_capability, &valid_path_1),
265            Ok(())
266        );
267        assert_matches!(
268            global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_0),
269            Err(_)
270        );
271        assert_matches!(
272            global_policy_checker.can_route_capability(&protocol_capability, &invalid_path_1),
273            Err(_)
274        );
275        Ok(())
276    }
277
278    // Tests `GlobalPolicyChecker::can_route_debug_capability()` for capability sources of type `Capability`.
279    async fn global_policy_checker_can_route_debug_capability_capability_cap(
280        &self,
281    ) -> Result<(), Error> {
282        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
283        policy_builder.add_debug_capability_policy(
284            DebugCapabilityKey {
285                name: "debug_service1".parse().unwrap(),
286                source: CapabilityAllowlistSource::Self_,
287                capability: CapabilityTypeName::Protocol,
288                env_name: "foo_env".parse().unwrap(),
289            },
290            AllowlistEntryBuilder::new().exact("foo").build(),
291        );
292        policy_builder.add_debug_capability_policy(
293            DebugCapabilityKey {
294                name: "debug_service1".parse().unwrap(),
295                source: CapabilityAllowlistSource::Self_,
296                capability: CapabilityTypeName::Protocol,
297                env_name: "bootstrap_env".parse().unwrap(),
298            },
299            AllowlistEntryBuilder::new().exact("root").exact("bootstrap").build(),
300        );
301        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
302        let protocol_name: Name = "debug_service1".parse().unwrap();
303
304        let valid_cases = vec![
305            (Moniker::try_from(vec!["root", "bootstrap"]).unwrap(), "bootstrap_env"),
306            (Moniker::try_from(vec!["foo"]).unwrap(), "foo_env"),
307        ];
308
309        let invalid_cases = vec![
310            (Moniker::try_from(vec!["foobar"]).unwrap(), "foobar_env"),
311            (Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap(), "foobar_env"),
312            (Moniker::try_from(vec!["root", "bootstrap"]).unwrap(), "foo_env"),
313            (Moniker::try_from(vec!["root", "baz"]).unwrap(), "foo_env"),
314        ];
315
316        for valid_case in valid_cases {
317            assert_matches!(
318                global_policy_checker.can_register_debug_capability(
319                    CapabilityTypeName::Protocol,
320                    &protocol_name,
321                    &valid_case.0,
322                    &valid_case.1.parse().unwrap(),
323                ),
324                Ok(()),
325                "{:?}",
326                valid_case
327            );
328        }
329
330        for invalid_case in invalid_cases {
331            assert_matches!(
332                global_policy_checker.can_register_debug_capability(
333                    CapabilityTypeName::Protocol,
334                    &protocol_name,
335                    &invalid_case.0,
336                    &invalid_case.1.parse().unwrap(),
337                ),
338                Err(_),
339                "{:?}",
340                invalid_case
341            );
342        }
343
344        Ok(())
345    }
346
347    // Tests `GlobalPolicyChecker::can_route_debug_capability()` for capability sources of type
348    // `Capability` with realm allowlist entries.
349    async fn global_policy_checker_can_route_debug_capability_with_realm_allowlist_entry(
350        &self,
351    ) -> Result<(), Error> {
352        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
353        policy_builder.add_debug_capability_policy(
354            DebugCapabilityKey {
355                name: "debug_service1".parse().unwrap(),
356                source: CapabilityAllowlistSource::Self_,
357                capability: CapabilityTypeName::Protocol,
358                env_name: "bar_env".parse().unwrap(),
359            },
360            AllowlistEntryBuilder::new().exact("root").exact("bootstrap1").any_descendant(),
361        );
362        policy_builder.add_debug_capability_policy(
363            DebugCapabilityKey {
364                name: "debug_service1".parse().unwrap(),
365                source: CapabilityAllowlistSource::Self_,
366                capability: CapabilityTypeName::Protocol,
367                env_name: "foo_env".parse().unwrap(),
368            },
369            AllowlistEntryBuilder::new().exact("root").exact("bootstrap2").build(),
370        );
371        policy_builder.add_debug_capability_policy(
372            DebugCapabilityKey {
373                name: "debug_service1".parse().unwrap(),
374                source: CapabilityAllowlistSource::Self_,
375                capability: CapabilityTypeName::Protocol,
376                env_name: "baz_env".parse().unwrap(),
377            },
378            AllowlistEntryBuilder::new().exact("root").exact("bootstrap3").any_descendant(),
379        );
380        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
381
382        // dest, env
383        let valid_cases = vec![
384            (vec!["root", "bootstrap1", "child"], "bar_env".to_string()),
385            (vec!["root", "bootstrap1", "child", "grandchild"], "bar_env".to_string()),
386            (vec!["root", "bootstrap2"], "foo_env".to_string()),
387            (vec!["root", "bootstrap3", "child"], "baz_env".to_string()),
388            (vec!["root", "bootstrap3", "child", "grandchild"], "baz_env".to_string()),
389        ];
390
391        let invalid_cases = vec![
392            (vec!["root", "not_bootstrap"], "bar_env".to_string()),
393            (vec!["root", "not_bootstrap"], "foo_env".to_string()),
394            (vec!["root", "bootstrap1"], "baz_env".to_string()),
395        ];
396
397        for (dest, env) in valid_cases {
398            let protocol_name: Name = "debug_service1".parse().unwrap();
399            let env: Name = env.parse().unwrap();
400            assert_matches!(
401                global_policy_checker.can_register_debug_capability(
402                    CapabilityTypeName::Protocol,
403                    &protocol_name,
404                    &Moniker::try_from(dest.clone()).unwrap(),
405                    &env,
406                ),
407                Ok(()),
408                "{:?}",
409                (dest, env)
410            );
411        }
412
413        for (dest, env) in invalid_cases {
414            let protocol_name: Name = "debug_service1".parse().unwrap();
415            let env: Name = env.parse().unwrap();
416            assert_matches!(
417                global_policy_checker.can_register_debug_capability(
418                    CapabilityTypeName::Protocol,
419                    &protocol_name,
420                    &Moniker::try_from(dest.clone()).unwrap(),
421                    &env,
422                ),
423                Err(_),
424                "{:?}",
425                (dest, env)
426            );
427        }
428
429        Ok(())
430    }
431
432    // Tests `GlobalPolicyChecker::can_route_debug_capability()` for capability sources of type
433    // `Capability` with collection allowlist entries.
434    async fn global_policy_checker_can_route_debug_capability_with_collection_allowlist_entry(
435        &self,
436    ) -> Result<(), Error> {
437        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
438        policy_builder.add_debug_capability_policy(
439            DebugCapabilityKey {
440                name: "debug_service1".parse().unwrap(),
441                source: CapabilityAllowlistSource::Self_,
442                capability: CapabilityTypeName::Protocol,
443                env_name: "bar_env".parse().unwrap(),
444            },
445            AllowlistEntryBuilder::new()
446                .exact("root")
447                .exact("bootstrap")
448                .any_descendant_in_collection("coll1"),
449        );
450        policy_builder.add_debug_capability_policy(
451            DebugCapabilityKey {
452                name: "debug_service1".parse().unwrap(),
453                source: CapabilityAllowlistSource::Self_,
454                capability: CapabilityTypeName::Protocol,
455                env_name: "foo_env".parse().unwrap(),
456            },
457            AllowlistEntryBuilder::new().exact("root").exact("bootstrap2").build(),
458        );
459        policy_builder.add_debug_capability_policy(
460            DebugCapabilityKey {
461                name: "debug_service1".parse().unwrap(),
462                source: CapabilityAllowlistSource::Self_,
463                capability: CapabilityTypeName::Protocol,
464                env_name: "baz_env".parse().unwrap(),
465            },
466            AllowlistEntryBuilder::new()
467                .exact("root")
468                .exact("bootstrap3")
469                .any_descendant_in_collection("coll4"),
470        );
471        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
472
473        // dest, env
474        let valid_cases = vec![
475            (vec!["root", "bootstrap", "coll1:instance1"], "bar_env".to_string()),
476            (vec!["root", "bootstrap", "coll1:instance1", "child"], "bar_env".to_string()),
477            (vec!["root", "bootstrap2"], "foo_env".to_string()),
478            (vec!["root", "bootstrap3", "coll4:instance4"], "baz_env".to_string()),
479            (vec!["root", "bootstrap3", "coll4:instance4", "child"], "baz_env".to_string()),
480        ];
481
482        let invalid_cases = vec![
483            (vec!["root", "bootstrap"], "bar_env".to_string()),
484            (vec!["root", "not_bootstrap"], "bar_env".to_string()),
485            (vec!["root", "not_bootstrap"], "foo_env".to_string()),
486            (vec!["root", "bootstrap3", "child"], "baz_env".to_string()),
487            (vec!["root", "bootstrap"], "baz_env".to_string()),
488        ];
489
490        for (dest, env) in valid_cases {
491            let protocol_name: Name = "debug_service1".parse().unwrap();
492            let env: Name = env.parse().unwrap();
493            assert_matches!(
494                global_policy_checker.can_register_debug_capability(
495                    CapabilityTypeName::Protocol,
496                    &protocol_name,
497                    &Moniker::try_from(dest.clone()).unwrap(),
498                    &env,
499                ),
500                Ok(()),
501                "{:?}",
502                (dest, env)
503            );
504        }
505
506        for (dest, env) in invalid_cases {
507            let protocol_name: Name = "debug_service1".parse().unwrap();
508            let env: Name = env.parse().unwrap();
509            assert_matches!(
510                global_policy_checker.can_register_debug_capability(
511                    CapabilityTypeName::Protocol,
512                    &protocol_name,
513                    &Moniker::try_from(dest.clone()).unwrap(),
514                    &env,
515                ),
516                Err(_),
517                "{:?}",
518                (dest, env)
519            );
520        }
521
522        Ok(())
523    }
524
525    // Tests `GlobalPolicyChecker::can_route_capability()` for builtin capabilities.
526    async fn global_policy_checker_can_route_capability_builtin_cap(&self) -> Result<(), Error> {
527        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
528        policy_builder.add_capability_policy(
529            CapabilityAllowlistKey {
530                source_moniker: ExtendedMoniker::ComponentManager,
531                source_name: "test".parse().unwrap(),
532                source: CapabilityAllowlistSource::Self_,
533                capability: CapabilityTypeName::Directory,
534            },
535            vec![
536                AllowlistEntryBuilder::new().exact("root").build(),
537                AllowlistEntryBuilder::new().exact("root").exact("core").build(),
538            ],
539        );
540        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
541
542        let dir_capability = CapabilitySource::Builtin(BuiltinSource {
543            capability: InternalCapability::Directory("test".parse().unwrap()),
544        });
545        let valid_path_0 = Moniker::try_from(vec!["root"]).unwrap();
546        let valid_path_1 = Moniker::try_from(vec!["root", "core"]).unwrap();
547        let invalid_path_0 = Moniker::try_from(vec!["foobar"]).unwrap();
548        let invalid_path_1 = Moniker::try_from(vec!["foo", "bar", "foobar"]).unwrap();
549
550        assert_matches!(
551            global_policy_checker.can_route_capability(&dir_capability, &valid_path_0),
552            Ok(())
553        );
554        assert_matches!(
555            global_policy_checker.can_route_capability(&dir_capability, &valid_path_1),
556            Ok(())
557        );
558        assert_matches!(
559            global_policy_checker.can_route_capability(&dir_capability, &invalid_path_0),
560            Err(_)
561        );
562        assert_matches!(
563            global_policy_checker.can_route_capability(&dir_capability, &invalid_path_1),
564            Err(_)
565        );
566        Ok(())
567    }
568
569    // Tests `GlobalPolicyChecker::can_route_capability()` for policy that includes non-exact
570    // `AllowlistEntry::Realm` entries.
571    async fn global_policy_checker_can_route_capability_with_realm_allowlist_entry(
572        &self,
573    ) -> Result<(), Error> {
574        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
575        policy_builder.add_capability_policy(
576            CapabilityAllowlistKey {
577                source_moniker: ExtendedMoniker::ComponentManager,
578                source_name: "fuchsia.kernel.MmioResource".parse().unwrap(),
579                source: CapabilityAllowlistSource::Self_,
580                capability: CapabilityTypeName::Protocol,
581            },
582            vec![
583                AllowlistEntryBuilder::new().exact("tests").any_descendant(),
584                AllowlistEntryBuilder::new().exact("core").exact("tests").any_descendant(),
585            ],
586        );
587        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
588        let protocol_capability = CapabilitySource::Namespace(NamespaceSource {
589            capability: ComponentCapability::Protocol(ProtocolDecl {
590                name: "fuchsia.kernel.MmioResource".parse().unwrap(),
591                source_path: Some("/svc/fuchsia.kernel.MmioResource".parse().unwrap()),
592                delivery: Default::default(),
593            }),
594        });
595
596        macro_rules! can_route {
597            ($moniker:expr) => {
598                global_policy_checker.can_route_capability(&protocol_capability, $moniker)
599            };
600        }
601
602        assert!(can_route!(&Moniker::try_from(vec!["tests", "test1"]).unwrap()).is_ok());
603        assert!(can_route!(&Moniker::try_from(vec!["tests", "coll:test1"]).unwrap()).is_ok());
604        assert!(can_route!(&Moniker::try_from(vec!["tests", "test1", "util"]).unwrap()).is_ok());
605        assert!(can_route!(&Moniker::try_from(vec!["tests", "test2"]).unwrap()).is_ok());
606        assert!(can_route!(&Moniker::try_from(vec!["core", "tests", "test"]).unwrap()).is_ok());
607        assert!(can_route!(&Moniker::try_from(vec!["core", "tests", "coll:t"]).unwrap()).is_ok());
608
609        assert!(can_route!(&Moniker::try_from(vec!["foo"]).unwrap()).is_err());
610        assert!(can_route!(&Moniker::try_from(vec!["tests"]).unwrap()).is_err());
611        assert!(can_route!(&Moniker::try_from(vec!["core", "foo"]).unwrap()).is_err());
612        assert!(can_route!(&Moniker::try_from(vec!["core", "tests"]).unwrap()).is_err());
613        assert!(can_route!(&Moniker::try_from(vec!["core", "tests:test"]).unwrap()).is_err());
614        Ok(())
615    }
616
617    // Tests `GlobalPolicyChecker::can_route_capability()` for policy that includes non-exact
618    // `AllowlistEntry::Collection` entries.
619    async fn global_policy_checker_can_route_capability_with_collection_allowlist_entry(
620        &self,
621    ) -> Result<(), Error> {
622        let mut policy_builder = CapabilityAllowlistPolicyBuilder::new();
623        policy_builder.add_capability_policy(
624            CapabilityAllowlistKey {
625                source_moniker: ExtendedMoniker::ComponentManager,
626                source_name: "fuchsia.kernel.MmioResource".parse().unwrap(),
627                source: CapabilityAllowlistSource::Self_,
628                capability: CapabilityTypeName::Protocol,
629            },
630            vec![
631                AllowlistEntryBuilder::new().any_descendant_in_collection("tests"),
632                AllowlistEntryBuilder::new().exact("core").any_descendant_in_collection("tests"),
633            ],
634        );
635        let global_policy_checker = GlobalPolicyChecker::new(Arc::new(policy_builder.build()));
636        let protocol_capability = CapabilitySource::Namespace(NamespaceSource {
637            capability: ComponentCapability::Protocol(ProtocolDecl {
638                name: "fuchsia.kernel.MmioResource".parse().unwrap(),
639                source_path: Some("/svc/fuchsia.kernel.MmioResource".parse().unwrap()),
640                delivery: Default::default(),
641            }),
642        });
643
644        macro_rules! can_route {
645            ($moniker:expr) => {
646                global_policy_checker.can_route_capability(&protocol_capability, $moniker)
647            };
648        }
649
650        assert!(can_route!(&Moniker::try_from(vec!["tests:t1"]).unwrap()).is_ok());
651        assert!(can_route!(&Moniker::try_from(vec!["tests:t2"]).unwrap()).is_ok());
652        assert!(can_route!(&Moniker::try_from(vec!["tests:t1", "util"]).unwrap()).is_ok());
653        assert!(can_route!(&Moniker::try_from(vec!["core", "tests:t1"]).unwrap()).is_ok());
654        assert!(can_route!(&Moniker::try_from(vec!["core", "tests:t2"]).unwrap()).is_ok());
655
656        assert!(can_route!(&Moniker::try_from(vec!["foo"]).unwrap()).is_err());
657        assert!(can_route!(&Moniker::try_from(vec!["tests"]).unwrap()).is_err());
658        assert!(can_route!(&Moniker::try_from(vec!["coll:foo"]).unwrap()).is_err());
659        assert!(can_route!(&Moniker::try_from(vec!["core", "foo"]).unwrap()).is_err());
660        assert!(can_route!(&Moniker::try_from(vec!["core", "coll:tests"]).unwrap()).is_err());
661        Ok(())
662    }
663}
664
665// Creates a SecurityPolicy based on the capability allowlist entries provided during
666// construction.
667struct CapabilityAllowlistPolicyBuilder {
668    capability_policy: HashMap<CapabilityAllowlistKey, HashSet<AllowlistEntry>>,
669    debug_capability_policy: HashMap<DebugCapabilityKey, HashSet<DebugCapabilityAllowlistEntry>>,
670}
671
672impl CapabilityAllowlistPolicyBuilder {
673    pub fn new() -> Self {
674        Self { capability_policy: HashMap::new(), debug_capability_policy: HashMap::new() }
675    }
676
677    /// Add a new entry to the configuration.
678    pub fn add_capability_policy<'a>(
679        &'a mut self,
680        key: CapabilityAllowlistKey,
681        value: Vec<AllowlistEntry>,
682    ) -> &'a mut Self {
683        let value_set = HashSet::from_iter(value.iter().cloned());
684        self.capability_policy.insert(key, value_set);
685        self
686    }
687
688    /// Add a new entry to the configuration.
689    pub fn add_debug_capability_policy<'a>(
690        &'a mut self,
691        key: DebugCapabilityKey,
692        dest: AllowlistEntry,
693    ) -> &'a mut Self {
694        self.debug_capability_policy
695            .entry(key)
696            .or_default()
697            .insert(DebugCapabilityAllowlistEntry::new(dest));
698        self
699    }
700
701    /// Creates a configuration from the provided policies.
702    pub fn build(&self) -> SecurityPolicy {
703        SecurityPolicy {
704            job_policy: JobPolicyAllowlists {
705                ambient_mark_vmo_exec: vec![],
706                main_process_critical: vec![],
707                create_raw_processes: vec![],
708            },
709            capability_policy: self.capability_policy.clone(),
710            debug_capability_policy: self.debug_capability_policy.clone(),
711            child_policy: ChildPolicyAllowlists { reboot_on_terminate: vec![] },
712            ..Default::default()
713        }
714    }
715}