Skip to content

Commit c91f871

Browse files
Stelquiscnb
authored andcommitted
style(safety): 修复 YAPF 代码格式化问题
1 parent 4043fa5 commit c91f871

12 files changed

Lines changed: 238 additions & 177 deletions

File tree

scripts/tool_safety_check.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,25 @@
2424
def detect_script_type(path: str) -> ScriptType:
2525
"""Detect script type from file extension."""
2626
ext = os.path.splitext(path)[1].lower()
27-
if ext in (".py",):
27+
if ext in (".py", ):
2828
return ScriptType.PYTHON
2929
if ext in (".sh", ".bash", ".zsh", ".ksh"):
3030
return ScriptType.BASH
3131
return ScriptType.UNKNOWN
3232

3333

3434
def main():
35-
parser = argparse.ArgumentParser(
36-
description="Tool Script Safety Check — scan scripts for security risks",
37-
)
35+
parser = argparse.ArgumentParser(description="Tool Script Safety Check — scan scripts for security risks", )
3836
parser.add_argument("path", nargs="?", help="Path to script file")
39-
parser.add_argument("--type", "-t", choices=["auto", "bash", "python"],
40-
default="auto", help="Script type (default: auto-detect)")
41-
parser.add_argument("--stdin", action="store_true",
42-
help="Read script from stdin")
43-
parser.add_argument("--json", action="store_true",
44-
help="Output as JSON")
45-
parser.add_argument("--policy", "-p",
46-
default="tool_safety_policy.yaml",
47-
help="Path to policy file")
48-
parser.add_argument("--version", "-v", action="store_true",
49-
help="Show version")
37+
parser.add_argument("--type",
38+
"-t",
39+
choices=["auto", "bash", "python"],
40+
default="auto",
41+
help="Script type (default: auto-detect)")
42+
parser.add_argument("--stdin", action="store_true", help="Read script from stdin")
43+
parser.add_argument("--json", action="store_true", help="Output as JSON")
44+
parser.add_argument("--policy", "-p", default="tool_safety_policy.yaml", help="Path to policy file")
45+
parser.add_argument("--version", "-v", action="store_true", help="Show version")
5046

5147
args = parser.parse_args()
5248

@@ -126,4 +122,4 @@ def _print_report(report):
126122

127123

128124
if __name__ == "__main__":
129-
main()
125+
main()

tests/tools/safety/test_filter.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
from trpc_agent_sdk.tools.safety._types import RiskLevel
2828
from trpc_agent_sdk.tools.safety._wrapper import SafetyWrapper
2929

30-
3130
# ── SafetyFilter Tests ────────────────────────────────────────────────────
3231

3332

@@ -123,7 +122,10 @@ def test_filter_skips_empty_command(self):
123122
import asyncio
124123
asyncio.run(safety_filter._before(
125124
ctx=None,
126-
req={"name": "test", "not_a_command": "value"},
125+
req={
126+
"name": "test",
127+
"not_a_command": "value"
128+
},
127129
rsp=rsp,
128130
))
129131
assert rsp.is_continue is True
@@ -133,8 +135,7 @@ def test_safety_blocked_error_message(self):
133135
from trpc_agent_sdk.tools.safety._types import RuleMatch
134136
from trpc_agent_sdk.tools.safety._types import RiskCategory
135137

136-
r = RuleMatch("R001", RiskCategory.DANGEROUS_FILE_OPERATION,
137-
RiskLevel.CRITICAL, "rm -rf /", 1, "Remove")
138+
r = RuleMatch("R001", RiskCategory.DANGEROUS_FILE_OPERATION, RiskLevel.CRITICAL, "rm -rf /", 1, "Remove")
138139
report = SafetyReport(SafetyDecision.DENY, RiskLevel.CRITICAL, [r])
139140
err = SafetyBlockedError("Bash", report)
140141
assert "Bash" in str(err)
@@ -266,4 +267,4 @@ def test_scan_only_safe_script(self):
266267
"rules": {},
267268
}))
268269
result = wrapper.scan_only("Bash", "ls -la", "bash")
269-
assert result["decision"] in ("ALLOW", "NEEDS_HUMAN_REVIEW")
270+
assert result["decision"] in ("ALLOW", "NEEDS_HUMAN_REVIEW")

tests/tools/safety/test_policy.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@
1515
from trpc_agent_sdk.tools.safety._types import RiskLevel
1616
from trpc_agent_sdk.tools.safety._types import SafetyDecision
1717

18-
1918
# ── Fixtures ──────────────────────────────────────────────────────────────
2019

2120

