83a01fe028
- auth_static_key WASM: 偵測 Authorization header "Basic <x>:<y>" (含冒號
的 user:pass 原文), 自動 base64 編碼; 無冒號則維持原樣 (向後相容
已 base64 過的值).
這涵蓋 twilio / jira / mailgun 三個 Basic Auth recipe, 用戶 recipe
只需寫 'Basic {{secret.user}}:{{secret.key}}' 直覺語法.
- 新增 3 個 recipe (auth-recipe-seeds.ts):
• gemini — static_key / header x-goog-api-key (單 secret)
• trello — static_key / QUERY key+token (雙 secret, 第一個 query
injection 測試覆蓋)
• mailgun — static_key / HEADER Basic api:<key> (雙 secret Basic Auth)
- hook fix (pre-write-guard.sh): 放行 auth-recipe-seeds.ts 的 {{secret.X}}
字面值. 該檔是 RECIPES KV 的 seed 資料, 不是 TS 展開邏輯;
真正展開仍在 WASM 完成.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
191 lines
9.8 KiB
Bash
Executable File
191 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
||
# .claude/hooks/pre-write-guard.sh
|
||
# arcrun PreToolUse guard for Write / Edit / MultiEdit
|
||
#
|
||
# 職責:擋下會違反 CLAUDE rules 的檔案寫入操作
|
||
# 退出 code:
|
||
# 0 = 允許
|
||
# 2 = 擋下(stderr 訊息會回傳給 CC)
|
||
#
|
||
# 依賴:jq
|
||
|
||
set -o pipefail
|
||
|
||
INPUT=$(cat)
|
||
|
||
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // ""')
|
||
# 取得將要寫入的內容(Write: content;Edit: new_string;MultiEdit: edits[].new_string 全部串起來)
|
||
CONTENT=$(echo "$INPUT" | jq -r '
|
||
.tool_input.content
|
||
// .tool_input.new_string
|
||
// (.tool_input.edits // [] | map(.new_string // "") | join("\n"))
|
||
// ""
|
||
')
|
||
|
||
block() {
|
||
local rule="$1"
|
||
local reason="$2"
|
||
local fix="$3"
|
||
cat >&2 <<EOF
|
||
❌ BLOCKED by arcrun CLAUDE rules
|
||
違反項:${rule}
|
||
檔案:${FILE_PATH}
|
||
原因:${reason}
|
||
正確做法:${fix}
|
||
參考:.claude/rules/02-forbidden.md
|
||
EOF
|
||
exit 2
|
||
}
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# 規則 1.1:registry/components/ 下不准 TS(除非是 AssemblyScript)
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
if [[ "$FILE_PATH" == *"registry/components/"* && "$FILE_PATH" == *.ts ]]; then
|
||
# 允許 asconfig.json 同目錄的 AssemblyScript
|
||
COMP_DIR=$(dirname "$FILE_PATH")
|
||
if [[ ! -f "$COMP_DIR/asconfig.json" ]]; then
|
||
block "1.1" \
|
||
"registry/components/ 下禁止 TypeScript(除非是 AssemblyScript 且同目錄有 asconfig.json)" \
|
||
"零件必須用 TinyGo (main.go) 或 AssemblyScript 實作並編譯成 .wasm"
|
||
fi
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# 規則 1.2:禁止在非法位置新增 auth/credential 實作
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# 合法位置:registry/components/auth_static_key | auth_oauth2 | auth_service_account | auth_mtls
|
||
if [[ "$FILE_PATH" =~ auth[-_](static[-_]key|oauth2|service[-_]account|mtls) ]]; then
|
||
if [[ "$FILE_PATH" != *"registry/components/auth_"* ]]; then
|
||
block "1.2" \
|
||
"auth primitive 實作只能放在 registry/components/auth_<type>/" \
|
||
"改去 registry/components/auth_static_key/ 等目錄,用 TinyGo 實作 main.go"
|
||
fi
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# 規則 2.1:禁止新增含特定關鍵字的 TS 檔案(credential-injector / jwt-signer 等)
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
if [[ "$FILE_PATH" == *.ts ]]; then
|
||
BASE=$(basename "$FILE_PATH")
|
||
# 既有的 credential-injector.ts / jwt-signer.ts 允許修改(為了刪除),但不准新增同名
|
||
if [[ "$BASE" =~ ^(credential[-_]injector|jwt[-_]signer)\.ts$ ]]; then
|
||
if [[ ! -f "$FILE_PATH" ]]; then
|
||
block "2.1" \
|
||
"禁止新增 ${BASE}(Phase 1-3 的目標是刪除此類檔案,不是重建)" \
|
||
"credential 注入 / JWT signing 屬於 WASM 零件職責,改去 registry/components/auth_*/"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# 規則 2.2:cypher-executor TS 裡不准實作業務邏輯(只准 wasi-shim.ts 做 crypto)
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
if [[ "$FILE_PATH" == *"cypher-executor/src/"* && "$FILE_PATH" == *.ts ]]; then
|
||
BASE=$(basename "$FILE_PATH")
|
||
|
||
# crypto.subtle.decrypt:只准在 wasi-shim.ts
|
||
if echo "$CONTENT" | grep -qE "crypto\.subtle\.decrypt"; then
|
||
if [[ "$BASE" != "wasi-shim.ts" ]]; then
|
||
block "2.2" \
|
||
"AES-GCM 解密(crypto.subtle.decrypt)只准出現在 wasi-shim.ts 的 crypto_decrypt host function" \
|
||
"把解密邏輯移到 wasi-shim.ts 的 host function;或讓 WASM 零件透過 u6u.crypto_decrypt 呼叫"
|
||
fi
|
||
fi
|
||
|
||
# crypto.subtle.sign with RSASSA:只准在 wasi-shim.ts
|
||
if echo "$CONTENT" | grep -qE "crypto\.subtle\.sign.*RSASSA"; then
|
||
if [[ "$BASE" != "wasi-shim.ts" ]]; then
|
||
block "2.2" \
|
||
"RS256 簽章只准出現在 wasi-shim.ts 的 crypto_sign_rs256 host function" \
|
||
"把簽章移到 wasi-shim.ts;或讓 auth_service_account WASM 透過 u6u.crypto_sign_rs256 呼叫"
|
||
fi
|
||
fi
|
||
|
||
# Template 展開:{{secret.X}} 或 {{runtime.X}} 屬於 WASM 職責
|
||
# 例外:auth-recipe-seeds.ts 是 recipe 資料定義(會被序列化寫進 RECIPES KV),
|
||
# 其中的 {{secret.X}} / {{runtime.X}} 是「資料字面值」而非 TS 展開邏輯,
|
||
# 真正的展開仍在 WASM auth primitive 內完成。
|
||
if [[ "$BASE" != "auth-recipe-seeds.ts" ]] && echo "$CONTENT" | grep -qE "\{\{(secret|runtime)\." ; then
|
||
block "2.2" \
|
||
"Template 展開({{secret.X}} / {{runtime.X}})屬於 WASM auth primitive 職責" \
|
||
"把這段邏輯改寫到 registry/components/auth_static_key/main.go(TinyGo)"
|
||
fi
|
||
|
||
# Hard-code 的 BUILTIN_API_RECIPES / BUILTIN_CREDENTIALS_MAP 新增
|
||
if echo "$CONTENT" | grep -qE "(BUILTIN_API_RECIPES|BUILTIN_CREDENTIALS_MAP)\s*[:=]"; then
|
||
# 允許「把它設成空物件」或「刪除」,但不准新增實作
|
||
if echo "$CONTENT" | grep -qE "BUILTIN_API_RECIPES.*=.*\{\s*[a-zA-Z]"; then
|
||
block "2.2" \
|
||
"禁止在 TS 裡新增 BUILTIN_API_RECIPES / BUILTIN_CREDENTIALS_MAP 實作" \
|
||
"API 呼叫邏輯屬於各自的 WASM 零件(gmail.wasm / telegram.wasm 等),cypher-executor 只做 routing"
|
||
fi
|
||
fi
|
||
|
||
# Hard-code API endpoint 實作
|
||
HARDCODED_APIS=(
|
||
"gmail\.googleapis\.com/gmail/v1/users/me/messages/send"
|
||
"api\.telegram\.org/bot.*sendMessage"
|
||
"sheets\.googleapis\.com/v4/spreadsheets"
|
||
"notify-api\.line\.me/api/notify"
|
||
)
|
||
for PATTERN in "${HARDCODED_APIS[@]}"; do
|
||
if echo "$CONTENT" | grep -qE "$PATTERN"; then
|
||
# 允許 wasi-shim.ts 裡的 http_request host function(它只是 proxy)
|
||
if [[ "$BASE" != "wasi-shim.ts" ]]; then
|
||
block "2.2" \
|
||
"禁止在 cypher-executor TS 裡 hard-code API endpoint(偵測到: $PATTERN)" \
|
||
"把 API 呼叫移到對應的 WASM 零件(registry/components/gmail/main.go 等)"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# exchangeGoogleJwt / 類似 token exchange function
|
||
if echo "$CONTENT" | grep -qE "(exchangeGoogleJwt|exchangeServiceAccountJwt|signGoogleJwt)"; then
|
||
if [[ "$BASE" != "wasi-shim.ts" ]]; then
|
||
block "2.2" \
|
||
"Token exchange 邏輯屬於 auth_service_account WASM 零件" \
|
||
"改到 registry/components/auth_service_account/main.go"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# 規則 3.3:禁止建立 *-v2 / new-* / *-worker 類複製貼上目錄
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
if [[ "$FILE_PATH" =~ /(auth|credential|jwt|oauth|gmail|telegram|google-sheets|line-notify|http-request)[-_](v2|v3|new|worker|backup|temp)/ ]]; then
|
||
block "3.3" \
|
||
"禁止為同一零件建立平行目錄(v2/new/worker/backup 等)" \
|
||
"直接修改 registry/components/<name>/main.go 即可;需要版本管理請用 git branch"
|
||
fi
|
||
if [[ "$FILE_PATH" =~ /new-(auth|credential|jwt|oauth|gmail|telegram)/ ]]; then
|
||
block "3.3" \
|
||
"禁止為同一零件建立 new-<name>/ 平行目錄" \
|
||
"直接修改 registry/components/<name>/main.go"
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
# 規則 4.3:禁止自行在 .agents/specs/ 下建新 SDD 目錄
|
||
# ─────────────────────────────────────────────────────────────────────────────
|
||
if [[ "$FILE_PATH" == *".agents/specs/"* ]]; then
|
||
# 檢查是否在已知 SDD 目錄內
|
||
KNOWN_SDDS=(
|
||
".agents/specs/arcrun"
|
||
".agents/specs/u6u-core-mvp"
|
||
".agents/specs/u6u-platform-evolution"
|
||
)
|
||
IN_KNOWN=false
|
||
for K in "${KNOWN_SDDS[@]}"; do
|
||
if [[ "$FILE_PATH" == *"$K/"* ]]; then
|
||
IN_KNOWN=true
|
||
break
|
||
fi
|
||
done
|
||
if [[ "$IN_KNOWN" == "false" ]]; then
|
||
block "4.3" \
|
||
"禁止自行在 .agents/specs/ 下建立新的頂層 SDD 目錄" \
|
||
"先與 richblack 確認 SDD 範圍。若是現有 SDD 的補充檔案,請放到已知 SDD 目錄下"
|
||
fi
|
||
fi
|
||
|
||
exit 0
|