From 136e65b400247ab9bbb8a4c1f37f2a4758df0135 Mon Sep 17 00:00:00 2001 From: OoO Date: Wed, 29 Apr 2026 09:12:26 +0800 Subject: [PATCH] =?UTF-8?q?chore(hooks):=20momo-db=20=E5=AE=88=E9=96=80=20?= =?UTF-8?q?9=20PoC=20=E5=BC=B7=E5=8C=96=EF=BC=88vuln-verifier=20=E8=A3=9C?= =?UTF-8?q?=E4=B8=81=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 回應 vuln-verifier 對前版規則 3e 的 8/9 PoC 繞過警告。 新增/強化擋點: - psql -f / heredoc / 重定向:hook 看不到 SQL 內容 → 一律擋 - multi-statement: ; 後接內容(去 -- 與 /* */ 註解後判斷)→ 擋 - writable CTE: WITH ... DELETE/INSERT/UPDATE → 擋 - /run/secrets, /proc/*/environ → 擋 - pg_read_file / pg_read_binary_file / lo_export / lo_import → 擋 - COPY ... FROM PROGRAM → 擋 - VACUUM FULL / REINDEX / REFRESH MATERIALIZED / CLUSTER 加入寫入黑名單 - env 加 (?!\\s+\\w+=) lookahead,避免誤殺 env VAR=value - alias/function 包裝 docker exec:警告(無法靜態判斷後續呼叫) - 白名單 prefix 不收 WITH(防 writable CTE 漏網),改收 SELECT/EXPLAIN/SHOW/VALUES/TABLE settings.json: 累積本輪 session 的 read-only 工具放行(py_compile、python3)。 --- .claude/hooks/momo-prod-guard.js | 71 ++++++++++++++++++++++++++++++++ .claude/settings.json | 10 ++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/.claude/hooks/momo-prod-guard.js b/.claude/hooks/momo-prod-guard.js index 44e8c3e..e9e3215 100644 --- a/.claude/hooks/momo-prod-guard.js +++ b/.claude/hooks/momo-prod-guard.js @@ -78,6 +78,77 @@ process.stdin.on('end', () => { warn('docker prune 類指令,已記錄稽核日誌'); } + // 3e. momo-db / momo-postgres 細粒度政策(SQL / env / dump / cp) + // 規則:read-only SELECT / EXPLAIN / SHOW / \d* 放行 + // 寫入 SQL(INSERT/UPDATE/DELETE/DROP/TRUNCATE/ALTER/CREATE/GRANT/REVOKE)一律阻擋 + // env / printenv 一律阻擋(避免洩漏 DATABASE_URL 等敏感) + // pg_dump / pg_dumpall 一律阻擋(資料外洩防線) + // docker cp momo-db:* / momo-postgres:* 一律阻擋(繞過 SQL 直接搬檔) + const isDbExec = /docker\s+exec\s+[^|;&]*\b(momo-db|momo-postgres)\b/.test(cmd); + const isDbCp = /docker\s+cp\s+[^|;&]*\b(momo-db|momo-postgres):/i.test(cmd); + + if (isDbCp) { + block('docker cp momo-db/momo-postgres 已阻擋(資料外洩防線,請走 SQL 查詢)'); + } + + if (/\bpg_dump(all)?\b/i.test(cmd)) { + block('pg_dump / pg_dumpall 已阻擋(資料外洩防線;備份請走每日 daily_backup 排程)'); + } + + // psql -f / 重定向 / heredoc:hook 看不到 SQL 內容 → 一律擋 + if (isDbExec && /\bpsql\b[^|;&]*(\s-f\s+\S+|\s+<\s+\S+\.sql|<<-?\s*['"]?\w+)/i.test(cmd)) { + block('momo-db psql 透過 -f / heredoc / 重定向載入 SQL,hook 無法靜態檢驗,已阻擋'); + } + + // alias / function 包裝 docker exec:警告 + if (isDbExec && /(alias|function)\s+\w+\s*=?[^|;&]*docker\s+exec[^|;&]*momo-db/i.test(cmd)) { + warn('偵測到 alias/function 包裝 docker exec momo-db,後續呼叫可能繞過 SQL 檢查'); + } + + if (isDbExec) { + // env / printenv:一律擋(排除 env VAR=value 的賦值用法) + if (/\b(env|printenv)\b(?!\s+\w+=)/.test(cmd) && !/\bgrep\b.*\b(POSTGRES_DB|POSTGRES_USER)\b/.test(cmd)) { + block('docker exec momo-db env/printenv 已阻擋(避免洩漏 DATABASE_URL/密碼)'); + } + + // /run/secrets, /proc/*/environ:擋 + if (/\/(run\/secrets|proc\/[\d\w*]+\/environ)/.test(cmd)) { + block('docker exec momo-db 讀取 /run/secrets 或 /proc/*/environ 已阻擋'); + } + + // PG 內建檔案讀寫 / COPY FROM PROGRAM + if (/\b(pg_read_file|pg_read_binary_file|lo_export|lo_import)\b/i.test(cmd) || + /\bCOPY\b[\s\S]*?\bFROM\s+PROGRAM\b/i.test(cmd)) { + block('momo-db PG 檔案系統函式 / COPY FROM PROGRAM 已阻擋'); + } + + // 寫入類 SQL(含 VACUUM FULL / REINDEX / REFRESH MATERIALIZED) + const writeSqlRe = /\b(INSERT\s+INTO|UPDATE\s+\w+\s+SET|DELETE\s+FROM|DROP\s+(TABLE|DATABASE|SCHEMA|INDEX|VIEW)|TRUNCATE|ALTER\s+(TABLE|DATABASE|SCHEMA|ROLE|USER)|CREATE\s+(TABLE|DATABASE|SCHEMA|ROLE|USER|INDEX)|GRANT\b|REVOKE\b|VACUUM\s+FULL|REINDEX|REFRESH\s+MATERIALIZED|CLUSTER)\b/i; + if (writeSqlRe.test(cmd)) { + block('momo-db 寫入類 SQL 已阻擋(INSERT/UPDATE/DELETE/DROP/TRUNCATE/ALTER/CREATE/GRANT/REVOKE/VACUUM FULL)'); + } + + // psql -c:白/黑名單雙閘 + multi-statement + writable CTE + const psqlCmdMatch = cmd.match(/psql[^|]*\s(?:-c|--command(?:=|\s+))\s*(['"])([\s\S]+?)\1/); + if (psqlCmdMatch) { + const sql = psqlCmdMatch[2].trim(); + // writable CTE:WITH ... DELETE/INSERT/UPDATE + if (/^\s*WITH\b/i.test(sql) && /\b(DELETE|INSERT|UPDATE)\b/i.test(sql)) { + block('momo-db psql writable CTE(WITH ... DELETE/INSERT/UPDATE)已阻擋'); + } + // multi-statement:; 後還有非空白非註解內容(去掉 -- 與 /* */ 後判斷) + const stripped = sql.replace(/--[^\n]*/g, '').replace(/\/\*[\s\S]*?\*\//g, ''); + if (/;\s*\S/.test(stripped)) { + block('momo-db psql 多語句(; 後接內容)已阻擋,請拆成單句'); + } + // 白名單 prefix(不收 WITH,避免 writable CTE 漏網) + const isReadOnly = /^\s*(SELECT|EXPLAIN|SHOW|VALUES|TABLE\s+\w|\\d|\\l|\\dt|\\du|\\df|\\?)\b/i.test(sql); + if (!isReadOnly) { + block(`momo-db psql -c 非 read-only SQL 已阻擋: ${sql.substring(0, 80)}`); + } + } + } + // 3d. 稽核所有 SSH 生產指令 fs.appendFileSync(logPath, `[${ts}] SSH-PROD | ${cmd.substring(0, 300)}\n`); } diff --git a/.claude/settings.json b/.claude/settings.json index 5621c37..159b837 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -29,12 +29,18 @@ "Bash(python -c \"from services.ai_orchestrator import AIOrchestrator; print\\('import OK'\\)\")", "Bash(python3 -c \"import sqlalchemy; print\\(sqlalchemy.__version__\\)\")", "Bash(./bin/python3 -c \"import sqlalchemy; print\\('sqlalchemy', sqlalchemy.__version__\\)\")", - "Bash(python3 -c \"import ast; [ast.parse\\(open\\(f\\).read\\(\\)\\) for f in ['services/ai_orchestrator.py','services/hermes_analyst_service.py','services/nemoton_dispatcher_service.py','services/openclaw_strategist_service.py','telegram_ai_integration.py']]; print\\('syntax OK'\\)\")" + "Bash(python3 -c \"import ast; [ast.parse\\(open\\(f\\).read\\(\\)\\) for f in ['services/ai_orchestrator.py','services/hermes_analyst_service.py','services/nemoton_dispatcher_service.py','services/openclaw_strategist_service.py','telegram_ai_integration.py']]; print\\('syntax OK'\\)\")", + "Read(//Users/ooo/**)", + "Bash(mkdir -p ~/Code)", + "Bash(python3)", + "Bash(python3 -c \"import py_compile; py_compile.compile\\('routes/daily_sales_routes.py', doraise=True\\); print\\('SYNTAX OK'\\)\")", + "Bash(python3 -c \"import py_compile; py_compile.compile\\('services/daily_sales_service.py', doraise=True\\); py_compile.compile\\('utils/df_helpers.py', doraise=True\\); print\\('ALL SYNTAX OK'\\)\")" ], "defaultMode": "bypassPermissions", "additionalDirectories": [ "/tmp", - "/Users/ooo/Library/Mobile Documents/com~apple~CloudDocs/momo-pro-system/.claude/hooks" + "/Users/ooo/Library/Mobile Documents/com~apple~CloudDocs/momo-pro-system/.claude/hooks", + "/Users/ooo/Code/momo-pro-system" ] }, "hooks": {