2221
@pytest.fixture(scope="module")
2322
def policy() -> SafetyPolicy:
2423
"""Load the default policy file for testing."""
2524
policy_path = os.path.join(
26-
os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
27-
os.path.abspath(__file__))))),
28-
"trpc_agent_sdk", "tools", "safety", "tool_safety_policy.yaml",
25+
os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))),
26+
"trpc_agent_sdk",
27+
"tools",
28+
"safety",
29+
"tool_safety_policy.yaml",
2930
)
3031
return SafetyPolicy.from_file(policy_path)
3132

@@ -123,4 +124,4 @@ def test_load_from_dict(self):
123124

124125
def test_rule_not_found_returns_none(self, policy: SafetyPolicy):
125126
"""get_rule for non-existent rule should return None."""
126-
assert policy.get_rule("non_existent_rule") is None
127+
assert policy.get_rule("non_existent_rule") is None

tests/tools/safety/test_scanner.py

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,18 @@
2626
from trpc_agent_sdk.tools.safety._types import ScanInput
2727
from trpc_agent_sdk.tools.safety._types import ScriptType
2828

29-
3029
# ── Fixtures ──────────────────────────────────────────────────────────────
3130

3231

3332
@pytest.fixture(scope="module")
3433
def policy() -> SafetyPolicy:
3534
"""Load the default policy file."""
3635
policy_path = os.path.join(
37-
os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
38-
os.path.abspath(__file__))))),
39-
"trpc_agent_sdk", "tools", "safety", "tool_safety_policy.yaml",
36+
os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))),
37+
"trpc_agent_sdk",
38+
"tools",
39+
"safety",
40+
"tool_safety_policy.yaml",
4041
)
4142
return SafetyPolicy.from_file(policy_path)
4243

@@ -63,8 +64,7 @@ def test_safetyreport_properties(self):
6364
"""SafetyReport properties should work correctly."""
6465
from trpc_agent_sdk.tools.safety._types import RuleMatch
6566

66-
r = RuleMatch("R001", RiskCategory.DANGEROUS_FILE_OPERATION,
67-
RiskLevel.CRITICAL, "rm -rf /", 1, "Remove")
67+
r = RuleMatch("R001", RiskCategory.DANGEROUS_FILE_OPERATION, RiskLevel.CRITICAL, "rm -rf /", 1, "Remove")
6868
report = SafetyReport(SafetyDecision.DENY, RiskLevel.CRITICAL, [r])
6969
assert report.is_blocked is True
7070
assert report.is_allowed is False
@@ -75,10 +75,11 @@ def test_safetyreport_to_dict(self):
7575
"""SafetyReport.to_dict() should produce correct JSON structure."""
7676
from trpc_agent_sdk.tools.safety._types import RuleMatch
7777

78-
r = RuleMatch("R001", RiskCategory.DANGEROUS_FILE_OPERATION,
79-
RiskLevel.CRITICAL, "rm -rf /", 1, "Remove")
80-
report = SafetyReport(SafetyDecision.DENY, RiskLevel.CRITICAL, [r],
81-
tool_name="Bash", script_type=ScriptType.BASH)
78+
r = RuleMatch("R001", RiskCategory.DANGEROUS_FILE_OPERATION, RiskLevel.CRITICAL, "rm -rf /", 1, "Remove")
79+
report = SafetyReport(SafetyDecision.DENY,
80+
RiskLevel.CRITICAL, [r],
81+
tool_name="Bash",
82+
script_type=ScriptType.BASH)
8283
d = report.to_dict()
8384
assert d["decision"] == "DENY"
8485
assert d["risk_level"] == "CRITICAL"
@@ -92,8 +93,7 @@ def test_auditevent_otel_attributes(self):
9293
"""AuditEvent should produce 6 OTel attributes."""
9394
from trpc_agent_sdk.tools.safety._types import AuditEvent
9495

95-
event = AuditEvent("Bash", "DENY", "CRITICAL", "R001", 1.23,
96-
False, True, "2026-07-20T00:00:00")
96+
event = AuditEvent("Bash", "DENY", "CRITICAL", "R001", 1.23, False, True, "2026-07-20T00:00:00")
9797
otel = event.to_otel_attributes()
9898
assert len(otel) == 6
9999
assert otel["tool.safety.decision"] == "DENY"
@@ -154,7 +154,7 @@ def test_bash_pipe_sensitive(self, scanner: SafetyScanner):
154154
"""cat /etc/passwd | grep root should be detected."""
155155
report = scanner.scan(ScanInput("cat /etc/passwd | grep root", ScriptType.BASH))
156156
# Should detect sensitive file read
157-
sensitive = [m for m in report.matches if m.rule_id in ("R002",)]
157+
sensitive = [m for m in report.matches if m.rule_id in ("R002", )]
158158
assert len(sensitive) > 0 or report.decision == SafetyDecision.DENY
159159

