Skip to content

Commit ab99a2d

Browse files
committed
feat: strengthen tool safety guard validation
1 parent 343a75c commit ab99a2d

24 files changed

Lines changed: 1735 additions & 272 deletions

examples/tool_safety/README.md

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,12 @@ tRPC-Agent 的 Tool、MCP Tool、Skill 和 CodeExecutor 能让 Agent 执行脚
6161
| --- | --- | --- |
6262
| 安全检查器代码 | 已完成 | `trpc_agent_sdk/tools/safety/` |
6363
| CLI 工具 | 已完成 | `scripts/tool_safety_check.py` |
64+
| Manifest 验收工具 | 已完成 | `scripts/tool_safety_manifest_report.py` |
6465
| 策略示例 | 已完成 | `examples/tool_safety/tool_safety_policy.yaml` |
65-
| 31 条公开样例 | 已完成 | `examples/tool_safety/samples/` |
66+
| 40 条公开样例 | 已完成 | `examples/tool_safety/samples/` |
67+
| 样例 manifest | 已完成 | `examples/tool_safety/samples/manifest.yaml` |
6668
| 报告示例 | 已完成 | `examples/tool_safety/tool_safety_report.json` |
67-
| 31 条样例汇总报告 | 已完成 | `examples/tool_safety/all_reports.json` |
69+
| 40 条样例汇总报告 | 已完成 | `examples/tool_safety/all_reports.json` |
6870
| 审计日志示例 | 已完成 | `examples/tool_safety/tool_safety_audit.jsonl` |
6971
| 自动化测试 | 已完成 | `tests/tools/safety/` |
7072
| 设计说明 | 已完成 | 本文档 |
@@ -109,6 +111,7 @@ Tool / Skill / MCP Tool / CodeExecutor
109111
| `_wrapper.py` | 独立 wrapper,执行前扫描、审计、埋点和拦截 |
110112
| `_filter.py` | tRPC-Agent Filter 接入示例 |
111113
| `scripts/tool_safety_check.py` | 命令行扫描工具 |
114+
| `scripts/tool_safety_manifest_report.py` | Manifest 驱动验收和 deterministic 报告生成工具 |
112115

113116
## 规则体系
114117

@@ -118,14 +121,16 @@ Tool / Skill / MCP Tool / CodeExecutor
118121
| --- | --- |
119122
| `allow` | 当前静态策略未命中风险,允许执行 |
120123
| `deny` | 命中高危或严重风险,执行前拒绝 |
121-
| `needs_human_review` | 命中不确定或中等风险,需要人工复核 |
124+
| `needs_human_review` | 命中不确定或中等风险,需要人工复核,默认记录但不阻断 |
122125

123126
最终决策由命中的 finding 聚合得到:
124127

125128
- 任意 finding 为 `deny`,最终结果为 `deny`
126129
- 没有 `deny`,但存在 `needs_human_review`,最终结果为 `needs_human_review`
127130
- 没有 finding 时,最终结果为 `allow`
128131

132+
扫描决策和执行拦截分开处理:`deny` 默认会在执行前拦截;`needs_human_review` 默认只写入报告、审计和 telemetry,不阻断执行;设置 `block_on_review=True` 后,`needs_human_review` 也会阻断。
133+
129134
### 风险等级
130135

131136
| 风险等级 | 典型含义 |
@@ -326,7 +331,7 @@ if report.blocked:
326331

327332
### Wrapper 接入
328333

329-
`ToolSafetyGuard` 适合不直接修改核心执行链路时使用。它会在真实执行函数之前扫描脚本,写审计日志,设置 OpenTelemetry attributes,并在非 `allow` 时阻止执行
334+
`ToolSafetyGuard` 适合不直接修改核心执行链路时使用。它会在真实执行函数之前扫描脚本,写审计日志,设置 OpenTelemetry attributes,并按 `deny` 或 `block_on_review=True` 策略阻止执行
330335

331336
```python
332337
from trpc_agent_sdk.tools.safety import ToolSafetyGuard
@@ -358,6 +363,15 @@ if result.blocked:
358363
# Return or log the structured report instead of executing the tool.
359364
```
360365

366+
如果希望人工复核决策也阻断执行:
367+
368+
```python
369+
guard = ToolSafetyGuard(
370+
audit_log_path="tool_safety_audit.jsonl",
371+
block_on_review=True,
372+
)
373+
```
374+
361375
如果希望直接抛错,可使用:
362376

