Skip to main content

aster/sandbox/
filesystem.rs

1//! 文件系统沙箱
2//!
3//! 提供文件系统访问控制和路径规则管理
4
5use serde::{Deserialize, Serialize};
6use std::path::{Path, PathBuf};
7
8/// 路径访问权限
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub enum PathPermission {
11    /// 只读
12    ReadOnly,
13    /// 读写
14    ReadWrite,
15    /// 禁止访问
16    Denied,
17}
18
19/// 路径规则
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct PathRule {
22    /// 路径模式
23    pub pattern: String,
24    /// 权限
25    pub permission: PathPermission,
26    /// 是否递归应用到子目录
27    pub recursive: bool,
28}
29
30impl PathRule {
31    /// 创建只读规则
32    pub fn read_only(pattern: impl Into<String>) -> Self {
33        Self {
34            pattern: pattern.into(),
35            permission: PathPermission::ReadOnly,
36            recursive: true,
37        }
38    }
39
40    /// 创建读写规则
41    pub fn read_write(pattern: impl Into<String>) -> Self {
42        Self {
43            pattern: pattern.into(),
44            permission: PathPermission::ReadWrite,
45            recursive: true,
46        }
47    }
48
49    /// 创建禁止访问规则
50    pub fn denied(pattern: impl Into<String>) -> Self {
51        Self {
52            pattern: pattern.into(),
53            permission: PathPermission::Denied,
54            recursive: true,
55        }
56    }
57
58    /// 检查路径是否匹配规则
59    pub fn matches(&self, path: &Path) -> bool {
60        let pattern_path = Path::new(&self.pattern);
61
62        if self.recursive {
63            path.starts_with(pattern_path)
64        } else {
65            path == pattern_path
66        }
67    }
68}
69
70/// 文件系统策略
71#[derive(Debug, Clone, Default, Serialize, Deserialize)]
72pub struct FilesystemPolicy {
73    /// 路径规则列表(按优先级排序,后面的规则优先)
74    pub rules: Vec<PathRule>,
75    /// 默认权限
76    pub default_permission: Option<PathPermission>,
77}
78
79impl FilesystemPolicy {
80    /// 创建新的策略
81    pub fn new() -> Self {
82        Self::default()
83    }
84
85    /// 添加规则
86    pub fn add_rule(&mut self, rule: PathRule) {
87        self.rules.push(rule);
88    }
89
90    /// 获取路径权限
91    pub fn get_permission(&self, path: &Path) -> PathPermission {
92        // 从后向前遍历,后面的规则优先级更高
93        for rule in self.rules.iter().rev() {
94            if rule.matches(path) {
95                return rule.permission;
96            }
97        }
98
99        self.default_permission.unwrap_or(PathPermission::Denied)
100    }
101
102    /// 检查路径是否可读
103    pub fn can_read(&self, path: &Path) -> bool {
104        matches!(
105            self.get_permission(path),
106            PathPermission::ReadOnly | PathPermission::ReadWrite
107        )
108    }
109
110    /// 检查路径是否可写
111    pub fn can_write(&self, path: &Path) -> bool {
112        self.get_permission(path) == PathPermission::ReadWrite
113    }
114}
115
116/// 文件系统沙箱
117pub struct FilesystemSandbox {
118    /// 策略
119    policy: FilesystemPolicy,
120    /// 根目录
121    root: PathBuf,
122    /// 是否启用
123    enabled: bool,
124}
125
126impl FilesystemSandbox {
127    /// 创建新的文件系统沙箱
128    pub fn new(root: PathBuf) -> Self {
129        Self {
130            policy: FilesystemPolicy::default(),
131            root,
132            enabled: true,
133        }
134    }
135
136    /// 使用策略创建
137    pub fn with_policy(root: PathBuf, policy: FilesystemPolicy) -> Self {
138        Self {
139            policy,
140            root,
141            enabled: true,
142        }
143    }
144
145    /// 启用/禁用沙箱
146    pub fn set_enabled(&mut self, enabled: bool) {
147        self.enabled = enabled;
148    }
149
150    /// 检查路径是否在沙箱内
151    pub fn is_within_sandbox(&self, path: &Path) -> bool {
152        path.starts_with(&self.root)
153    }
154
155    /// 规范化路径(解析相对路径和符号链接)
156    pub fn normalize_path(&self, path: &Path) -> anyhow::Result<PathBuf> {
157        let normalized = if path.is_absolute() {
158            path.to_path_buf()
159        } else {
160            self.root.join(path)
161        };
162
163        // 检查是否在沙箱内
164        if !self.is_within_sandbox(&normalized) {
165            anyhow::bail!("路径 {} 不在沙箱范围内", path.display());
166        }
167
168        Ok(normalized)
169    }
170
171    /// 检查读取权限
172    pub fn check_read(&self, path: &Path) -> anyhow::Result<()> {
173        if !self.enabled {
174            return Ok(());
175        }
176
177        let normalized = self.normalize_path(path)?;
178
179        if !self.policy.can_read(&normalized) {
180            anyhow::bail!("没有读取权限: {}", path.display());
181        }
182
183        Ok(())
184    }
185
186    /// 检查写入权限
187    pub fn check_write(&self, path: &Path) -> anyhow::Result<()> {
188        if !self.enabled {
189            return Ok(());
190        }
191
192        let normalized = self.normalize_path(path)?;
193
194        if !self.policy.can_write(&normalized) {
195            anyhow::bail!("没有写入权限: {}", path.display());
196        }
197
198        Ok(())
199    }
200
201    /// 获取策略
202    pub fn policy(&self) -> &FilesystemPolicy {
203        &self.policy
204    }
205
206    /// 获取可变策略
207    pub fn policy_mut(&mut self) -> &mut FilesystemPolicy {
208        &mut self.policy
209    }
210
211    /// 获取根目录
212    pub fn root(&self) -> &Path {
213        &self.root
214    }
215}