Skip to main content

cageforge_policy_compose/
context.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Narrowed runtime path context produced by [`crate::EffectiveSandbox`].
4//!
5//! The private constructor is intentional: callers should obtain this context
6//! from the effective result rather than rebuilding a broader context by hand.
7
8use std::path::{Path, PathBuf};
9use std::sync::Arc;
10
11use cageforge_policy::{PathPattern, PathResolutionContext, PathSelector};
12
13/// Identity shared by one effective sandbox and the contexts it creates.
14///
15/// The value is intentionally opaque. Pointer identity, rather than the
16/// value stored in the token, binds a context to its originating composition.
17#[derive(Debug, Clone)]
18pub(crate) struct ContextIdentity(Arc<()>);
19
20/// A runtime path context created by [`crate::EffectiveSandbox::path_context`].
21///
22/// The constructor is intentionally private so a filesystem decision cannot
23/// accidentally use a context with workspace roots broader than the composed
24/// result.
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct EffectivePathContext {
27    context: PathResolutionContext,
28    identity: ContextIdentity,
29}
30
31impl ContextIdentity {
32    pub(crate) fn new() -> Self {
33        Self(Arc::new(()))
34    }
35
36    fn matches(&self, other: &Self) -> bool {
37        Arc::ptr_eq(&self.0, &other.0)
38    }
39}
40
41impl PartialEq for ContextIdentity {
42    fn eq(&self, other: &Self) -> bool {
43        self.matches(other)
44    }
45}
46
47impl Eq for ContextIdentity {}
48
49impl EffectivePathContext {
50    pub(crate) fn new(context: PathResolutionContext, identity: ContextIdentity) -> Self {
51        Self { context, identity }
52    }
53
54    /// Returns the workspace roots permitted by the composed result.
55    pub fn workspace_roots(&self) -> &[PathBuf] {
56        self.context.workspace_roots()
57    }
58
59    /// Returns the system roots retained by the composed runtime context.
60    pub fn root_paths(&self) -> &[PathBuf] {
61        self.context.root_paths()
62    }
63
64    /// Returns the platform-minimal paths retained by the composed context.
65    pub fn minimal_paths(&self) -> &[PathBuf] {
66        self.context.minimal_paths()
67    }
68
69    /// Returns the platform temporary directory, if one was supplied.
70    pub fn tmpdir(&self) -> Option<&std::path::Path> {
71        self.context.tmpdir()
72    }
73
74    /// Returns the conventional `/tmp` directory, if one was supplied.
75    pub fn slash_tmp(&self) -> Option<&std::path::Path> {
76        self.context.slash_tmp()
77    }
78
79    /// Returns the absolute runtime directory used to resolve or inherit a
80    /// command working directory.
81    pub fn current_directory(&self) -> Option<&Path> {
82        self.context.current_directory()
83    }
84
85    /// Resolves a symbolic selector through this bound effective context.
86    pub fn resolve(&self, selector: &PathSelector) -> Vec<PathBuf> {
87        selector.resolve(&self.context)
88    }
89
90    /// Returns the concrete scan roots for one validated filesystem pattern.
91    ///
92    /// Workspace patterns are resolved only through the roots retained by this
93    /// effective context. Absolute patterns produce their own static prefix.
94    /// The roots are lexical scan anchors, not filesystem authorization.
95    pub fn glob_search_roots(&self, pattern: &PathPattern) -> Vec<PathBuf> {
96        let prefix = pattern.literal_prefix();
97        if pattern.is_absolute() {
98            vec![prefix]
99        } else {
100            self.context
101                .workspace_roots()
102                .iter()
103                .map(|root| root.join(&prefix))
104                .collect()
105        }
106    }
107
108    /// Tests a path pattern through this composition-bound runtime context.
109    ///
110    /// This method preserves workspace-root narrowing. It is a matcher used by
111    /// native lowering and is not an authorization result; the final path must
112    /// still be checked against the complete effective filesystem policy.
113    pub fn pattern_matches(&self, pattern: &PathPattern, path: &Path) -> bool {
114        pattern.matches_path(path, &self.context)
115    }
116
117    pub(crate) fn belongs_to(&self, identity: &ContextIdentity) -> bool {
118        self.identity.matches(identity)
119    }
120
121    pub(crate) fn raw(&self) -> &PathResolutionContext {
122        &self.context
123    }
124}