Files
MailUI4Agents/deploy/check-file-modes.sh
JianFeeeee 15df621045 feat(deploy): 权限位两条判据 —— 政策(源文件不严于 0644)+ 一致性(副本=仓库)
pi 评审 2026-09-15 §三 给了决定:**开,但拆两条**。理由我采纳并写进注释:
合成的结果会是"看起来覆盖了、其实只覆盖一半"。

## 为什么要这两条(实测实例,不是设想)

写文件的工具**不理会 umask**(umask 022,它建的仍是 0600),而
`deploy/redeploy-plugin.sh` 用 `cp -a "$SRC/."` 打快照 ⇒ **0600 会进生产**。
实测:快照里 `src/paths.mjs`/`src/turn-cwd.mjs` 是 0600、仓库 0644,
而 **`cmp` 五个 same、① 报"逐字节一致"** —— 两头都不报警:
`collectFiles` 只把**内容**做 sha256,`check-deploy-drift` 只在脚本上判可执行位。

## 两条的分工(别合成一条)

- `deploy/check-file-modes.sh` = **政策**:源文件不得比 0644 更严。
  **不看快照** ⇒ 能抓"两边都 0600",而一致性那条永远抓不到(两边一致 ⇒ 恒绿)。
- `check-deploy-drift.mjs` 新增 **①b**(`collectModes`/`diffModes`)= **一致性**:
  部署副本的权限 = 仓库那一份。抓不到"两边都错"。

## 落地

**政策侧**:新脚本判「已跟踪文件里 group/other 任一读位缺失」。
实测判出 **53 个**(含 `plugins/pi-mail-bridge/package.json`、`src/naming.mjs`、
`client/harmony/.../*.ets`、`docs/GUI-PLAN-HARMONY.md` 等)—— 全部 `chmod 644` 修掉,
现在 exit 0。**区分力实测**:把 `deploy/check-shared-libs.sh` 临时改 0600 ⇒ 判据红并点名它,
恢复后绿;0755 的可执行脚本**不**被判红(有读位,不是"更严")。
★ 也修正了我先前的一处过报:那 53 个里有凭据类命名的文件吗 —— **0 个**(先查了才批量改)。

**一致性侧**:①b 一上线就抓到**真实的**、**先于本次改动**存在的漂移:
`plugins/*-mail-bridge/lib/permission-grants.{js,d.ts}`、`rename-proposal.{js,d.ts}`
在三个宿主的快照里是 **600**、仓库是 **644**(pi 宿主 1 处、dsh 4 处、opencode 2 处)。
⇒ 这条缝**一直存在**,只是此前没有任何判据看着它。
**处理**:不单独 redeploy 去"洗"权限(那要重启 pi 宿主,为权限位重启服务不值得),
**下一次正常部署顺带修好** —— 这是记账,不是新欠账。
①b 现在是**失败**态(真实不一致),所以 `drift` 整表 exit 1;这与"① 全过"并存是对的,
两条量的是不同的东西。

## 自检

给 `collectModes`/`diffModes` 加了 4 条自检(`--self-check`),四条一起写,
因为"能发现差异"单独一条会被一个**恒判"都不同"**的坏实现骗过:
① 权限相同不得误报;② 内容一致但 0600 vs 0644 必须被发现(连数值一起断言);
③ 权限差异不污染 ① 的内容判据;④ 仅一侧存在的文件不算权限漂移(归 ① 的文件集判据)。
★ 其中两条我**第一版写错了**并当场修掉,都记在注释里:
- 用了 `lib/x.mjs` 做样本,而上面 `mk(b,'DIFFERENT')` 已把它改成内容不同
  ⇒ ③ 红在**内容**上,而它想验的是"权限不污染内容";改用一对独立的内容相同文件。
- 夹具真实创建的是 `lib/x.mjs` 而不是我以为的 `lib/same.mjs`(ENOENT 才发现)。