160160
def test_fork_bomb_detected(self, scanner: SafetyScanner):
@@ -283,8 +283,12 @@ def test_log_decision_creates_event(self):
283283
"""AuditLogger.log_decision should create a valid event."""
284284
logger = AuditLogger()
285285
event = logger.log_decision(
286-
tool_name="Bash", decision="DENY", risk_level="CRITICAL",
287-
rule_id="R001", scan_duration_ms=1.23, blocked=True,
286+
tool_name="Bash",
287+
decision="DENY",
288+
risk_level="CRITICAL",
289+
rule_id="R001",
290+
scan_duration_ms=1.23,
291+
blocked=True,
288292
)
289293
assert event.tool_name == "Bash"
290294
assert event.decision == "DENY"
@@ -293,8 +297,7 @@ def test_log_decision_creates_event(self):
293297
def test_audit_event_to_dict(self):
294298
"""AuditEvent.to_dict() should produce correct structure."""
295299
from trpc_agent_sdk.tools.safety._types import AuditEvent
296-
event = AuditEvent("Bash", "DENY", "CRITICAL", "R001", 1.23,
297-
False, True, "2026-07-20T00:00:00")
300+
event = AuditEvent("Bash", "DENY", "CRITICAL", "R001", 1.23, False, True, "2026-07-20T00:00:00")
298301
d = event.to_dict()
299302
assert d["tool_name"] == "Bash"
300303
assert d["decision"] == "DENY"
@@ -304,12 +307,14 @@ def test_audit_event_to_dict(self):
304307
def test_audit_event_otel_attributes(self):
305308
"""AuditEvent should produce OTel-compatible attributes."""
306309
from trpc_agent_sdk.tools.safety._types import AuditEvent
307-
event = AuditEvent("Bash", "DENY", "CRITICAL", "R001", 1.23,
308-
False, True, "2026-07-20T00:00:00")
310+
event = AuditEvent("Bash", "DENY", "CRITICAL", "R001", 1.23, False, True, "2026-07-20T00:00:00")
309311
otel = event.to_otel_attributes()
310312
expected_keys = {
311-
"tool.safety.decision", "tool.safety.risk_level",
312-
"tool.safety.rule_id", "tool.safety.blocked",
313-
"tool.safety.masked", "tool.safety.duration_ms",
313+
"tool.safety.decision",
314+
"tool.safety.risk_level",
315+
"tool.safety.rule_id",
316+
"tool.safety.blocked",
317+
"tool.safety.masked",
318+
"tool.safety.duration_ms",
314319
}
315-
assert set(otel.keys()) == expected_keys
320+
assert set(otel.keys()) == expected_keys

trpc_agent_sdk/tools/safety/_audit.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,22 @@ def log_report(self, report: SafetyReport) -> AuditEvent:
7979
if report.is_blocked:
8080
logger.warning(
8181
"Safety audit: tool=%s decision=%s risk=%s rules=%s duration=%.1fms blocked=%s",
82-
event.tool_name, event.decision, event.risk_level,
83-
event.rule_id, event.scan_duration_ms, event.blocked,
82+
event.tool_name,
83+
event.decision,
84+
event.risk_level,
85+
event.rule_id,
86+
event.scan_duration_ms,
87+
event.blocked,
8488
)
8589
else:
8690
logger.info(
8791
"Safety audit: tool=%s decision=%s risk=%s rules=%s duration=%.1fms blocked=%s",
88-
event.tool_name, event.decision, event.risk_level,
89-
event.rule_id, event.scan_duration_ms, event.blocked,
92+
event.tool_name,
93+
event.decision,
94+
event.risk_level,
95+
event.rule_id,
96+
event.scan_duration_ms,
97+
event.blocked,
9098
)
9199

