feat(claude): 新增 awoooi-guard.js 守衛 hook

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-04-22 00:24:18 +08:00
parent 8f15c57019
commit 3dbb3d70b4

View File

@@ -0,0 +1,129 @@
// AWOOOI 專案守衛 hook — PreToolUse
// 阻擋生產環境高危操作,整合 pre-commit-check.sh 邏輯
let d = '';
process.stdin.on('data', c => d += c);
process.stdin.on('end', () => {
try {
const i = JSON.parse(d);
const tool = i.tool_name || '';
const cmd = String(i.tool_input?.command || '');
const filepath = String(i.tool_input?.file_path || '');
// ── Bash 指令守衛 ──────────────────────────────────────────
if (tool === 'Bash') {
// git commit / git push 的 -m 或 heredoc 內容可能含任何關鍵字,跳過所有規則
if (/git\s+commit|git\s+push/.test(cmd)) { process.stdout.write(d); return; }
// 只在行首(或 && ; | 後)的真實命令才觸發,避免 commit message 誤觸
const lines = cmd.split(/\n|&&|\|\||;/).map(s => s.trim()).filter(Boolean);
// [HARD BLOCK] K8s 生產命名空間刪除
if (lines.some(l => /^kubectl.*delete.*namespace.*awoooi-prod/.test(l))) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止刪除生產命名空間 awoooi-prod'
}));
return;
}
// [HARD BLOCK] K8s 生產環境強制刪除 PVC / Secret
if (lines.some(l => /^kubectl.*delete.*(pvc|secret).*-n.*awoooi-prod/.test(l) ||
/^kubectl.*-n.*awoooi-prod.*delete.*(pvc|secret)/.test(l))) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止在 awoooi-prod 刪除 PVC 或 Secret — 需人工確認'
}));
return;
}
// [HARD BLOCK] docker compose down -v摧毀 volume
if (lines.some(l => /^docker[\s-]?compose.*down.*(-v\b|--volumes)/.test(l))) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止 docker compose down -v — 會刪除資料庫 volume'
}));
return;
}
// [HARD BLOCK] docker system prune清除所有容器/映像)
if (lines.some(l => /^docker system prune/.test(l) && /-f|--force/.test(l))) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止 docker system prune -f — 會清除 Gitea 等共用容器'
}));
return;
}
// [HARD BLOCK] Telegram bot logout先停後換原則—— 只攔截實際 API 呼叫
if (/api\.telegram\.org\/bot[^/]+\/(logOut|getUpdates|deleteWebhook)/.test(cmd)) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止 Telegram logOut / getUpdates — 見 feedback_telegram_token_disaster.md'
}));
return;
}
// [HARD BLOCK] 直接 DROP TABLE / DROP DATABASE非測試環境
if (lines.some(l => /^psql.*-c.*DROP\s+(TABLE|DATABASE|SCHEMA)/i.test(l)) &&
!/test|dev|sqlite|memory/i.test(cmd)) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止直接 DROP TABLE/DATABASE — 需先確認非生產環境'
}));
return;
}
// [HARD BLOCK] git push --force 到 gitea main在 git push 以外的脈絡才檢查)
if (lines.some(l => /^git push.*(--force|-f).*gitea.*main|^git push.*gitea.*main.*(--force|-f)/.test(l))) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止 force push 到 gitea main'
}));
return;
}
// [WARN] kubectl delete 在生產(非 PVC/Secret允許但警告
if (lines.some(l => /^kubectl.*delete.*-n.*awoooi-prod|^kubectl.*-n.*awoooi-prod.*delete/.test(l) &&
!/(pvc|secret)/.test(l))) {
process.stderr.write('[AWOOOI-GUARD] ⚠️ 警告:在 awoooi-prod 執行 kubectl delete請確認這是預期操作\n');
}
// [HARD BLOCK] 修改 Gitea runnersGitHub Billing 規則)
if (/ubuntu-latest/.test(cmd) && /workflow|\.github/.test(cmd)) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止使用 ubuntu-latest — 必須用 self-hosted runner費用'
}));
return;
}
}
// ── Write/Edit 檔案守衛 ─────────────────────────────────────
if (tool === 'Write' || tool === 'Edit') {
// 保護 K8s namespace 定義不被意外改名
if (/k8s.*prod|kubernetes.*prod|awoooi-prod/.test(filepath) &&
/namespace.*awoooi/.test(String(i.tool_input?.old_string || '') + String(i.tool_input?.new_string || ''))) {
process.stderr.write('[AWOOOI-GUARD] ⚠️ 警告:修改生產 K8s namespace 定義,請確認變更範圍\n');
}
// 保護 CI/CD workflow 不引入 ubuntu-latest
if (/\.github\/workflows/.test(filepath)) {
const content = String(i.tool_input?.content || i.tool_input?.new_string || '');
if (/runs-on:\s*ubuntu-latest/.test(content)) {
process.stdout.write(JSON.stringify({
decision: 'block',
reason: '🔴 [AWOOOI-GUARD] 禁止在 workflow 使用 ubuntu-latest — 必須用 self-hostedGitHub Billing'
}));
return;
}
}
}
} catch (e) {
// parse 失敗時放行,不阻斷正常操作
}
process.stdout.write(d);
});