Claude Code 实战 (23):Bash 安全水晶包开发
导读: 这是 Claude Code 源码解析系列的第 23 篇,实战篇第 3 篇。基于 Claude Code BashTool 安全设计,实战开发 Bash 安全水晶包。
📋 目录
Bash 安全水晶包概述
基于 Claude Code BashTool 设计
实战:开发 Bash 安全水晶包
Bash 安全最佳实践
总结
Bash 安全水晶包概述 核心功能 Bash 安全水晶包 = Claude Code BashTool 安全机制 OpenClaw 化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Bash 安全水晶包提供: ├── 危险命令检测 │ ├── 模式匹配 (9 种危险模式) │ ├── 语义分析 (tree-sitter) │ └── 路径验证 (允许目录检查) ├── 权限分级 │ ├── 规则匹配 (allow/deny/ask) │ ├── AI 分类器 (智能判断) │ └── 用户确认 (审批流程) ├── 审计日志 │ ├── 命令记录 │ ├── 决策记录 │ └── 统计报告 └── Sandbox 支持 ├── Docker 隔离 ├── 资源限制 └── 网络隔离
使用场景
场景
风险
水晶包作用
删除操作
rm -rf /
危险命令拦截
远程执行
curl
bash
权限修改
chmod 777
风险警告
系统文件
/etc/passwd
路径验证
正常操作
ls, grep
直接允许
多层安全验证架构:
1 2 命令 → 模式匹配 → 语义分析 → 路径验证 → 权限决策 → 执行 (1ms) (50ms) (5ms) (可变)
危险命令模式 (第 1 篇):
1 2 3 4 5 6 7 8 const dangerousPatterns = [ { pattern : /rm\s+(-[rf]+\s+)*\// , severity : 'critical' }, { pattern : /dd\s+.*\/dev\/(zero|null)/ , severity : 'critical' }, { pattern : /mkfs\./ , severity : 'critical' }, { pattern : /chmod\s+(-[Rr]+\s+)*777/ , severity : 'high' }, { pattern : /curl\s+.*\|\s*(ba)?sh/ , severity : 'high' }, { pattern : /eval\s+.*\$\(.*\)/ , severity : 'medium' }, ];
权限决策引擎 (第 9 篇):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 async function decidePermission (command, context ): Promise <PermissionDecision > { const rule = matchRule (command); if (rule) return { decision : rule.action }; const classifier = await runClassifier (command); if (classifier.confidence > 0.85 ) { return { decision : classifier.safe ? 'allow' : 'deny' }; } return { decision : 'ask' }; }
水晶包设计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Bash 安全水晶包架构: ┌─────────────────────────────────────────────────────────────┐ │ 用户命令 │ │ "rm -rf node_modules" │ └─────────────────────┬───────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 安全检查层 │ │ ├── 危险模式检测 │ │ ├── 路径验证 │ │ └── 权限检查 │ └─────────────┬───────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 决策引擎层 │ │ ├── 规则匹配 (白名单/黑名单) │ │ ├── 风险评估 (低/中/高/严重) │ │ └── 决策生成 (allow/deny/ask) │ └─────────────┬───────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 执行层 │ │ ├── 直接执行 (allow) │ │ ├── 用户确认后执行 (ask) │ │ └── 拒绝执行 (deny) │ └─────────────┬───────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 审计层 │ │ ├── 命令日志 │ │ ├── 决策日志 │ │ └── 统计报告 │ └─────────────────────────────────────────────────────────────┘
实战:开发 Bash 安全水晶包 步骤 1:创建水晶包目录 1 2 3 mkdir -p ~/.openclaw/crystals/bash-security/{scripts,config,tests,docs}cd ~/.openclaw/crystals/bash-security
步骤 2:编写 CRYSTAL.md 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 --- name: bash-security version: 1.0.0 description: Bash 安全水晶包 (基于 Claude Code BashTool) author: John license: MIT # 能力声明 capabilities: - name: safe_bash description: 安全执行 Bash 命令 based_ on: Claude Code BashTool features: - 危险命令检测 - 路径验证 - 权限分级 - 审计日志 - name: bash_audit description: Bash 审计日志 based_on: Claude Code Audit Logger features: - 命令记录 - 决策记录 - 统计报告 # 依赖 dependencies: - name: tree-sitter-bash version: optional # 语义分析用 # 配置 config: # 危险模式 dangerous_patterns: - pattern: "rm\\s+(-[rf]+\\s+)*/" severity: critical - pattern: "dd\\s+.*" severity: critical - pattern: "mkfs\\." severity: critical - pattern: "curl\\s+[^|]*\\|\\s*(ba)?sh" severity: high # 允许目录 allowed_dirs: - ~/.openclaw/workspace - /tmp # 禁止目录 denied_dirs: - /etc - /root - /boot # 权限规则 permission_rules: - pattern: "ls\\s+.*" action: allow - pattern: "git\\s+(status|log|diff).*" action: allow - pattern: "npm\\s+install.*" action: allow conditions: agent: devops # 权限 permissions: - bash - file_read - file_ write--- # Bash 安全水晶包 基于 Claude Code BashTool 安全设计的 OpenClaw 水晶包。 ## 使用方式 ### 安全执行命令 ```bash openclaw run bash-security exec \ --command "ls -la"
查看审计日志 1 openclaw run bash-security audit --limit 20
查看统计报告 1 openclaw run bash-security report
安全特性
危险命令检测 - 9 种危险模式拦截
路径验证 - 只允许访问指定目录
权限分级 - allow/deny/ask 三级决策
审计日志 - 完整记录所有命令
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 ### 步骤 3:实现核心脚本 **文件:** `scripts/safe-bash.sh` ```bash #!/bin/bash # Bash 安全执行脚本 (基于 Claude Code BashTool) set -e COMMAND="" WORKDIR="" TIMEOUT=300 SKIP_SAFETY=false # 加载配置 CONFIG_DIR="$HOME/.openclaw/crystals/bash-security/config" DANGEROUS_PATTERNS_FILE="$CONFIG_DIR/dangerous-patterns.txt" ALLOWED_DIRS_FILE="$CONFIG_DIR/allowed-dirs.txt" AUDIT_LOG="$HOME/.openclaw/logs/bash-security.log" while [[ $# -gt 0 ]]; do case $1 in --command) COMMAND="$2" shift 2 ;; --workdir) WORKDIR="$2" shift 2 ;; --timeout) TIMEOUT="$2" shift 2 ;; --skip-safety) SKIP_SAFETY=true shift ;; *) echo "Unknown option: $1" exit 1 ;; esac done # 日志记录 log_audit() { local command="$1" local decision="$2" local reason="$3" local timestamp=$(date -Iseconds) mkdir -p "$(dirname "$AUDIT_LOG")" echo "$timestamp | $decision | $reason | $command" >> "$AUDIT_LOG" } # 危险命令检测 check_dangerous_patterns() { local command="$1" # 读取危险模式 while IFS='|' read -r pattern severity; do if [[ -z "$pattern" ]] || [[ "$pattern" == \#* ]]; then continue fi if echo "$command" | grep -qiE "$pattern"; then echo "CRITICAL:$severity:匹配危险模式:$pattern" return 1 fi done < "$DANGEROUS_PATTERNS_FILE" return 0 } # 路径验证 validate_paths() { local command="$1" # 提取命令中的路径 local paths=$(echo "$command" | grep -oE '(/[\w./-]+)|(\.\.?/[\w./-]+)' || true) if [[ -z "$paths" ]]; then return 0 fi # 检查每个路径 for path in $paths; do local resolved=$(realpath -m "$path" 2>/dev/null || echo "$path") # 检查是否在禁止目录 while IFS= read -r denied_dir; do if [[ -z "$denied_dir" ]] || [[ "$denied_dir" == \#* ]]; then continue fi if [[ "$resolved" == "$denied_dir"* ]]; then echo "CRITICAL:访问禁止目录:$denied_dir" return 1 fi done < <(cat "$CONFIG_DIR/denied-dirs.txt" 2>/dev/null) # 检查是否在允许目录 local allowed=false while IFS= read -r allowed_dir; do if [[ -z "$allowed_dir" ]] || [[ "$allowed_dir" == \#* ]]; then continue fi if [[ "$resolved" == "$allowed_dir"* ]]; then allowed=true break fi done < "$ALLOWED_DIRS_FILE" if [[ "$allowed" == false ]]; then echo "WARNING:路径不在允许目录:$resolved" return 2 # 警告,不阻止 fi done return 0 } # 权限规则匹配 match_permission_rules() { local command="$1" # 读取权限规则 while IFS='|' read -r pattern action conditions; do if [[ -z "$pattern" ]] || [[ "$pattern" == \#* ]]; then continue fi if echo "$command" | grep -qiE "$pattern"; then echo "$action:$conditions" return 0 fi done < "$CONFIG_DIR/permission-rules.txt" echo "ask:" # 默认需要确认 return 0 } # 用户确认 request_confirmation() { local command="$1" local reason="$2" echo "" echo "⚠️ 安全确认" echo "─────────────────────────────────────" echo "命令:$command" echo "原因:$reason" echo "" echo "[y] 允许 [n] 拒绝 [a] 允许并记住" read -p "请选择: " -n 1 -r echo "" case $REPLY in y|Y) return 0 ;; a|A) # 添加到白名单 echo "$command|allow|" >> "$CONFIG_DIR/permission-rules.txt" return 0 ;; *) return 1 ;; esac } # 执行命令 execute_command() { local command="$1" local workdir="$2" local timeout="$3" # 设置工作目录 if [[ -n "$workdir" ]]; then cd "$workdir" || exit 1 fi # 执行命令 echo "执行命令:$command" timeout "$timeout" bash -c "$command" local exit_code=$? echo "" echo "命令完成,退出码:$exit_code" return $exit_code } # 主函数 main() { if [[ -z "$COMMAND" ]]; then echo "Error: --command is required" exit 1 fi # 跳过安全检查 if [[ "$SKIP_SAFETY" == true ]]; then log_audit "$COMMAND" "allow" "跳过安全检查" execute_command "$COMMAND" "$WORKDIR" "$TIMEOUT" exit $? fi # 1. 危险命令检测 echo "进行安全检查..." local danger_result=$(check_dangerous_patterns "$COMMAND" || echo "") if [[ -n "$danger_result" ]]; then local severity=$(echo "$danger_result" | cut -d: -f1) local reason=$(echo "$danger_result" | cut -d: -f3-) log_audit "$COMMAND" "deny" "$reason" if [[ "$severity" == "CRITICAL" ]]; then echo "❌ 危险命令被阻止:" echo " $reason" echo "" echo "命令:$COMMAND" exit 1 fi fi # 2. 路径验证 local path_result=$(validate_paths "$COMMAND" || echo "") if [[ -n "$path_result" ]]; then local severity=$(echo "$path_result" | cut -d: -f1) local reason=$(echo "$path_result" | cut -d: -f2-) if [[ "$severity" == "CRITICAL" ]]; then log_audit "$COMMAND" "deny" "$reason" echo "❌ 路径验证失败:" echo " $reason" exit 1 elif [[ "$severity" == "WARNING" ]]; then echo "⚠️ 警告:$reason" fi fi # 3. 权限规则匹配 local permission_result=$(match_permission_rules "$COMMAND") local action=$(echo "$permission_result" | cut -d: -f1) case "$action" in allow) log_audit "$COMMAND" "allow" "规则匹配允许" execute_command "$COMMAND" "$WORKDIR" "$TIMEOUT" exit $? ;; deny) log_audit "$COMMAND" "deny" "规则匹配拒绝" echo "❌ 命令被拒绝 (规则匹配)" echo "命令:$COMMAND" exit 1 ;; ask) if request_confirmation "$COMMAND" "需要用户确认"; then log_audit "$COMMAND" "allow" "用户确认" execute_command "$COMMAND" "$WORKDIR" "$TIMEOUT" exit $? else log_audit "$COMMAND" "deny" "用户拒绝" echo "命令已取消" exit 1 fi ;; esac } main "$@"
文件: scripts/bash-audit.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 #!/bin/bash set -eLIMIT=50 FORMAT="table" FILTER="" while [[ $# -gt 0 ]]; do case $1 in --limit ) LIMIT="$2 " shift 2 ;; --format) FORMAT="$2 " shift 2 ;; --filter) FILTER="$2 " shift 2 ;; *) echo "Unknown option: $1 " exit 1 ;; esac done AUDIT_LOG="$HOME /.openclaw/logs/bash-security.log" show_audit () { if [[ ! -f "$AUDIT_LOG " ]]; then echo "没有审计日志" return 0 fi local logs=$(cat "$AUDIT_LOG " ) if [[ -n "$FILTER " ]]; then logs=$(echo "$logs " | grep -i "$FILTER " ) fi case "$FORMAT " in "table" ) printf "%-25s %-8s %-30s %s\n" "时间" "决策" "原因" "命令" echo "--------------------------------------------------------------------------------" echo "$logs " | tail -n "$LIMIT " | while IFS='|' read -r timestamp decision reason command ; do printf "%-25s %-8s %-30s %s\n" \ "$(echo $timestamp | cut -d+ -f1) " \ "$decision " \ "$(echo $reason | cut -c1-30) " \ "$(echo $command | cut -c1-50) " done ;; "json" ) echo "$logs " | tail -n "$LIMIT " | while IFS='|' read -r timestamp decision reason command ; do echo "{\"timestamp\":\"$timestamp \",\"decision\":\"$decision \",\"reason\":\"$reason \",\"command\":\"$command \"}" done | jq -s '.' ;; "simple" ) echo "$logs " | tail -n "$LIMIT " ;; esac } show_stats () { if [[ ! -f "$AUDIT_LOG " ]]; then return 0 fi local total=$(wc -l < "$AUDIT_LOG " ) local allow=$(grep -c "|allow|" "$AUDIT_LOG " || echo 0) local deny=$(grep -c "|deny|" "$AUDIT_LOG " || echo 0) local ask=$(grep -c "|ask|" "$AUDIT_LOG " || echo 0) echo "" echo "统计:" echo " 总命令数:$total " echo " 允许:$allow " echo " 拒绝:$deny " echo " 需要确认:$ask " if [[ $total -gt 0 ]]; then local allow_rate=$((allow * 100 / total)) echo " 允许率:$allow_rate %" fi } main () { show_audit show_stats } main "$@ "
步骤 4:配置文件 文件: config/dangerous-patterns.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 rm \s+(-[rf]+\s+)*\/|criticaldd \s+.*|criticalmkfs\.|critical :\(\)\{\s*:\|:\s*&\s*\}\s*;|critical chmod \s+(-[Rr]+\s+)*777|highchown \s+.*root|highsudo \s+.*passwd|highcurl\s+.*\|\s*(ba)?sh|high wget\s+.*\|\s*(ba)?sh|high eval \s+.*\$\(.*\)|medium`\w+`|medium
文件: config/allowed-dirs.txt
1 2 3 4 ~/.openclaw/workspace /tmp /var/tmp
文件: config/denied-dirs.txt
1 2 3 4 5 6 /etc /root /boot /sys /proc
文件: config/permission-rules.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ls \s+.*|allow|cat \s+.*|allow|grep\s+.*|allow| find\s+.*-name.*|allow| git\s+(status|log |diff|show).*|allow| npm\s+install.*|allow|agent=devops npm\s+test .*|allow| git\s+push.*|ask| docker\s+rm \s+.*|ask| rm \s+-rf\s+node_modules|ask|rm \s+(-[rf]+\s+)*\/|deny|dd \s+.*|deny|
步骤 5:编写测试用例 文件: tests/test.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 #!/bin/bash set -eSCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]} " ) " && pwd) " TEST_PASSED=0 TEST_FAILED=0 test_dangerous_command () { echo "Test: Dangerous command detection..." local output=$(bash "$SCRIPT_DIR /../scripts/safe-bash.sh" \ --command "rm -rf /" 2>&1 || true ) if echo "$output " | grep -q "危险命令被阻止" ; then echo "✅ PASS" ((TEST_PASSED++)) else echo "❌ FAIL" ((TEST_FAILED++)) fi } test_safe_command () { echo "Test: Safe command execution..." local output=$(bash "$SCRIPT_DIR /../scripts/safe-bash.sh" \ --command "echo Hello World" 2>&1) if echo "$output " | grep -q "Hello World" ; then echo "✅ PASS" ((TEST_PASSED++)) else echo "❌ FAIL" ((TEST_FAILED++)) fi } test_path_validation () { echo "Test: Path validation..." local output=$(bash "$SCRIPT_DIR /../scripts/safe-bash.sh" \ --command "cat /etc/passwd" 2>&1 || true ) if echo "$output " | grep -q "禁止目录\|WARNING" ; then echo "✅ PASS" ((TEST_PASSED++)) else echo "❌ FAIL" ((TEST_FAILED++)) fi } test_audit_log () { echo "Test: Audit logging..." bash "$SCRIPT_DIR /../scripts/safe-bash.sh" \ --command "echo Test" > /dev/null 2>&1 || true local log_file="$HOME /.openclaw/logs/bash-security.log" if [[ -f "$log_file " ]] && grep -q "echo Test" "$log_file " ; then echo "✅ PASS" ((TEST_PASSED++)) else echo "❌ FAIL" ((TEST_FAILED++)) fi } run_tests () { echo "==================================" echo "Bash 安全水晶包测试" echo "==================================" echo "" test_dangerous_command test_safe_command test_path_validation test_audit_log echo "" echo "==================================" echo "测试结果:$TEST_PASSED 通过,$TEST_FAILED 失败" echo "==================================" if [[ $TEST_FAILED -gt 0 ]]; then exit 1 fi } run_tests
Bash 安全最佳实践 实践 1:配置危险模式 1 2 3 4 5 vim ~/.openclaw/crystals/bash-security/config/dangerous-patterns.txt my-custom-dangerous-command|critical
实践 2:配置允许目录 1 2 3 echo "/home/johnzok/projects" >> \ ~/.openclaw/crystals/bash-security/config/allowed-dirs.txt
实践 3:查看审计日志 1 2 3 4 5 6 7 8 openclaw run bash-security audit --limit 50 openclaw run bash-security audit --filter "deny" openclaw run bash-security audit --format json --output audit.json
实践 4:生成统计报告 1 2 3 4 5 6 7 8 9 10 11 12 13 openclaw run bash-security report Bash 安全统计报告: ───────────────────────────────────── 总命令数:150 允许:120 (80%) 拒绝:20 (13%) 需要确认:10 (7%) 危险命令拦截:5 次 路径验证失败:3 次
总结 核心要点
基于 Claude Code - BashTool 安全机制 OpenClaw 化
多层防御 - 模式检测 + 路径验证 + 权限分级
审计日志 - 完整记录所有命令
可配置 - 危险模式、允许目录、权限规则
实战成果
成果
说明
bash-security 水晶包
完整的 Bash 安全执行能力
2 个核心命令
safe-bash / bash-audit
配置文件
危险模式、允许目录、权限规则
完整测试
4 个测试用例
危险命令拦截示例 1 2 3 4 5 6 7 $ openclaw run bash-security exec --command "rm -rf /" ❌ 危险命令被阻止: 匹配危险模式:rm \s+(-[rf]+\s+)*\/ 命令:rm -rf /
下一步
系列文章:
[1-20] Claude Code 源码解析 (已完成)
[21] 水晶包开发完整指南 (已完成)
[22] 任务管理水晶包开发 (已完成)
[23] Bash 安全水晶包开发 (本文)
[24+] 更多实战案例 (继续中…)
关于作者: John,OpenClaw 平台开发者,专注 AI 助手架构设计与实现。