Skip to content

Commit 88e78cf

Browse files
committed
fix: block_on_review switch, dead code removal, \n in regex
1 parent f88f898 commit 88e78cf

4 files changed

Lines changed: 20 additions & 14 deletions

File tree

trpc_agent_sdk/tools/safety/_bash_scanner.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ class of false positives where a "dangerous" pattern appears inside a string
3030
from typing import Any
3131
from typing import Dict
3232
from typing import List
33-
from typing import Optional
3433
from typing import Set
3534

3635
# ---------------------------------------------------------------------------
@@ -353,10 +352,10 @@ def _scan_lines(self) -> None:
353352
# ------------------------------------------------------------------
354353

355354
def _check_rm(self, line_no: int, raw_line: str, tokens: List[str], args: List[str]) -> None:
356-
"""Check for recursive-delete. Flags both ``rm -rf`` and ``rm -r -f``."""
357-
all_tokens = set(tokens)
358-
# Parse short flags character-by-character (avoids substring false
359-
# positives: "-i" has no "r", "-v" has no "f", etc.).
355+
"""Check for recursive-delete. Flags both ``rm -rf`` and ``rm -r -f``.
356+
357+
Parses short flags character-by-character (avoids substring false
358+
positives: "-i" has no "r", "-v" has no "f", etc.)."""
360359
short_flags: set[str] = set()
361360
has_long_recursive = False
362361
has_long_force = False

trpc_agent_sdk/tools/safety/_rules.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ def __call__(
733733
r"ProcessPoolExecutor\s*\(.*max_workers\s*=\s*(\d+)",
734734
r"concurrent\.futures",
735735
r"multiprocessing\.Pool\s*\(.*processes\s*=\s*(\d+)",
736-
r"&[\s\n]*done",
736+
r"&[\s]*done",
737737
]
738738
for conc_pat in conc_patterns:
739739
for line_no, line_text in _find_lines(script, conc_pat, script_type=scan_input.script_type):

trpc_agent_sdk/tools/safety/_safety_filter.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ class ToolSafetyFilter(BaseFilter):
6767
events are only emitted via the logger.
6868
block_on_deny: If True (default), the filter prevents execution when
6969
the decision is DENY.
70+
block_on_review: If True, also block on NEEDS_HUMAN_REVIEW.
71+
Defaults to False — callers should check
72+
``rsp.safety_report`` to implement a human-
73+
review gate.
7074
"""
7175

7276
def __init__(
@@ -75,12 +79,14 @@ def __init__(
7579
policy: Optional[SafetyPolicy] = None,
7680
audit_log_path: Optional[str] = None,
7781
block_on_deny: bool = True,
82+
block_on_review: bool = False,
7883
) -> None:
7984
super().__init__()
8085
self._policy = policy or get_policy()
8186
self._scanner = SafetyScanner(self._policy)
8287
self._audit = AuditLogger(audit_log_path)
8388
self._block_on_deny = block_on_deny
89+
self._block_on_review = block_on_review
8490

8591
# Identify ourselves within the filter chain
8692
from trpc_agent_sdk.abc import FilterType
@@ -137,9 +143,10 @@ async def _before(self, ctx: AgentContext, req: Any, rsp: FilterResult) -> None:
137143
tool_name,
138144
report.summary,
139145
)
140-
# Still allow by default — the caller should check the report.
141-
# Set a readable attribute on the result so downstream can decide.
142146
setattr(rsp, "safety_report", report)
147+
if self._block_on_review:
148+
rsp.error = ToolSafetyDeniedError(report)
149+
rsp.is_continue = False
143150

144151
else:
145152
logger.debug("ToolSafetyFilter allowed tool '%s'.", tool_name)
@@ -198,7 +205,6 @@ def _collect(val: Any) -> None:
198205
_collect(getattr(req, "script_content"))
199206

200207
return "\n".join(parts) if parts else None
201-
return None
202208

203209

204210
def _guess_script_type(req: Any, script: str) -> ScriptType:

trpc_agent_sdk/tools/safety/_scanner.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ def scan(self, scan_input: SafetyScanInput) -> SafetyScanReport:
164164
decision=Decision.DENY,
165165
risk_level=RiskLevel.CRITICAL if blocklist_hit else RiskLevel.HIGH,
166166
findings=oversized_findings,
167-
summary=f"{oversized_reason}." +
168-
(" Blocklist pattern matched — denied." if blocklist_hit else " Denied for safety."),
167+
summary=(f"{oversized_reason}." +
168+
(" Blocklist pattern matched — denied." if blocklist_hit else " Denied for safety.")),
169169
scan_duration_ms=round(duration_ms, 2),
170170
policy_version=self._policy.content_hash,
171171
sanitized=False,
@@ -397,9 +397,10 @@ def _scan_python_ast(self, script: str, scan_input: SafetyScanInput) -> List[Saf
397397
RiskCategory.PROCESS_AND_SYSTEM,
398398
risk,
399399
f.evidence,
400-
f"AST: {'privilege escalation' if f.extra.get('risk') == 'privilege' else 'process execution'} via {f.canonical_name}",
401-
"Avoid spawning child processes in agent tools." if f.extra.get("risk") != "privilege" else
402-
"Privilege escalation is not allowed in tool scripts.",
400+
("AST: privilege escalation via " if f.extra.get("risk") == "privilege" else
401+
"AST: process execution via ") + f.canonical_name,
402+
("Privilege escalation is not allowed in tool scripts." if f.extra.get("risk")
403+
== "privilege" else "Avoid spawning child processes in agent tools."),
403404
f.line_number,
404405
f.canonical_name,
405406
))

0 commit comments

Comments
 (0)