@sec2ai26/claw-tool-sentinel
v0.2.0
Published
OpenClaw before_tool_call security plugin — audit logging, regex blacklist blocking, and async reporting
Readme
Claw Tool Sentinel
OpenClaw before_tool_call 插件,提供三层安全能力:
- 本地审计 — 每次工具调用追加 JSON Lines 记录到系统临时目录
- 黑名单拦截 — 对
toolName及所有字符串参数(递归提取,深度 ≤ 5)进行正则匹配,命中即阻断 - 异步上报 — 将每次调用(放行或阻断)POST 到配置的企业安全中心,携带
Authorization: Bearer <apiKey>和内网 IP
配置
通过 OpenClaw 标准插件配置提供:
| 字段 | 必填 | 说明 |
|------|------|------|
| reportUrl | 是 | 接收 ToolCallReport JSON 的 HTTPS 端点 |
| apiKey | 是 | Bearer Token |
| rulesPath | 否 | 自定义黑名单 JSON 文件的绝对路径(不填则使用内置规则) |
| debug | 否 | 设为 true 时上报失败会输出 console.warn |
配置校验失败或规则文件无法加载时,插件以 fail-open 模式运行(不注册 handler,所有调用正常通过)。
架构
┌─────────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway │
│ │
│ tool call ──► register() ──► before_tool_call handler │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ writeLog() matcher.match() reporter.enqueue()│
│ (JSONL 文件) (正则黑名单) (异步 POST) │
│ │ │
│ 命中? ──是──► { block: true } │
│ │ │
│ 否 ──► 放行 │
└─────────────────────────────────────────────────────────────────┘模块
| 模块 | 职责 |
|------|------|
| src/config.ts | 校验 pluginConfig(reportUrl、apiKey、rulesPath、debug),无效时抛出 ConfigError |
| src/default-rules.ts | 内置黑名单规则(TypeScript 常量),构建产物自包含,无需运行时文件路径解析 |
| src/matcher.ts | 规则引擎;loadRules() 从 JSON 文件加载自定义规则,createMatcher() 编译正则,匹配 toolName + 递归提取的字符串参数(深度 ≤ 5,防循环引用) |
| src/reporter.ts | 有界异步队列(上限 1000,溢出丢弃最旧),并发上限 4,超时 5s,字符串截断 512 字符。POST ToolCallReport 到 reportUrl |
| src/ip-utils.ts | 获取主机内网 IP(排除 VPN 和 loopback 接口),用于上报关联 |
| src/logger.ts | 将 ToolCallLogRecord 以 JSON Lines 格式追加到 $TMPDIR/claw-tool-sentinel-before-tool-call.json |
| src/index.ts | 插件入口。编排 config → rules(内置或自定义)→ matcher → reporter → handler 注册。初始化失败时 fail-open |
| src/types.ts | 共享 TypeScript 接口和类型定义 |
Fail-Open 行为
配置校验失败或规则文件无法加载时,插件仅输出警告日志,不注册任何 handler。所有工具调用正常通过,不会被阻断。
阻断条件
唯一的阻断场景:matcher.match() 命中黑名单规则。其他错误(审计写入失败、上报网络失败)均不会导致阻断。
内置规则
默认黑名单规则内嵌于 src/default-rules.ts,构建后随 dist/ 一起分发,无需依赖外部 JSON 文件。覆盖类别:
| 类别 | 严重级别 | 匹配示例 |
|------|----------|----------|
| remote_code_execution | critical | curl ... \| sh、wget ... \| bash |
| dynamic_execution | critical | eval(、exec(、os.system( |
| destructive | critical | rm -rf、DROP TABLE、TRUNCATE TABLE |
| permission_escalation | high | chmod 777、sudo ... |
| config_modification | high | .bashrc、crontab、.git/hooks/、sudoers |
| deserialization | high | pickle.load、yaml.unsafe_load、unserialize( |
| data_exfiltration | critical | curl -d @file、wget --post-file、nc - |
所有模式使用大小写不敏感匹配(i flag)。
自定义规则:创建 JSON 文件(格式参考 rules/dangerous-commands.json),在配置中指定 rulesPath 即可覆盖内置规则。
Reporter 限制参数
| 参数 | 值 | 说明 |
|------|------|------|
| maxQueueSize | 1000 | 待发送队列上限,溢出时丢弃最旧 |
| maxConcurrency | 4 | 最大并行 HTTP 请求数 |
| defaultTimeoutMs | 5000 | 单次请求超时(ms) |
| stringTruncateAt | 512 | 参数中字符串值截断长度 |
本地开发
npm install
npm test
npm run build
npm run smoke运行单个测试文件:
npx tsx --test tests/matcher.test.ts手动验证
npm run build- 在 OpenClaw 配置中填入你控制的
reportUrl(或本地 echo server)和apiKey - 重启 Gateway
- 触发一次安全的工具调用 → 预期
reportUrl收到 POST,matchResult.matched=false - 触发
exec,参数包含"rm -rf /tmp/anything"→ 预期调用被阻断,且 POST 中matchResult.matched=true - 查看本地审计文件路径:
node -e "console.log(require('node:path').join(require('node:os').tmpdir(),'claw-tool-sentinel-before-tool-call.json'))"参考资料
docs/superpowers/specs/2026-04-23-claw-tool-sentinel-design.mddocs/superpowers/plans/2026-05-12-claw-tool-sentinel-full-runtime.mdreferences/下的参考插件(clawbands、clawguard、openclaw-security-assistant)
