chore(hooks): momo-db 守門 9 PoC 強化(vuln-verifier 補丁)
All checks were successful
CD Pipeline / deploy (push) Successful in 1m8s

回應 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)。
This commit is contained in:
OoO
2026-04-29 09:12:26 +08:00
parent 3971fd4020
commit 136e65b400
2 changed files with 79 additions and 2 deletions

View File

@@ -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* 放行
// 寫入 SQLINSERT/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 / 重定向 / heredochook 看不到 SQL 內容 → 一律擋
if (isDbExec && /\bpsql\b[^|;&]*(\s-f\s+\S+|\s+<\s+\S+\.sql|<<-?\s*['"]?\w+)/i.test(cmd)) {
block('momo-db psql 透過 -f / heredoc / 重定向載入 SQLhook 無法靜態檢驗,已阻擋');
}
// 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 CTEWITH ... DELETE/INSERT/UPDATE
if (/^\s*WITH\b/i.test(sql) && /\b(DELETE|INSERT|UPDATE)\b/i.test(sql)) {
block('momo-db psql writable CTEWITH ... 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`);
}

View File

@@ -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": {