验证:`--self-check` 全绿;pi 桥 509/509;`check-shared-libs` exit 0;
`check-file-modes.sh` exit 0;`install.sh --check` exit 0。
2026-09-15 10:26:23 +08:00

67 lines
3.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# 源文件的权限位**政策**:不许比 0644 更严。
#
# # 为什么要这条(一个实测出来的实例)
#
# 这台机器上写文件的工具**不理会 umask**(umask 是 022,但它建的仍是 0600)。
# 后果不是"本地难看",而是它会**进生产**:`deploy/redeploy-plugin.sh` 用
# `cp -a "$SRC/." "$STAGING/"` 打快照,`-a` 保留权限位 ——
# 于是快照里 `src/paths.mjs`/`src/turn-cwd.mjs` 是 **0600**,而仓库是 0644。
#
# 两头都不报警:
# · `cmp` 只比**内容** ⇒ 报"逐字节相同"(实测就是这样);
# · `check-deploy-drift.mjs` 只比**内容**、且只在脚本上判**可执行位**(`mode & 0o111`)。
# ⇒ "线上与仓库不一致"里,**只有权限位这一类**完全没有判据看着。
#
# # 与漂移侧那条的分工(pi 评审 2026-09-15 §三 明确要求分开)
#
# · **本脚本 = 政策**:源文件不得比 0644 更严。它**不**看快照,
# 所以"两边都是 0600"这种情况它能抓到,而漂移侧永远抓不到(两边一致 ⇒ 恒绿)。
# · **漂移侧 = 一致性**:部署副本的权限位 = 仓库那一份。
# 它抓不到"两边都错"。
# 两条别合成一条:合成的结果是"看起来覆盖了、其实只覆盖一半"。
#
# # 为什么"更严"也算错
#
# 这些文件是**源码**,要被构建/测试/部署以不同身份读(`install.sh` 会切身份做可写性判定,
# 判据里还有以 nobody 跑的分支)。0600 在"跑的人恰好是属主"时不炸,一旦换身份就是
# EACCES —— 而那串报错看起来是**代码问题**。这条链一路上在消的就是这一类。
set -uo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO"
# 允许的最严权限:0644。比它更严(组/其他读位缺失)即判红。
# 例外:**可执行**脚本的 0755 不是"更严",它多了执行位、读位齐全,属正常。
fail=0
worst=""
count=0
while IFS= read -r f; do
[ -n "$f" ] || continue
# 只判 git 已跟踪的文件(未跟踪的构建产物不在这条政策范围内)
git ls-files --error-unmatch "$f" >/dev/null 2>&1 || continue
[ -f "$f" ] || continue
mode="$(stat -c '%a' "$f" 2>/dev/null)" || continue
# 取八进制后三位
m="${mode: -3}"
o="${m: -1}" # other 位
g="${m:1:1}" # group 位
# other 与 group 都必须有读位(4 及以上);否则这条政策判红
if [ "$(( o & 4 ))" -eq 0 ] || [ "$(( g & 4 ))" -eq 0 ]; then
count=$((count + 1))
[ -z "$worst" ] && worst="$f"
printf ' [FAIL] 权限过严:%s 是 %s(政策要求不严于 0644)\n' "$f" "$mode" >&2
fi
done < <(git ls-files)
if [ "$count" -gt 0 ]; then
printf '\n 共 %d 个源文件比 0644 更严(首个:%s)。\n' "$count" "$worst" >&2
printf ' 药方:chmod 644 <文件>。原因:写文件的工具不理会 umask,而 `cp -a` 会把这个\n' >&2
printf ' 权限位带进生产快照,且 `cmp`/漂移判据都看不见它。\n' >&2
printf ' 这是**政策问题**(源文件应当可被构建/测试/部署以不同身份读),不是代码问题。\n' >&2
exit 1
fi
printf ' 源文件权限政策:通过(没有任何已跟踪文件比 0644 更严)\n'
exit 0