mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-29 09:21:58 +00:00
feat: wire BoundaryGuard into ToolExecutor for external tool scanning
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
677ece6fb3
commit
d2dbd4c2a2
@@ -43,6 +43,7 @@ class JarvisSystem:
|
||||
session_store: Optional[Any] = None # SessionStore
|
||||
capability_policy: Optional[Any] = None # CapabilityPolicy
|
||||
audit_logger: Optional[Any] = None # AuditLogger
|
||||
boundary_guard: Optional[Any] = None # BoundaryGuard
|
||||
operator_manager: Optional[Any] = None # OperatorManager
|
||||
agent_manager: Optional[Any] = None # AgentManager
|
||||
agent_scheduler: Optional[Any] = None # AgentScheduler
|
||||
|
||||
@@ -101,6 +101,7 @@ class ToolExecutor:
|
||||
default_timeout: float = 30.0,
|
||||
capability_policy: Optional[Any] = None,
|
||||
agent_id: str = "",
|
||||
boundary_guard: Optional[Any] = None,
|
||||
) -> None:
|
||||
self._tools: Dict[str, BaseTool] = {t.spec.name: t for t in tools}
|
||||
self._bus = bus
|
||||
@@ -109,6 +110,7 @@ class ToolExecutor:
|
||||
self._default_timeout = default_timeout
|
||||
self._capability_policy = capability_policy
|
||||
self._agent_id = agent_id
|
||||
self._boundary_guard = boundary_guard
|
||||
|
||||
def execute(self, tool_call: ToolCall) -> ToolResult:
|
||||
"""Parse arguments, dispatch to tool, measure latency, emit events."""
|
||||
@@ -130,6 +132,22 @@ class ToolExecutor:
|
||||
success=False,
|
||||
)
|
||||
|
||||
# Boundary guard: scan external tool arguments
|
||||
if (
|
||||
self._boundary_guard is not None
|
||||
and not getattr(tool, "is_local", True)
|
||||
):
|
||||
try:
|
||||
tool_call = self._boundary_guard.check_outbound(tool_call)
|
||||
# Re-parse arguments after potential redaction
|
||||
params = json.loads(tool_call.arguments) if tool_call.arguments else {}
|
||||
except Exception as exc:
|
||||
return ToolResult(
|
||||
tool_name=tool_call.name,
|
||||
content=f"Security block: {exc}",
|
||||
success=False,
|
||||
)
|
||||
|
||||
# RBAC capability check
|
||||
if self._capability_policy and tool.spec.required_capabilities:
|
||||
for cap in tool.spec.required_capabilities:
|
||||
|
||||
@@ -160,3 +160,68 @@ class TestToolTagging:
|
||||
from openjarvis.tools.calculator import CalculatorTool
|
||||
|
||||
assert CalculatorTool.is_local is True
|
||||
|
||||
|
||||
class TestToolExecutorBoundaryIntegration:
|
||||
"""ToolExecutor should use BoundaryGuard for external tool calls."""
|
||||
|
||||
def _make_executor(self, boundary_guard=None):
|
||||
from openjarvis.tools._stubs import BaseTool, ToolExecutor, ToolSpec
|
||||
|
||||
class FakeExternalTool(BaseTool):
|
||||
tool_id = "fake_external"
|
||||
is_local = False
|
||||
|
||||
@property
|
||||
def spec(self):
|
||||
return ToolSpec(
|
||||
name="fake_external",
|
||||
description="test",
|
||||
parameters={
|
||||
"type": "object",
|
||||
"properties": {"q": {"type": "string"}},
|
||||
},
|
||||
)
|
||||
|
||||
def execute(self, **params):
|
||||
from openjarvis.core.types import ToolResult
|
||||
|
||||
return ToolResult(
|
||||
tool_name="fake_external",
|
||||
content=f"result for {params.get('q', '')}",
|
||||
success=True,
|
||||
)
|
||||
|
||||
return ToolExecutor(
|
||||
tools=[FakeExternalTool()],
|
||||
boundary_guard=boundary_guard,
|
||||
)
|
||||
|
||||
def test_external_tool_args_scanned(self) -> None:
|
||||
from openjarvis.core.types import ToolCall
|
||||
from openjarvis.security.boundary import BoundaryGuard
|
||||
|
||||
guard = BoundaryGuard(mode="redact")
|
||||
executor = self._make_executor(boundary_guard=guard)
|
||||
|
||||
tc = ToolCall(
|
||||
id="t1",
|
||||
name="fake_external",
|
||||
arguments=(
|
||||
'{"q": "my key is sk-proj-abc123def456ghi789jkl012mno345pqr678stu"}'
|
||||
),
|
||||
)
|
||||
result = executor.execute(tc)
|
||||
assert "sk-proj-" not in result.content
|
||||
|
||||
def test_no_guard_passes_through(self) -> None:
|
||||
from openjarvis.core.types import ToolCall
|
||||
|
||||
executor = self._make_executor(boundary_guard=None)
|
||||
tc = ToolCall(
|
||||
id="t2",
|
||||
name="fake_external",
|
||||
arguments='{"q": "sk-proj-abc123def456ghi789jkl012mno345pqr678stu"}',
|
||||
)
|
||||
result = executor.execute(tc)
|
||||
assert "sk-proj-" in result.content
|
||||
|
||||
Reference in New Issue
Block a user