Course Builder个人学习工作台 · 本地模式
mise 实战:从工具版本到可复现 CI

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

目标是安装工具并运行仓库测试。决策过程:

  1. 输入 commit 是否经过正常代码审阅?
  2. job 是否必须运行仓库 task?是。
  3. safe mode 会拒绝 task,因此不能把它当正常 test runner。
  4. 在隔离 CI runner 中运行 locked install 和 mise run ci,只注入该 job 必需的最小 secret。

路线 B:不可信 PR 上的 lock-bump/解析 bot

目标只是查看版本解析变化。决策过程:

  1. PR 配置不受 bot 所有者控制。
  2. job 不需要运行业务 task、hook 或 source。
  3. 使用 MISE_SAFE=1 mise lock --bump --dry-run --json,让项目配置无法执行代码。
  4. job 不获得业务 secret,只产出可审阅 diff 或机器可读结果。[S08]

不要把两条路线合并成“所有 CI 都 safe mode”。一个需要运行测试,一个明确不应运行项目代码。

26–29 分钟:写一个六格威胁模型

按以下顺序填写,每格至少一句:

  1. 资产:开发者凭据、CI token、源码、runner、构建结果。
  2. 攻击者/输入:陌生仓库、fork PR、被接管的发布源、误提交 secret。
  3. 配置执行面:templates、_.source、hooks、tasks、插件安装脚本。
  4. 供应链面:版本解析、下载 URL、checksum、provenance、backend 限制。
  5. 控制:审阅 + trust、paranoid、safe mode、locked install、CI 隔离、secret 最小权限。
  6. 剩余风险:例如 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 权限不同。
  • 威胁模型明确记录至少一个剩余风险。

Sources used

  • S04 jdx/mise, Environments.
  • S06 jdx/mise, mise.lock Lockfile.
  • S07 jdx/mise, mise trust.
  • S08 jdx/mise, Security.