363377
```python
@@ -476,8 +490,10 @@ if not result.is_continue:
476490

477491
当前测试覆盖:
478492

479-
- 31 条公开样例,其中包含 issue 指定的 12 类必测场景和额外边界场景。
493+
- 40 条公开样例,其中包含 issue 指定的 12 类必测场景和额外边界场景。
494+
- Manifest 驱动验收,校验 expected decision 和 required rule ids。
480495
- YAML policy 加载和匹配。
496+
- Strict policy validation,拒绝未知字段、错误类型和负数限制。
481497
- 结构化报告字段。
482498
- 500 行脚本扫描性能。
483499
- 命令行参数、工作目录、超时和输出大小检查。
@@ -486,15 +502,30 @@ if not result.is_continue:
486502
- Filter 执行前拦截和审计日志。
487503
- CLI 输出和返回码。
488504

489-
### 扫描 31 个公开样例
505+
### 扫描 40 个公开样例
490506

491-
仓库中已提供一份汇总报告
507+
仓库中已提供一份 deterministic 汇总报告
492508

493509
```text
494510
examples/tool_safety/all_reports.json
495511
```
496512

497-
也可以重新扫描生成:
513+
推荐使用 manifest 验收脚本重新生成:
514+
515+
```bash
516+
.venv/bin/python scripts/tool_safety_manifest_report.py \
517+
--strict-policy \
518+
--output examples/tool_safety/all_reports.json
519+
```
520+
521+
该命令会校验:
522+
523+
- 40/40 expected decision 匹配。
524+
- 40/40 required rule ids 匹配。
525+
- 读取密钥、危险删除、非白名单网络外连三类样例均不会被 allow。
526+
- 安全样例不会被 deny。
527+
528+
也可以使用通用 CLI 扫描目录:
498529

499530
```bash
500531
.venv/bin/python scripts/tool_safety_check.py \
@@ -503,41 +534,19 @@ examples/tool_safety/all_reports.json
503534
--output examples/tool_safety/all_reports.json
504535
```
505536

506-
样例覆盖:
537+
样例期望决策和 required rule ids 由 `examples/tool_safety/samples/manifest.yaml` 维护。
507538

508-
| 样例 | 期望决策 |
509-
| --- | --- |
510-
| `aiohttp_non_whitelist.py` | `deny` |
511-
| `apt_install.sh` | `deny` |
512-
| `background_process.sh` | `needs_human_review` |
513-
| `bash_pipe.sh` | `deny` |
514-
| `command_substitution.sh` | `needs_human_review` |
515-
| `credential_file_key.py` | `deny` |
516-
| `danger_delete.sh` | `deny` |
517-
| `dependency_install.sh` | `deny` |
518-
| `fork_bomb.sh` | `deny` |
519-
| `human_review.py` | `needs_human_review` |
520-
| `infinite_loop.py` | `needs_human_review` |
521-
| `long_sleep.sh` | `needs_human_review` |
522-
| `network_non_whitelist.py` | `deny` |
523-
| `network_whitelist.py` | `allow` |
524-
| `npm_install.sh` | `deny` |
525-
| `os_system.py` | `needs_human_review` |
526-
| `pip_module_install.py` | `deny` |
527-
| `private_key_literal.py` | `deny` |
528-
| `privilege_escalation.sh` | `deny` |
529-
| `read_env.py` | `deny` |
530-
| `read_secret.py` | `deny` |
531-
| `safe_bash.sh` | `allow` |
532-
| `safe_file_read.py` | `allow` |
533-
| `safe_python.py` | `allow` |
534-
| `sensitive_output.py` | `deny` |
535-
| `shell_injection.py` | `needs_human_review` |
536-
| `socket_access.py` | `needs_human_review` |
537-
| `subprocess_call.py` | `needs_human_review` |
538-
| `subprocess_danger_delete.py` | `deny` |
539-
| `system_overwrite.sh` | `deny` |
540-
| `unknown_network_dynamic.py` | `needs_human_review` |
539+
新增高价值绕过样例包括:
540+
541+
- `base64 | sh`
542+
- `python -c`
543+
- `bash -c` / `sh -c`
544+
- 动态 URL
545+
- 动态 `.env` / `~/.ssh` 路径
546+
- `curl --data-binary @.env`
547+
- `find -delete`
548+
- `xargs rm -rf`
549+
- `os.getenv("API_TOKEN")` 外传
541550

542551
### 性能验证
543552

@@ -658,7 +667,8 @@ trpc_agent_sdk/tools/safety/
658667
└── _wrapper.py
659668
660669
scripts/
661-
└── tool_safety_check.py
670+
├── tool_safety_check.py
671+
└── tool_safety_manifest_report.py
662672
663673
examples/tool_safety/
664674
├── README.md
@@ -670,22 +680,31 @@ examples/tool_safety/
670680
├── aiohttp_non_whitelist.py
671681
├── apt_install.sh
672682
├── background_process.sh
683+
├── base64_exec_review.sh
684+
├── bash_inline_command.sh
673685
├── bash_pipe.sh
674686
├── command_substitution.sh
675687
├── credential_file_key.py
688+
├── curl_env_upload.sh
676689
├── danger_delete.sh
677690
├── dependency_install.sh
691+
├── dynamic_secret_path.py
692+
├── dynamic_url_join.py
693+
├── find_delete_review.sh
678694
├── fork_bomb.sh
679695
├── human_review.py
680696
├── infinite_loop.py
681697
├── long_sleep.sh
698+
├── manifest.yaml
682699
├── network_non_whitelist.py
683700
├── network_whitelist.py
684701
├── npm_install.sh
702+
├── os_getenv_token_exfiltration.py
685703
├── os_system.py
686704
├── pip_module_install.py
687705
├── private_key_literal.py
688706
├── privilege_escalation.sh
707+
├── python_inline_command.sh
689708
├── read_env.py
690709
├── read_secret.py
691710
├── safe_bash.sh
@@ -697,7 +716,8 @@ examples/tool_safety/
697716
├── subprocess_call.py
698717
├── subprocess_danger_delete.py
699718
├── system_overwrite.sh
700-
└── unknown_network_dynamic.py
719+
├── unknown_network_dynamic.py
720+
└── xargs_rm_review.sh
701721
702722
tests/tools/safety/
703723
├── test_audit.py

0 commit comments

Comments
 (0)