安全策略引擎:Starlark DSL 的设计与实现
安全策略引擎:Starlark DSL 的设计与实现
学习目标
- 理解 Codex 选择 Starlark 作为策略 DSL 的理由
- 掌握 Codex 的
PrefixRule+NetworkRule策略模型 - 分析 Codex 的策略加载、执行和运行时追加机制
项目实践
策略引擎架构
Codex 的策略引擎位于 codex-rs/execpolicy/src/:
execpolicy/- src/lib.rs # 核心类型和 trait- src/policy.rs # Policy 结构体- src/parser.rs # Starlark 策略解析- src/evaluate.rs # 策略执行- src/amend.rs # 运行时规则追加- src/execpolicycheck.rs # CLI 策略测试工具策略语法
Codex 使用 Starlark(Python 子集)作为策略 DSL:
# 前缀规则:匹配命令开头prefix_rule( pattern=["git", "status"], decision="allow", justification="查看 git 状态是安全的")
prefix_rule( pattern=["rm", "-rf", "/"], decision="forbidden", justification="删除根目录是危险的")
# 网络规则:匹配目标主机和协议network_rule( host="api.github.com", protocol="https", decision="allow", justification="访问 GitHub API")
network_rule( host="*", protocol="http", decision="prompt", justification="HTTP 请求需要确认")核心数据结构
pub struct Policy { pub rules_by_program: HashMap<String, Vec<PrefixRule>>, pub network_rules: Vec<NetworkRule>, pub host_executables_by_name: HashMap<String, PathBuf>,}
pub struct PrefixRule { pub pattern: PrefixPattern, pub decision: Decision, pub justification: Option<String>,}
pub struct NetworkRule { pub host: String, pub protocol: String, pub decision: Decision, pub justification: Option<String>,}
pub enum Decision { Allow, Prompt, Forbidden,}策略执行
Policy::check() 对命令进行前缀匹配:
impl Policy { pub fn check(&self, command: &[String] ) -> Evaluation { let program = command.first()?; let rules = self.rules_by_program.get(program)?;
for rule in rules { if rule.pattern.matches(command) { return Evaluation { decision: rule.decision, matched_rule: Some(rule), }; } }
// 无匹配规则时,默认决策 Evaluation::default() }}匹配逻辑:按首命令分组,在组内按规则定义顺序匹配,第一个匹配的规则生效。
运行时规则追加
amend.rs 支持用户批准后动态添加规则:
pub fn amend_policy( policy_path: &Path, new_rule: PrefixRule,) -> Result<> { // 1. 获取文件锁 let lock = advisory_lock(policy_path)?;
// 2. 读取现有策略 let mut policy = read_policy(policy_path)?;
// 3. 追加新规则 policy.rules_by_program .entry(program) .or_default() .push(new_rule);
// 4. 原子写入 write_atomic(policy_path, policy)?;
// 5. 释放锁 drop(lock); Ok(())}关键设计:
- 使用 advisory file lock 保护并发修改
- 原子写入:先写临时文件,再重命名替换
- 追加而非替换:保留用户已有的所有规则
策略测试工具
execpolicycheck.rs 提供 CLI 工具用于测试策略:
codex execpolicy check -r my.rules -- git status# 输出: Allow (matched: prefix_rule(["git", "status"], ...))用途:在部署策略前验证其行为,避免误杀合法命令。
问题与规避
Starlark 沙箱逃逸
Starlark 是确定性的、无 I/O 的语言,天然沙箱安全。Codex 没有额外的沙箱措施,依赖 Starlark 的语言特性保证安全。
前缀匹配的歧义性
prefix_rule(pattern=["git"], decision="allow")prefix_rule(pattern=["git", "status"], decision="prompt")对于 git status,第一条规则先匹配,导致第二条规则被忽略。Codex 的对策是:在组内按规则定义顺序匹配,鼓励用户将更具体的规则放在前面。
设计取舍
Starlark vs JSON/YAML
Codex 选择 Starlark 而非 JSON/YAML 作为策略语言:
| 维度 | Starlark | JSON/YAML |
|---|---|---|
| 表达能力 | 高(变量、函数、条件) | 低(静态数据) |
| 安全性 | 高(无 I/O、确定性) | 中(只是数据) |
| 可读性 | 中(Python 子集) | 高(广泛熟悉) |
| 解析依赖 | 需 starlark crate | 标准库支持 |
Codex 的权衡:Starlark 的表达能力对于复杂的策略场景(如条件规则、变量复用)更有价值,代价是增加了依赖和学习成本。
前缀匹配 vs 正则匹配
Codex 使用前缀匹配而非正则匹配。优势:
- 性能:前缀匹配是 O(n),正则可能回溯
- 可预测性:前缀匹配的行为更直观
- 安全性:正则的复杂模式可能引入 ReDoS
代价是表达能力受限,无法表达复杂的匹配逻辑。