## 我在上一个提交里弄坏了一个脚本 `client/electron/scripts/release-linux.sh` 原本是 **711**: group/other 读位缺失 ⇒ 本判据判红(**判得对**),但我给的药方是无脑 `chmod 644` ⇒ **去掉执行位** —— 把"读位缺失"换成"不能执行",**比原来更坏**, 而判据随后报"通过",工作区显示这个文件被改(`M`)。是我在提交前扫 `git status` 时 看见那一行 `M client/electron/scripts/release-linux.sh` 才发现的 —— **不是判据发现的**。 已恢复 755(`git diff` 干净,因为它 git 记录是 100755)。 ★ 教训:**一条判据说"这个文件被改错了"时,药方必须按文件类型分岔**; 只有一个药方(`chmod 644`)就会把另一类文件改坏,而它同样报"已修好"。 这与"判据的适用范围没写出来"同族,只是这次代价落在了**修的动作**上,不是读的动作上。 ## 改法 可执行与否按 **git 记录的那个位**(`git ls-files -s` 的 `100755`)判, 不按当前文件系统权限 —— 否则"执行位被谁弄丢了"会被这条判据**追认**成正常。 三条区分力实测(都当场恢复 + `cmp` 校验): 1. 非可执行文件改 0600 ⇒ 红,文案"权限过严"; 2. 可执行文件丢掉执行位 ⇒ 红,文案"**注意别去掉执行位**"; 3. 可执行文件 711(= `release-linux.sh` 原来的形态)⇒ 红 —— 即**最初那次判红是对的**, 错的是药方。 另外把计数循环改成读 `git ls-files -s` 一次(不再在循环里对每个文件调 `git ls-files --error-unmatch`,也不再让计数落在管道子 shell 里)。 验证:`check-file-modes.sh` 正常态 exit 0;三条变异行为如上; `git status` 只剩本文件本身的改动。
89 lines
4.4 KiB
Bash
Executable File
89 lines
4.4 KiB
Bash
Executable File
#!/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 不是"更严",它多了执行位、读位齐全,属正常。
|
||
# 允许的最严权限:0644(非可执行)/ 0755(可执行)。
|
||
#
|
||
# ★ 可执行与否按 **git 记录的那个位**判(`git ls-files -s` 的 100755),不按当前文件系统权限
|
||
# —— 否则"执行位被谁弄丢了"会被这条判据**追认**成正常。
|
||
#
|
||
# ★ 这条我第一版写错了,代价是**真的弄坏了一个脚本**:`client/electron/scripts/release-linux.sh`
|
||
# 原本是 **711**(group/other 读位缺失 ⇒ 本判据判红,判得对),但我给的药方是无脑
|
||
# `chmod 644` —— 那是**去掉执行位**,等于把"读位缺失"换成"不能执行",比原来更坏,
|
||
# 而判据随后报"通过"。⇒ 一条判据说"这个文件被改错了"时,**药方必须按文件类型分岔**;
|
||
# 只有一个药方就会把另一类文件改坏,且同样显示"已修好"。
|
||
fail=0
|
||
worst=""
|
||
count=0
|
||
while IFS= read -r line; do
|
||
gmode="${line%% *}"
|
||
f="${line##* }"
|
||
[ -n "$f" ] || continue
|
||
[ -f "$f" ] || continue
|
||
mode="$(stat -c '%a' "$f" 2>/dev/null)" || continue
|
||
m="${mode: -3}"
|
||
o="${m: -1}"; g="${m:1:1}"; u="${m:0:1}"
|
||
bad=0
|
||
# group 与 other 必须有读位
|
||
[ "$(( o & 4 ))" -eq 0 ] && bad=1
|
||
[ "$(( g & 4 ))" -eq 0 ] && bad=1
|
||
# git 记为可执行的,三处执行位都要在
|
||
if [ "$gmode" = "100755" ]; then
|
||
[ "$(( u & 1 ))" -eq 0 ] && bad=1
|
||
[ "$(( g & 1 ))" -eq 0 ] && bad=1
|
||
[ "$(( o & 1 ))" -eq 0 ] && bad=1
|
||
fi
|
||
[ "$bad" -eq 0 ] && continue
|
||
count=$((count + 1))
|
||
[ -z "$worst" ] && worst="$f"
|
||
if [ "$gmode" = "100755" ]; then
|
||
printf ' [FAIL] 权限不对:%s 是 %s(git 记为可执行 ⇒ 应为 0755,注意别去掉执行位)\n' "$f" "$mode" >&2
|
||
else
|
||
printf ' [FAIL] 权限过严:%s 是 %s(政策要求不严于 0644)\n' "$f" "$mode" >&2
|
||
fi
|
||
done < <(git ls-files -s | awk '{print $1" "$4}')
|
||
|
||
if [ "$count" -gt 0 ]; then
|
||
printf '\n 共 %d 个源文件权限不对(首个:%s)。\n' "$count" "$worst" >&2
|
||
printf ' 药方(**按类型分岔**):非可执行文件补齐到 644;git 记为 100755 的补齐到 755。\n' >&2
|
||
printf ' 原因:写文件的工具不理会 umask,而 `cp -a` 会把这个权限位带进生产快照,\n' >&2
|
||
printf ' 且 `cmp` / ① 内容判据都看不见它。\n' >&2
|
||
printf ' 这是**政策问题**(源文件应当可被构建/测试/部署以不同身份读),不是代码问题。\n' >&2
|
||
exit 1
|
||
fi
|
||
|
||
printf ' 源文件权限政策:通过(已跟踪文件都够读;git 记为可执行的都保留了执行位)\n'
|
||
exit 0
|
||
|