0cdb6f2c05
inkstone/InkStoneCo#23、#56 同一個病:閘對整條指令字串做關鍵字掃描, 把「檔案內容/留言引用裡剛好提到某個關鍵字」當成「真的在執行」, 同時放過包一層讀取指令、或藏在 heredoc body 裡的真動作。 - 新增共用輔助 hooks/lib/strip_heredoc.py:heredoc body 是資料不是指令, 四支閘(github-contact / main-and-prod-push / stage-before-prod / kbdb-api-wall 的 Bash 分支)呼叫前一律先拿掉 body 再比對。 - main-and-prod-push-guard.sh:修掉跟 release-tag-guard.sh 同款的 「開頭是讀取工具就整條放行」前綴繞過洞;git push 的偵測改成指令位置比對; main/master 目標改用單字邊界,不再誤中 "domain" 這種子字串。 - github-contact-guard.sh:拿掉 gh CLI/git push 判準裡「前面隨便一個空白 就算數」的鬆散邊界,只認真正的指令分隔符。 - kbdb-api-wall-guard.sh:Bash 分支原本引用不存在的 kbdb_cmd_check.py, python3 找不到檔案就吃掉錯誤印 "OK",該分支形同虛設——任何 `wrangler d1 execute` 直打 kbdb 都會被放行。邏輯搬進新檔 hooks/lib/kbdb_cmd_check.py(shlex 分詞、quote-aware),把 .sh 的 參照路徑改過去,補回 Bash 分支的 kbdb-sql-ok 逃生口。 四支各補 InkStoneCo#40 §1 要求的三行中文檔頭。 新增四支可重跑測試(scripts/test-*.sh),共 69 條斷言全過, 含 #23/#56 票上實撞的原始形狀(寫 docs/TESTING.md 的 heredoc、 貼引用 #56 敘述的留言、`grep git push`)。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
74 lines
2.6 KiB
Python
74 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""hooks/lib/strip_heredoc.py -- shared helper, not a gate (inkstone/ISEP#40 S7:
|
|
helpers live in lib/ and do not count as a hand-written gate).
|
|
|
|
What it guards: nothing by itself. It strips the *body* of a bash heredoc out
|
|
of a command string, keeping the start line (the control-flow part) intact.
|
|
|
|
Why: a heredoc body is data, not an instruction. Two mis-blocks on
|
|
2026-08-20 (inkstone/InkStoneCo#23, #56) shared one root cause: a gate ran a
|
|
keyword scan over the *entire* command string, heredoc body included, so text
|
|
that merely *mentioned* a trigger phrase inside a file being written (or a
|
|
comment being posted) was treated as if that phrase were actually being
|
|
executed. One case was writing docs/TESTING.md (the body had one example line
|
|
of a GitHub push command as literal text for a human to try later); the other
|
|
was posting a Gitea comment that quoted this very ticket's own description.
|
|
This helper only removes the body; each gate keeps its own keyword rules, it
|
|
just no longer has to solve heredoc-quoting itself.
|
|
|
|
Usage:
|
|
printf '%s' "$CMD" | python3 hooks/lib/strip_heredoc.py
|
|
|
|
Or import it as a module:
|
|
from strip_heredoc import strip_heredocs
|
|
"""
|
|
import re
|
|
import sys
|
|
|
|
# <<EOF <<-EOF <<~EOF <<'EOF' <<"EOF" (only one modifier is valid at a
|
|
# time in real bash; both are accepted here so a odd combo still matches).
|
|
_START_RE = re.compile(r"<<(-|~)?[ \t]*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\2")
|
|
|
|
|
|
def strip_heredocs(cmd: str) -> str:
|
|
"""Replace every heredoc body in cmd with nothing; keep the start line
|
|
and everything outside heredocs untouched."""
|
|
lines = cmd.split("\n")
|
|
out = []
|
|
i = 0
|
|
n = len(lines)
|
|
while i < n:
|
|
line = lines[i]
|
|
m = _START_RE.search(line)
|
|
if not m:
|
|
out.append(line)
|
|
i += 1
|
|
continue
|
|
strip_tabs = m.group(1) == "-"
|
|
delim = m.group(3)
|
|
out.append(line) # the start line itself is control flow, keep it
|
|
i += 1
|
|
found_end = False
|
|
while i < n:
|
|
probe = lines[i]
|
|
check = probe.lstrip("\t") if strip_tabs else probe
|
|
if check == delim:
|
|
i += 1 # the terminator line is a marker, drop it too
|
|
found_end = True
|
|
break
|
|
i += 1 # body line: drop it, do not append to out
|
|
if not found_end:
|
|
# command was truncated / no terminator found -- do not invent
|
|
# one, we've already consumed to the end of the string.
|
|
pass
|
|
return "\n".join(out)
|
|
|
|
|
|
def main() -> None:
|
|
cmd = sys.stdin.read()
|
|
sys.stdout.write(strip_heredocs(cmd))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|