65120a1c65
- docs/permissions-allow.json + scripts/settings-allow-sync:四個 Gitea 正門工具的權限白名單一份, setup script 裝完 plugin 寫一次、SessionStart 每次再對一次(只加不減、冪等) - hooks/lib/mainline.py/scripts/mainline:家目錄沒主線就讀 InkStoneCo/system-dev/mainline.json; set/adopt/clear 兩份一起寫,refresh 只寫家目錄 - hooks/leo21c-write-guard.sh:唯讀 -d(tr/cut/sort…)先剪掉再判、notify_leo trigger 放行 (與 prod-write-guard 同一份白名單)、改法段改印 09-02 起的 youlin 子網域;補第一支測試(26 條) - prod-write-guard/main-and-prod-push-guard/kbdb-live-exam:認得 youlin 新子網域 arcrun-yuga3bse - scripts/ticket:收件 repo 寫 inkstone/ISEP 不再 404(org 寫錯當場講) - scripts/isep-notify:有 TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID 先走 Bot API 直送(雲端唯一通的路) - 測試:A31–A35 共 79 條;README/plugin.json/hooks-inventory 數字實數(61 支、85 條、53 支腳本) 假設(記在這裡等 review):權限規則的形狀沿用 leo 09-07 親手加、實測有效的那四條; 「分類器真的不擋」要雲端一趟 run 的 permission_denials 才驗得到,本 PR 驗不了。 版本:待總管定版(plugin.json 仍 0.22.0)。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DTZ9QtvjY7MNxjfQbexAm7
81 lines
5.4 KiB
Bash
Executable File
81 lines
5.4 KiB
Bash
Executable File
#!/bin/bash
|
||
# isep-notify 的「Bot API 直送」測試(inkstone/ISEP#130)
|
||
#
|
||
# 它守什麼: 雲端唯一走得通的通知路。2026-09-07 從雲端實測:leo21c 的 notify_leo 回 404、
|
||
# youlin 被 egress proxy 擋(403 policy denial)、api.telegram.org 通。
|
||
# ⇒ 有 TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID 就先直送;沒有要講清楚缺哪兩個,不能靜默。
|
||
#
|
||
# 🔴 全程不打真 Telegram:Bot API 指到本機一個假伺服器(只回固定 JSON),
|
||
# 實例那條用 ISEP_NOTIFY_OFFLINE=1 關掉、閘用 ISEP_NOTIFY_GUARD 指到真跡。
|
||
cd "$(dirname "$0")/.." || exit 1
|
||
N=scripts/isep-notify
|
||
TMP=$(mktemp -d); trap 'rm -rf "$TMP"; [ -n "${SRV:-}" ] && kill "$SRV" 2>/dev/null' EXIT
|
||
export ISEP_NOTIFY_STATE_DIR="$TMP/state" ISEP_NOTIFY_GUARD="$PWD/hooks/prod-write-guard.sh"
|
||
unset TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID
|
||
PASS=0; FAIL=0
|
||
ok(){ PASS=$((PASS+1)); printf ' ✅ %s\n' "$1"; }
|
||
bad(){ FAIL=$((FAIL+1)); printf ' ❌ %s\n' "$1"; }
|
||
|
||
# 假 Bot API:記下收到什麼,回 Telegram 的形狀
|
||
cat > "$TMP/srv.py" <<'PY'
|
||
import json, sys
|
||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||
LOG = sys.argv[2]
|
||
class H(BaseHTTPRequestHandler):
|
||
def do_POST(self):
|
||
body = self.rfile.read(int(self.headers.get("Content-Length") or 0)).decode()
|
||
open(LOG, "a").write(json.dumps({"path": self.path, "body": body, "ua": self.headers.get("User-Agent")}) + "\n")
|
||
ok = "/botGOOD/" in self.path
|
||
out = json.dumps({"ok": True, "result": {"message_id": 42}} if ok else {"ok": False, "description": "Unauthorized"}).encode()
|
||
self.send_response(200 if ok else 401); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(out)
|
||
def log_message(self, *a): pass
|
||
HTTPServer(("127.0.0.1", int(sys.argv[1])), H).serve_forever()
|
||
PY
|
||
PORT=$(python3 -c 'import socket;s=socket.socket();s.bind(("127.0.0.1",0));print(s.getsockname()[1])')
|
||
python3 "$TMP/srv.py" "$PORT" "$TMP/log" & SRV=$!
|
||
for i in 1 2 3 4 5 6 7 8 9 10; do curl -s -o /dev/null "http://127.0.0.1:$PORT/" && break; sleep 0.2; done
|
||
export ISEP_NOTIFY_BOT_API="http://127.0.0.1:$PORT"
|
||
|
||
echo "── A. 沒有憑證:講清楚缺哪兩個,不靜默 ──"
|
||
OUT=$(ISEP_NOTIFY_OFFLINE=1 python3 "$N" --text "測試" 2>&1); RC=$?
|
||
printf '%s' "$OUT" | grep -q 'TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID' && ok "A1 沒憑證 ⇒ 報告點名缺 TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID(leo 看了知道要在 Cloud environment 加什麼)" || bad "A1 沒點名缺什麼:$OUT"
|
||
[ "$RC" != 0 ] && printf '%s' "$OUT" | grep -q 'leo 的手機上沒有出現' && ok "A2 兩條路都沒通 ⇒ 離開碼非 0、明說沒送到" || bad "A2 rc=$RC:$OUT"
|
||
[ ! -s "$TMP/log" ] && ok "A3 沒憑證就不打 Bot API" || bad "A3 沒憑證還打了:$(cat "$TMP/log")"
|
||
|
||
echo "── B. 有憑證:先直送,送到就結束 ──"
|
||
: > "$TMP/log"
|
||
OUT=$(TELEGRAM_BOT_TOKEN=GOOD TELEGRAM_CHAT_ID=777 python3 "$N" --text "雲端收工摘要" 2>&1); RC=$?
|
||
[ "$RC" = 0 ] && printf '%s' "$OUT" | grep -q 'Bot API 直送):送到了' && ok "B1 憑證在 ⇒ Bot API 直送,離開碼 0" || bad "B1 rc=$RC:$OUT"
|
||
python3 - "$TMP/log" <<'PY' && ok "B2 打到 /botGOOD/sendMessage、chat_id=777、內文有署名、UA 帶了" || bad "B2 送出去的形狀不對:$(cat "$TMP/log")"
|
||
import json, sys
|
||
rows = [json.loads(l) for l in open(sys.argv[1])]
|
||
assert len(rows) == 1, rows
|
||
r = rows[0]; b = json.loads(r["body"])
|
||
assert r["path"] == "/botGOOD/sendMessage", r["path"]
|
||
assert b["chat_id"] == "777" and b["text"].startswith("[總管] 雲端收工摘要"), b
|
||
assert "isep-notify" in (r["ua"] or ""), r["ua"]
|
||
PY
|
||
printf '%s' "$OUT" | grep -q 'notify_leo' && bad "B3 直送成功後不該再碰實例那條" || ok "B3 直送成功 ⇒ 不再打實例的 notify_leo"
|
||
|
||
echo "── C. 憑證錯:Bot API 回 ok=false ⇒ 不算送到,退到原本那條 ──"
|
||
: > "$TMP/log"
|
||
# 實例那條也指到假伺服器(它對非 /botGOOD/ 的路徑回 401)——兩條都不出這台機器
|
||
OUT=$(TELEGRAM_BOT_TOKEN=BAD TELEGRAM_CHAT_ID=777 ISEP_NOTIFY_URL="http://127.0.0.1:$PORT/webhooks/named/leo/notify_leo/trigger" python3 "$N" --text "x" 2>&1); RC=$?
|
||
printf '%s' "$OUT" | grep -q 'Bot API HTTP 401' && ok "C1 401 被講出來(不是「送出成功」)" || bad "C1 沒講 401:$OUT"
|
||
[ "$RC" != 0 ] && printf '%s' "$OUT" | grep -q 'leo 的手機上沒有出現' && ok "C2 直送失敗+實例關著 ⇒ 仍然明說沒送到" || bad "C2 rc=$RC:$OUT"
|
||
|
||
echo "── D. 閘說會擋,直送仍走得通(它不碰實例)──"
|
||
: > "$TMP/log"; mkdir -p "$TMP/oldguard"
|
||
printf '#!/bin/sh\nexit 2\n' > "$TMP/oldguard/prod-write-guard.sh"; chmod +x "$TMP/oldguard/prod-write-guard.sh"
|
||
OUT=$(ISEP_NOTIFY_GUARD="$TMP/oldguard/prod-write-guard.sh" TELEGRAM_BOT_TOKEN=GOOD TELEGRAM_CHAT_ID=1 python3 "$N" --text "y" 2>&1); RC=$?
|
||
[ "$RC" = 0 ] && printf '%s' "$OUT" | grep -q '通知閘:\*\*block\*\*' && printf '%s' "$OUT" | grep -q '直送):送到了' \
|
||
&& ok "D1 閘 block + 憑證在 ⇒ 直送照樣送到(閘擋的是寫實例,直送沒碰實例)" || bad "D1 rc=$RC:$OUT"
|
||
|
||
echo "── E. --dry-run 什麼都不送 ──"
|
||
: > "$TMP/log"
|
||
OUT=$(TELEGRAM_BOT_TOKEN=GOOD TELEGRAM_CHAT_ID=1 python3 "$N" --text "z" --dry-run 2>&1)
|
||
[ ! -s "$TMP/log" ] && printf '%s' "$OUT" | grep -q 'dry-run' && ok "E1 --dry-run:Bot API 一次都沒被打" || bad "E1 dry-run 打了:$(cat "$TMP/log")"
|
||
|
||
echo; echo "$PASS/$((PASS+FAIL)) 通過"
|
||
[ "$FAIL" -eq 0 ]
|