跳转到内容

安全策略引擎: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 工具用于测试策略:

Terminal window
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 作为策略语言:

维度StarlarkJSON/YAML
表达能力高(变量、函数、条件)低(静态数据)
安全性高(无 I/O、确定性)中(只是数据)
可读性中(Python 子集)高(广泛熟悉)
解析依赖需 starlark crate标准库支持

Codex 的权衡:Starlark 的表达能力对于复杂的策略场景(如条件规则、变量复用)更有价值,代价是增加了依赖和学习成本。

前缀匹配 vs 正则匹配

Codex 使用前缀匹配而非正则匹配。优势:

  • 性能:前缀匹配是 O(n),正则可能回溯
  • 可预测性:前缀匹配的行为更直观
  • 安全性:正则的复杂模式可能引入 ReDoS

代价是表达能力受限,无法表达复杂的匹配逻辑。


参考来源