|
| 1 | +# Tool Script Safety Guard |
| 2 | + |
| 3 | +This example shows how to scan Python and Bash tool scripts before execution. It uses a policy file, emits structured reports, appends JSONL audit events, and can be attached as a Tool Filter or CodeExecutor wrapper. |
| 4 | + |
| 5 | +## Run The Samples |
| 6 | + |
| 7 | +```bash |
| 8 | +python scripts/tool_safety_check.py examples/tool_safety_guard/samples/04_network_external.py \ |
| 9 | + --policy examples/tool_safety_guard/tool_safety_policy.yaml \ |
| 10 | + --report-out examples/tool_safety_guard/out/report.json \ |
| 11 | + --audit-out examples/tool_safety_guard/out/audit.jsonl |
| 12 | +``` |
| 13 | + |
| 14 | +To scan every sample: |
| 15 | + |
| 16 | +```bash |
| 17 | +for f in examples/tool_safety_guard/samples/*; do |
| 18 | + lang=python |
| 19 | + case "$f" in *.sh) lang=bash ;; esac |
| 20 | + python scripts/tool_safety_check.py "$f" --language "$lang" \ |
| 21 | + --policy examples/tool_safety_guard/tool_safety_policy.yaml || true |
| 22 | +done |
| 23 | +``` |
| 24 | + |
| 25 | +## Policy |
| 26 | + |
| 27 | +`tool_safety_policy.yaml` controls: |
| 28 | + |
| 29 | +- `allowed_domains`: domains that network rules may allow without code changes. |
| 30 | +- `allowed_commands`: commands permitted in command argument scans. |
| 31 | +- `denied_paths`: sensitive paths such as `.env`, `~/.ssh`, cloud credentials, and private keys. |
| 32 | +- `system_write_paths`: protected filesystem roots. |
| 33 | +- `max_timeout_seconds`, `max_output_bytes`, `max_sleep_seconds`, `max_loop_iterations`, and write limits. |
| 34 | +- `deny_risk_level`, `review_risk_level`, and `block_on_review`. |
| 35 | + |
| 36 | +Changing the YAML is enough to alter domain allowlists, denied paths, and allowed commands. |
| 37 | + |
| 38 | +## Decisions And Reports |
| 39 | + |
| 40 | +The scanner returns: |
| 41 | + |
| 42 | +- `allow`: no configured rule found a material risk. |
| 43 | +- `needs_human_review`: medium risk or uncertain behavior, for example dynamic network targets. |
| 44 | +- `deny`: high or critical risk such as credential reads, recursive deletion, non-allowlisted network egress, dependency installation, or secret leakage. |
| 45 | + |
| 46 | +Every finding includes `risk_type`, `rule_id`, `evidence`, `recommendation`, line information when available, and whether evidence was redacted. Reports also include OpenTelemetry-ready attributes such as `tool.safety.decision`, `tool.safety.risk_level`, and `tool.safety.rule_id`. |
| 47 | + |
| 48 | +## Filter Integration |
| 49 | + |
| 50 | +Attach the registered Tool Filter to script-like tools: |
| 51 | + |
| 52 | +```python |
| 53 | +from trpc_agent_sdk.tools import FunctionTool |
| 54 | +from trpc_agent_sdk.tools.safety import ToolSafetyFilter # registers "tool_safety_guard" |
| 55 | + |
| 56 | +tool = FunctionTool(run_script, filters_name=["tool_safety_guard"]) |
| 57 | +``` |
| 58 | + |
| 59 | +The filter scans common argument shapes: `command`, `script`, `code`, `source`, and `code_blocks`. For `deny`, and for `needs_human_review` when `block_on_review=true`, it returns a structured `TOOL_SAFETY_GUARD_BLOCKED` response before the tool body runs. |
| 60 | + |
| 61 | +## CodeExecutor Wrapper |
| 62 | + |
| 63 | +```python |
| 64 | +from trpc_agent_sdk.code_executors import UnsafeLocalCodeExecutor |
| 65 | +from trpc_agent_sdk.tools.safety import SafetyGuardedCodeExecutor |
| 66 | +from trpc_agent_sdk.tools.safety import ToolSafetyGuard |
| 67 | +from trpc_agent_sdk.tools.safety import ToolSafetyPolicy |
| 68 | + |
| 69 | +policy = ToolSafetyPolicy.load("examples/tool_safety_guard/tool_safety_policy.yaml") |
| 70 | +executor = SafetyGuardedCodeExecutor( |
| 71 | + delegate=UnsafeLocalCodeExecutor(timeout=10), |
| 72 | + guard=ToolSafetyGuard(policy=policy), |
| 73 | +) |
| 74 | +``` |
| 75 | + |
| 76 | +## Relationship To Sandbox, Filter, Telemetry, And CodeExecutor |
| 77 | + |
| 78 | +This guard is a pre-execution control. It is designed to run in a Filter or wrapper before a Tool, Skill, MCP Tool, or CodeExecutor starts real execution. It complements sandbox isolation by catching obvious high-risk scripts early and by creating audit records. It also emits fields that can be copied into OpenTelemetry spans. |
| 79 | + |
| 80 | +It cannot replace a sandbox. Static scanning has false positives, false negatives, and bypass risks: obfuscated code, generated strings, encoded payloads, indirect imports, shell expansions, downloaded second-stage scripts, and interpreter-specific behavior can evade static rules. Production deployments should still use least-privilege credentials, network egress controls, filesystem mounts, process and memory limits, timeouts, container or remote sandbox isolation, and post-execution auditing. |
| 81 | + |
| 82 | +## Extending Rules |
| 83 | + |
| 84 | +Add new rules in `trpc_agent_sdk.tools.safety._scanner`. Rules should return `ToolSafetyFinding` with a stable `rule_id`, clear `risk_type`, bounded evidence, and a recommendation. Prefer AST checks for Python and explicit token checks for Bash, then use regex only as a fallback for patterns that are hard to parse. |
0 commit comments