M007:建立安全边界:trust、paranoid 与 safe mode
目标:把配置执行、工件完整性与 secret 暴露分开建模,并为可信开发与不可信自动化选择不同控制。
0–8 分钟:先区分五种控制解决的风险
安全讨论容易把“更安全”压成一个开关。实际至少有五个问题:
| 控制 | 核心问题 | 不能替代什么 |
|---|---|---|
| trust | 是否允许某份项目配置影响环境或执行项目行为 | 不是代码沙箱 |
| paranoid mode | 是否要求非 global 配置经过更严格、与内容绑定的显式信任 | 不校验下载工件 |
safe mode (MISE_SAFE=1) | 面对不受控制的项目配置时,拒绝模板执行、source、hooks、tasks 等代码执行面 | 不允许你“安全运行”不可信 task |
| lock/checksum/provenance | 解析与下载工件是否匹配审阅结果 | 不管理项目 secret |
| secret store + masking | 谁能取得凭据、日志如何遮盖 | 不判断配置是否可信 |
trust 与 safe mode 的承诺不同:trust 是“我审阅并允许这份配置”;safe mode 是“我只做不需要项目代码执行的受限操作”。官方安全文档把 safe mode 定义为拒绝边界,而不是把不可信代码放进沙箱执行。[S07][S08]
8–19 分钟:在不执行副作用的前提下审阅配置
这次不创建会真正泄露数据的脚本,也不对配置执行普通 task。只建立一个“看起来需要警惕”的实验仓库,然后验证安全模式拒绝执行。
步骤 1:创建审阅样本
rm -rf /tmp/mise-security-lab
mkdir -p /tmp/mise-security-lab
cd /tmp/mise-security-lab
cat > mise.toml <<'EOF'
[tools]
node = "22"
[env]
_.source = "./would-run.sh"
[tasks.demo]
run = 'printf "task executed\n"'
EOF
cat > would-run.sh <<'EOF'
# Review sample only. Do not execute in this lesson.
export SECURITY_LAB_MARKER="sourced"
EOF
步骤 2:静态审阅执行面
先只读文件:
sed -n '1,120p' mise.toml
sed -n '1,120p' would-run.sh
标出三类内容:纯版本声明、会改变环境的 _.source、会执行命令的 task。安全审阅不是寻找“恶意”关键字,而是识别哪些内容拥有执行能力。
步骤 3:查看 trust 状态
mise trust --show
只记录状态,不执行 mise trust。若当前版本或全局策略显示已经信任,说明实验路径可能受 trusted_config_paths 或既有状态影响;换一个新临时目录,并把全局策略列为环境前提。[S07]
步骤 4:用 safe mode 做允许的解析操作
MISE_SAFE=1 mise lock --bump --dry-run --json
预期观察:safe mode 可以对受支持的 HTTP backend 做版本解析,而不执行项目 source、hooks 或 tasks。[S08] 输出是否有可更新版本取决于当前日期和缓存;关键证据是没有生成 SECURITY_LAB_MARKER,也没有打印 task executed。
步骤 5:证明 task 被拒绝,而不是被“安全运行”
MISE_SAFE=1 mise run demo
status=$?
printf 'safe_task_exit=%s\n' "$status"
预期观察:命令非零失败,并明确说明 safe mode 不运行 task。若看到 task executed,立即停止,保存 mise 版本与完整命令,检查是否真的设置了环境变量,并按当前官方文档重新验证。
19–26 分钟:为两条 CI 路线做决策
路线 A:受审阅代码的正常 test job
目标是安装工具并运行仓库测试。决策过程:
- 输入 commit 是否经过正常代码审阅?
- job 是否必须运行仓库 task?是。
- safe mode 会拒绝 task,因此不能把它当正常 test runner。
- 在隔离 CI runner 中运行 locked install 和
mise run ci,只注入该 job 必需的最小 secret。
路线 B:不可信 PR 上的 lock-bump/解析 bot
目标只是查看版本解析变化。决策过程:
- PR 配置不受 bot 所有者控制。
- job 不需要运行业务 task、hook 或 source。
- 使用
MISE_SAFE=1 mise lock --bump --dry-run --json,让项目配置无法执行代码。 - job 不获得业务 secret,只产出可审阅 diff 或机器可读结果。[S08]
不要把两条路线合并成“所有 CI 都 safe mode”。一个需要运行测试,一个明确不应运行项目代码。
26–29 分钟:写一个六格威胁模型
按以下顺序填写,每格至少一句:
- 资产:开发者凭据、CI token、源码、runner、构建结果。
- 攻击者/输入:陌生仓库、fork PR、被接管的发布源、误提交 secret。
- 配置执行面:templates、
_.source、hooks、tasks、插件安装脚本。 - 供应链面:版本解析、下载 URL、checksum、provenance、backend 限制。
- 控制:审阅 + trust、paranoid、safe mode、locked install、CI 隔离、secret 最小权限。
- 剩余风险:例如 safe mode 不运行测试、lockfile 不覆盖工具内部依赖、受信配置仍可能有漏洞。
Worked example
场景:fork PR 要求自动刷新 mise.lock。
资产:bot token 与 runner。
攻击输入:PR 可修改 mise.toml。
选择:无业务 secret + MISE_SAFE=1 + 只做 lock dry-run + 人工审阅 diff。
不选择:mise trust 后运行 PR task。
剩余风险:backend/解析器自身漏洞与生成 diff 的审阅质量。
常见失败与下一步:
- “用了 trust,所以可以给 secret”:错误。trust 只表达配置允许加载/执行,不替代最小权限。
- “用了 safe mode,所以可以跑不可信测试”:错误。safe mode 应拒绝 task;要运行不可信代码需要容器、VM、权限和网络隔离等外部边界。
- “有 checksum,所以 hook 安全”:错误。checksum 处理下载工件,不审查仓库配置逻辑。
- trust 状态与预期不符:检查 global 配置、trusted paths、worktree 共享和 paranoid 设置,再记录环境差异。[S07]
29–30 分钟:检索问题
问题:为什么不能用 safe mode 跑不可信测试?
作答后查看参考答案
safe mode 的安全承诺是拒绝项目控制的可执行功能,包括 tasks;它不是隔离后允许执行这些功能的沙箱。需要运行不可信代码时,必须在 mise 之外建立真正的权限、文件系统、网络和 secret 隔离。
通过标准
- 能分别说明 trust、paranoid、safe、lock 与 secret store 解决的问题。
- safe mode 实验表现为“允许受限解析、拒绝 task”,没有执行演示 source。
- 两条 CI 路线的输入信任、可执行能力和 secret 权限不同。
- 威胁模型明确记录至少一个剩余风险。