# 第九章:安全模型与供应链边界
安装器天然处在高风险位置:它读取配置、访问网络、解压归档、运行构建脚本、修改 PATH 或系统包数据库,并把新的可执行文件放到用户会执行的位置。
安全不能只依赖“配置来自自己”,而要为每一层定义可验证的输入、最小权限和残余风险。
阅读本章时把每项保证拆成三部分:验证了什么、在哪个边界生效、仍然不能抵抗什么。
最后的残余风险清单不是附注,而是部署控制的输入。
## 9.1 信任边界
`bot-forge` 把以下内容视为不可信输入:
- 用户配置、覆盖层与 `include`;
- 远程 source、archive、Git repository 和包 metadata;
- subprocess stdout/stderr 与版本字符串;
- archive/Cargo work cache、工件清单、事务日志和托管注册表文件;
- 激活目标、Unix symlink、Windows 可执行文件副本和删除路径。
信任当前主机用户、操作系统和系统 trust store;APT、Homebrew、winget、npm、Cargo registry 等软件仓库保留自己的信任模型。上游包即使 identity 与 checksum 正确,也仍可能包含恶意代码。
## 9.2 配置是可执行策略
严格 Schema 拒绝未知字段,避免拼写错误静默关闭控制。语义校验进一步拒绝:
- 浮动 Cargo coordinate;
- 未固定 commit 的远程 Git tool/skill source;
- 不安全 URL 或缺 checksum;
- 依赖、能力与能力提供者冲突;
- 越界 include;
- 未授权 Shell;
- 缺少必需坐标、资源或安全元数据的高危安装后端。
组织应评审覆盖层,限制写权限,并通过 `config effective` 与 `plan --why` 审计最终事实,而不是只检查主配置的短文本。
## 9.3 Shell 是例外能力
类型化安装后端使用 program + argument vector,避免字符串拼接引入 command injection。Shell 默认由 `allow_shell = false` 禁止。
确实需要 Shell 时必须显式启用,并为每个组件声明 command、resource、timeout;能够可靠恢复时再声明 rollback。Shell Schema 不接受“幂等”自我声明,也不会自动重试任意 Shell。
Shell 不参与托管 Cargo 工件的原子回滚承诺;启用者需要承担其可能修改托管根目录之外状态的风险。
不要用 Shell 绕过已经存在的 Cargo、APT、Homebrew、archive、Git、rustup、npm 或 winget 安装后端。
## 9.4 固定来源和可复现输入
Cargo 要求精确 version;远程 Git 工具/skill source 要求 HTTPS 和固定十六进制 commit;features、target、toolchain、安装级别、bins、locked 状态与环境 allowlist 参与指纹计算。
Archive 始终必须提供 SHA-256,这项校验不能通过配置关闭。默认 Rust bootstrap 同样固定 `rustup-init` URL,并要求当前目标 triple 的精确 SHA-256。
下载复用已校验 archive cache,以参数向量无交互运行,不执行远端 Shell 文本。
正式附件发布后,安装脚本会先下载 `SHA256SUMS`,用调用方从可信发布页面提供的 `BOT_FORGE_CHECKSUMS_SHA256` 校验清单本身,再校验二进制文件与配置,全部通过后才执行。
独立取得的清单摘要把附件下载通道与信任锚分开,但仍不能证明发布者或上游代码没有恶意行为;签名、仓库权限和发布流程仍是外层控制。
## 9.5 下载、缓存和归档
- HTTPS endpoint 受 Schema 约束;
- archive 相同 key 在单次进程内去重,不让部分下载覆盖有效缓存;托管工件提交使用跨进程命名锁;
- cache 复用前重新验证,损坏内容进入 quarantine;
- archive extraction 拒绝 `..` 和绝对路径,默认拒绝 symlink/hardlink;显式启用 `allow_links` 时仅接受解析后仍位于安装目录内的 TAR link。安装目标必须是 `$BOT_FORGE_HOME` 下不经过 symlink 的安全路径;
- 归档文件临时下载校验后重命名到缓存;工件存储同步清单后再重命名,但当前不承诺载荷与父目录的断电级持久性;
- offline/cache-only 不会因 cache miss 偷偷访问网络。
内部镜像应在站点覆盖层中配置,不写进公开配方目录或公共议题。
## 9.6 构建与进程隔离
Cargo build 使用独立暂存区/target 和 env-clear allowlist;PATH、HOME/USERPROFILE、SYSTEMROOT、TEMP/TMP 与 `build_env_allow` 明确变量会传入子进程。命令宿主提供:
- program/args 分离;
- timeout 与 inactivity timeout;
- 有界输出 tail 和完整受限日志;
- Unix process group 与 Windows Job Object;
- run-scoped graceful/forced cancellation;
- error taxonomy;托管 Cargo 只对幂等且归类为瞬态的失败自动重试,integrity/config/cancelled 错误不重试;Git 事务尚未自动重试。
构建脚本仍是上游代码,会以当前用户权限运行。对不可信 crate 应使用额外 OS sandbox、容器或专用构建机;bot-forge 的进程管理不是完整恶意代码沙箱。
## 9.7 验证、存储和激活
Store 前按暂存区绝对路径检查完整二进制文件集并计算 checksum;配置的健康检查当前在激活后运行,失败时回滚。
由于健康检查仍按命令名解析 PATH,托管 bin 的 PATH 顺序属于使用者必须审计的边界。Store 写入 checksum、清单和来源信息,并把文件设为只读。
激活阶段先锁住完整的规范二进制文件名集合,再逐个原子替换 Unix symlink 或 Windows `.exe` 副本;失败时逆序回滚已切换项,但整组并非单次文件系统原子提交。
Remove 在 lexical/canonical 托管目标边界内验证目标,不跟随链接把删除扩展到托管根之外。
当前用户仍能修改自己的数据目录;只读权限和 checksum 用于检测/减少意外变更,不能抵抗拥有同等权限的主动攻击者。
## 9.8 托管注册表、事务日志与锁
托管注册表写入和提交点使用跨进程锁与原子替换。事务日志绑定配置/计划哈希,阶段变化追加记录。
状态文件同样按不可信输入解析:
- 托管注册表与事务日志拒绝未知字段;
- 托管注册表读写会校验 `kind`/`backend`、`artifact`/`hash`、`target`/`binary`、source revision 与记录唯一性之间的语义关系;
- 事务日志会拒绝路径穿越标识符、非 SHA-256 hash,以及缺少工件/激活元数据的已提交阶段;
- `resume` 校验配置/计划哈希,`remove` 再校验托管目标边界。
工件清单内的 checksum/provenance 仍由工件存储在使用时验证,不能只凭 JSON Schema 通过就信任磁盘内容。不要手工编辑事务日志来强制 `Recorded`,也不要删除待处理事务日志后假设激活已经安全恢复。
## 9.9 密钥与输出安全
代理 URL 中的 `user:password@` 会脱敏;Cargo config 只报告路径和代理设置数量,不回显完整认证字段。统一 redactor 覆盖 UI、event、log 和 error。
仍应遵守:
- 不在公共仓库提交内部 mirror、token 或生产覆盖层;
- 不把 `--show-sensitive` 输出发送到 CI log;
- 使用最小权限运行,只有真正需要写系统目录时才提升权限;
- 为数据目录与配置文件设置合适权限;
- 安全问题按 [SECURITY.md](../../SECURITY.md) 私下报告。
## 9.10 APT、Homebrew 与系统包管理器
`apt-mirror check` 使用隔离目录验证候选 source;`apply` 备份并写入,`restore` 恢复备份。它不会关闭签名检查,也不会自动禁用已有 source。
APT、Homebrew、winget 等操作修改全局或用户包数据库,事务边界由这些工具决定。Homebrew 调用使用类型化 formula 参数、禁用隐式 auto-update,并受跨进程锁保护;bot-forge 不会自动安装 Homebrew。
bot-forge 可以安排、记录和验证命令,但不能承诺把任意系统升级完全恢复到字节级旧状态。系统包的 remove 也不会通过任意持久化 Shell 自动执行。
## 9.11 残余风险
主要残余风险:
1. 上游包、crate 或构建脚本本身恶意;
2. 用户启用 Shell 后修改托管根之外状态;
3. 系统包管理器的脚本和依赖副作用;
4. 当前用户可篡改自己的配置和数据目录;
5. 正确 checksum 对应的发布制品仍可能由受损发布链产生;
6. 网络、磁盘或 OS 崩溃发生在外部工具自身不可事务化的阶段。
缓解策略是可信覆盖层、固定来源、最小权限、OS sandbox、发布签名、先 `plan`、定期 `status` 与事务日志审计,而不是声称风险完全消失。
| 上游代码 | 固定身份、校验、隔离工作目录 | sandbox、可信仓库、代码审计 |
| 系统包管理器 | 类型化参数、锁、结果验证 | 主机快照、运维回滚策略 |
| 用户目录篡改 | checksum、严格解析、托管路径校验 | 文件权限、主机账户安全 |
| 发布链受损 | 附件 checksum 与清单校验 | 独立信任锚、签名、发布权限 |
## 9.12 本章小结
安全模型覆盖配置、来源、缓存、归档、构建、激活、状态和输出,但不会把系统包管理器、Shell 或上游代码伪装成完全受控沙箱。每种保证都有明确边界,残余风险需要组织层面的权限与供应链控制补足。
---