92100
return event
@@ -140,7 +148,11 @@ def log_decision(
140148

141149
logger.info(
142150
"Safety audit: tool=%s decision=%s risk=%s rules=%s blocked=%s",
143-
tool_name, decision, risk_level, rule_id, blocked,
151+
tool_name,
152+
decision,
153+
risk_level,
154+
rule_id,
155+
blocked,
144156
)
145157

146158
return event
@@ -227,4 +239,4 @@ def _emit_otel_event(event: AuditEvent) -> None:
227239
if span and span.is_recording():
228240
span.set_attributes(event.to_otel_attributes())
229241
except Exception: # pylint: disable=broad-except
230-
pass
242+
pass

trpc_agent_sdk/tools/safety/_bash_scanner.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,8 @@ class BashScanner:
3232
"""
3333

3434
# ── Domain extraction patterns ──
35-
_DOMAIN_RE = re.compile(
36-
r"(?:https?://|ftp://)?([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?"
37-
r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?::\d+)?)"
38-
)
35+
_DOMAIN_RE = re.compile(r"(?:https?://|ftp://)?([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?"
36+
r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?::\d+)?)")
3937

4038
# ── Sensitive data patterns (for leak detection) ──
4139
_SENSITIVE_PATTERNS: list[tuple[str, str]] = [
@@ -109,15 +107,16 @@ def _scan_line(
109107
if re.search(pattern, line, re.IGNORECASE):
110108
# Determine risk category from rule name
111109
category = self._rule_name_to_category(rule_name)
112-
matches.append(RuleMatch(
113-
rule_id=self._rule_name_to_id(rule_name),
114-
risk_category=category,
115-
risk_level=rule_cfg.risk_level,
116-
evidence=line[:200],
117-
line_number=line_no,
118-
recommendation=self._get_recommendation(category),
119-
masked=False,
120-
))
110+
matches.append(
111+
RuleMatch(
112+
rule_id=self._rule_name_to_id(rule_name),
113+
risk_category=category,
114+
risk_level=rule_cfg.risk_level,
115+
evidence=line[:200],
116+
line_number=line_no,
117+
recommendation=self._get_recommendation(category),
118+
masked=False,
119+
))
121120
break # One match per rule per line
122121

123122
# Check domain whitelist for network egress rules
@@ -160,7 +159,7 @@ def _check_network_egress(
160159
evidence=f"Non-whitelisted domain: {domain}",
161160
line_number=0,
162161
recommendation="Remove or replace with a whitelisted domain, "
163-
"or add the domain to allowed_domains in the policy",
162+
"or add the domain to allowed_domains in the policy",
164163
masked=False,
165164
)
166165
return None
@@ -189,7 +188,7 @@ def _check_sensitive_leak(
189188
evidence=f"Potential sensitive data leak: {name}",
190189
line_number=line_no,
191190
recommendation="Avoid writing secrets to files, logs, or network requests. "
192-
"Use environment variables or a secrets manager instead.",
191+
"Use environment variables or a secrets manager instead.",
193192
masked=True,
194193
)
195194
return None
@@ -229,16 +228,13 @@ def _get_recommendation(category: RiskCategory) -> str:
229228
"""Get a default recommendation for a risk category."""
230229
recommendations = {
231230
RiskCategory.DANGEROUS_FILE_OPERATION:
232-
"Remove or replace this file operation. Avoid destructive commands like rm -rf.",
231+
"Remove or replace this file operation. Avoid destructive commands like rm -rf.",
233232
RiskCategory.NETWORK_EGRESS:
234-
"Remove network requests to non-whitelisted domains, or add the domain to the policy.",
235-
RiskCategory.PROCESS_EXECUTION:
236-
"Avoid executing system commands directly. Use safe APIs instead.",
233+
"Remove network requests to non-whitelisted domains, or add the domain to the policy.",
234+
RiskCategory.PROCESS_EXECUTION: "Avoid executing system commands directly. Use safe APIs instead.",
237235
RiskCategory.DEPENDENCY_INSTALLATION:
238-
"Do not install packages during execution. Pre-install all dependencies.",
239-
RiskCategory.RESOURCE_ABUSE:
240-
"Avoid infinite loops, long sleeps, and resource-exhaustive patterns.",
241-
RiskCategory.SENSITIVE_INFO_LEAK:
242-
"Do not write secrets to files, logs, or network requests.",
236+
"Do not install packages during execution. Pre-install all dependencies.",
237+
RiskCategory.RESOURCE_ABUSE: "Avoid infinite loops, long sleeps, and resource-exhaustive patterns.",
238+
RiskCategory.SENSITIVE_INFO_LEAK: "Do not write secrets to files, logs, or network requests.",
243239
}
244-
return recommendations.get(category, "Review this line for potential security risks.")
240+
return recommendations.get(category, "Review this line for potential security risks.")

trpc_agent_sdk/tools/safety/_policy.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
from ._types import RiskLevel
3232
from ._types import SafetyDecision
3333

34-
3534
# ── Policy Data Classes ───────────────────────────────────────────────────
3635

3736

@@ -128,9 +127,7 @@ def from_dict(cls, raw: dict) -> SafetyPolicy:
128127
if global_settings:
129128
policy.max_timeout_seconds = int(global_settings.get("max_timeout_seconds", 300))
130129
policy.max_output_size_bytes = int(global_settings.get("max_output_size_bytes", 10 * 1024 * 1024))
131-
policy.default_decision = _parse_decision(
132-
global_settings.get("default_decision", "needs_human_review")
133-
)
130+
policy.default_decision = _parse_decision(global_settings.get("default_decision", "needs_human_review"))
134131

135132
# ── Whitelists ──
136133
policy.allowed_domains = raw.get("allowed_domains", [])
@@ -280,4 +277,4 @@ def _glob_to_regex(pattern: str) -> str:
280277
else:
281278
regex += c
282279
i += 1
283-
return regex
280+
return regex

0 commit comments

Comments
 (0)