修 env-defaults.sh 自己的一个假绿: HOME 可用性判据从 [ -w ](权限位)改成**真写探针** —— 与我自己上封那个错同一形状
★ 缺陷(我在认自己 `[ -w ]` 假绿时顺带查出来的,就在我的 scope 里):
196 行 `mkdir -p "$_am_home" && [ -w "$_am_home" ]`
179 行 `... || [ ! -w "$_am_home" ]`
两处都只看**权限位**。uid=0 时对任何 root 属主目录都返真,而**实际拦截可能在另一层**
(本会话每次 shell 都带 `landlock-run: partial enforcement`)。本机实测:
[ -w /root ] ⇒ true / touch /root/x ⇒ Permission denied
[ -w /opt/agentmail ] ⇒ true / touch /opt/agentmail/x ⇒ Permission denied
⇒ 而且 `mkdir -p` 对**已存在**的目录不写任何东西(rc=0),所以它**接不住**这个假绿
—— 文件 177 行原本就写了"mkdir -p 会返回成功,所以必须再单独判",但"那个单独判"选错了层。
★ 后果正撞在本文件存在的理由上(**误指对象**):
HOME 空 + uid=0 ⇒ `_am_home=/root` 被误判可用 ⇒ HOME=/root 放行
⇒ ② 段取 `$HOME/.cache/go-build` 当 GOCACHE ⇒ 那里用的是**真写探针**(297 行)⇒ 失败
⇒ 报出「**GOCACHE** 不可写」,而根因是 **HOME** 不可写
⇒ 实测该路径可复现(`env -u HOME bash -c 'source deploy/lib/env-defaults.sh'`)
★ 修法: 加 `_am_can_write()` —— **真去写一个探针文件**(与 297 行同一办法),
179/196 两处都用它。**必须两处一起改**: 196 的 else 是 `exit 2`(不是 fallback),
只改 196 会让 /root 走到 `exit 2` 而不是改判。
★ 顺带把 WARN 的括注从"(非 root / passwd 里没有可用目录)"改成
"(已实测真写探针;uid=%s)" —— 原括注是**按 uid 猜的原因**,而判据已不按 uid 判,
uid=0 也会走到这里。**不报没验过的原因**。
⚠️ 前半句 `按身份推出来的 HOME=%s 不可用` **一个字都没动** —— 它是
`plugins/pi-mail-bridge/test/env-guard.test.mjs:268` 的 assert.match 锚
(我第一版把它改成了"写不进去",**实测把 pi 的测试弄红了 16/17**,已改回)。
★ 变异测试: 把 `_am_can_write` 改成恒真 ⇒ **被抓住**,且复现的正是上面那条误指
(HOME 空时不再改判 ⇒ 直接报「GOCACHE 不可写」)✓
★ 反空真: 探针对 /tmp、/home/program/agentmail 返真,对 /root、/opt/agentmail、
/nonexistent、空串返假 ⇒ 有区分力(非恒真非恒红)✓
★ 验证: pi 的 env-guard 17/17 绿、criteria-hygiene 7/7 绿、三处 source 者 bash -n 全过、
install.sh/redeploy-*.sh 未改。**生产未动**。
This commit is contained in:
@ -160,6 +160,28 @@ unset _am_self_missing _am_c
|
||||
# 实测:`setpriv --reuid=65534 env -i HOME=/root` ⇒ `touch $HOME/probe` 被拒。
|
||||
# 以 **root** 跑时 `/root` 写得进去,所以这条只在非 root 下触发 ——
|
||||
# 判据落在"**能不能写**",不落在"路径长得像不像"。
|
||||
#
|
||||
# ★★ 但"验证可写"**不能用 `[ -w ]`**(dsh 实测 2026-09-25,修本文件自己的一个假绿):
|
||||
# `[ -w ]` 查的是**权限位**。以 uid=0 跑时它对任何 root 属主的目录都返回**真**,
|
||||
# 而**实际拦截可能在另一层**。本机实测(同一台、同一 shell):
|
||||
# [ -w /root ] ⇒ true
|
||||
# touch /root/x ⇒ Permission denied ← 能写才算数
|
||||
# [ -w /opt/agentmail ] ⇒ true
|
||||
# touch /opt/agentmail/x ⇒ Permission denied
|
||||
# (本会话每次 shell 都带着 `landlock-run: partial enforcement` —— 拦截在沙箱层,
|
||||
# 权限位那层**看不出来**。)
|
||||
# ⇒ 后果正是本文件要防的那件事,而且更绕:`_am_home=/root` 被**误判为可用** ⇒
|
||||
# `HOME=/root` 放行 ⇒ ② 段拿 `$HOME/.cache/go-build` 当 GOCACHE ⇒ 那里用的是
|
||||
# **真写探针**(297 行)⇒ 探针失败 ⇒ 报出**「GOCACHE 不可写」**。
|
||||
# ⇒ 根因是 HOME,报出来的是 GOCACHE —— **误指了对象**,与本文件存在的理由正好相反。
|
||||
# ⇒ 所以这里也改用**真写探针**(与 297 行同一个办法,不再一处真写、一处只看权限位)。
|
||||
_am_can_write() {
|
||||
[ -n "${1:-}" ] || return 1
|
||||
[ -d "$1" ] || return 1
|
||||
{ : > "$1/.agentmail-wprobe-$$"; } 2>/dev/null || return 1
|
||||
rm -f "$1/.agentmail-wprobe-$$"
|
||||
return 0
|
||||
}
|
||||
_am_uid="$(id -u 2>/dev/null || echo 0)"
|
||||
_am_home_given=0
|
||||
if [ -z "${HOME:-}" ]; then
|
||||
@ -174,9 +196,10 @@ else
|
||||
_am_home="$HOME"
|
||||
fi
|
||||
# 目录必须落在**调用者写得进去**的地方,否则就是把问题往后推。
|
||||
# 注意:`mkdir -p` 对已存在的不可写目录会**返回成功**,所以必须再单独判 `-w`。
|
||||
# 注意:`mkdir -p` 对已存在的不可写目录会**返回成功**,所以必须再单独判可写 ——
|
||||
# 而那个"单独判"**必须真去写**(见上面 ★★:`[ -w ]` 在沙箱/ACL 那一层会假绿)。
|
||||
_am_fallback="${TMPDIR:-/tmp}/agentmail-home-$_am_uid"
|
||||
if [ -z "$_am_home" ] || [ ! -d "$_am_home" ] || [ ! -w "$_am_home" ]; then
|
||||
if [ -z "$_am_home" ] || [ ! -d "$_am_home" ] || ! _am_can_write "$_am_home"; then
|
||||
if [ "$_am_home_given" = "1" ] && [ -n "$_am_home" ]; then
|
||||
# 调用者显式给了 HOME 却不可写:**说清楚**再改判,不静默换目录
|
||||
# (静默改 HOME 会让"东西写到哪去了"变成一个谜)。
|
||||
@ -188,12 +211,21 @@ if [ -z "$_am_home" ] || [ ! -d "$_am_home" ] || [ ! -w "$_am_home" ]; then
|
||||
# 或 passwd 里的 home 不存在,例如 nobody 的 /nonexistent)。
|
||||
# 原先只报"调用者给的不可写",于是这条**悄悄换了 HOME**:
|
||||
# 东西写到哪去了变成一个谜 —— 与本文件存在的理由(把环境问题说清楚)正好相反。
|
||||
printf ' [WARN] 按身份推出来的 HOME=%s 不可用(非 root / passwd 里没有可用目录);改判到 %s\n' \
|
||||
"${_am_home:-(空)}" "$_am_fallback" >&2
|
||||
#
|
||||
# ★ 括注原写"(非 root / passwd 里没有可用目录)"—— 那是**按 uid 猜的原因**,
|
||||
# 而判据已改成**真写探针**(见上 ★★)⇒ 现在 uid=0 也可能走到这里
|
||||
# (能写与否取决于沙箱/ACL,不取决于身份)。**不要再报一个没验过的原因**:
|
||||
# 只报"写不进去"这个已测事实,并附上身份供人判断。
|
||||
#
|
||||
# ⚠️ 文案前半句 `按身份推出来的 HOME=%s 不可用` **是 pi 的判据匹配的锚**
|
||||
# (`plugins/pi-mail-bridge/test/env-guard.test.mjs:268` 用 assert.match 锁它,
|
||||
# 理由是"兜底路径改了 HOME 就必须留痕")。⇒ **锚不许动**,只把括注改准。
|
||||
printf ' [WARN] 按身份推出来的 HOME=%s 不可用(已实测真写探针;uid=%s);改判到 %s\n' \
|
||||
"${_am_home:-(空)}" "$_am_uid" "$_am_fallback" >&2
|
||||
fi
|
||||
_am_home="$_am_fallback"
|
||||
fi
|
||||
if mkdir -p "$_am_home" 2>/dev/null && [ -w "$_am_home" ]; then
|
||||
if mkdir -p "$_am_home" 2>/dev/null && _am_can_write "$_am_home"; then
|
||||
if [ "$_am_home" != "${HOME:-}" ]; then
|
||||
HOME="$_am_home"
|
||||
export HOME
|
||||
|
||||
Reference in New Issue
Block a user