Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai>
Co-authored-by: Vincent G <10739306+VinceOPS@users.noreply.github.com>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Mathias Gesbert 2026-04-01 18:38:22 +02:00 committed by GitHub
parent 54b9a17457
commit 9c1c32e058
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 587 additions and 179 deletions

View file

@ -208,3 +208,66 @@ class TestResolvePermissionWindowsSyntax:
)
assert isinstance(result, PermissionContext)
assert result.permission is ToolPermission.ALWAYS
class TestDenylistWordBoundary:
"""Verify denylist matches whole command names, not prefixes."""
def _make_bash(self, **kwargs) -> Bash:
config = BashToolConfig(**kwargs)
return Bash(config=config, state=BaseToolState())
def test_vi_blocks_vi_exact(self):
bash_tool = self._make_bash(denylist=["vi"])
result = bash_tool.resolve_permission(BashArgs(command="vi"))
assert isinstance(result, PermissionContext)
assert result.permission is ToolPermission.NEVER
def test_vi_blocks_vi_with_args(self):
bash_tool = self._make_bash(denylist=["vi"])
result = bash_tool.resolve_permission(BashArgs(command="vi file.txt"))
assert isinstance(result, PermissionContext)
assert result.permission is ToolPermission.NEVER
def test_vi_does_not_block_vibe(self):
bash_tool = self._make_bash(denylist=["vi"])
result = bash_tool.resolve_permission(BashArgs(command="vibe -p hello"))
assert result is None or result.permission is not ToolPermission.NEVER
def test_multiword_pattern_still_works(self):
bash_tool = self._make_bash(denylist=["bash -i"])
result = bash_tool.resolve_permission(BashArgs(command="bash -i"))
assert isinstance(result, PermissionContext)
assert result.permission is ToolPermission.NEVER
def test_multiword_pattern_with_trailing_args(self):
bash_tool = self._make_bash(denylist=["bash -i"])
result = bash_tool.resolve_permission(BashArgs(command="bash -i extra"))
assert isinstance(result, PermissionContext)
assert result.permission is ToolPermission.NEVER
def test_multiword_pattern_does_not_match_partial(self):
bash_tool = self._make_bash(denylist=["bash -i"])
result = bash_tool.resolve_permission(BashArgs(command="bash -init"))
assert result is None or result.permission is not ToolPermission.NEVER
def test_deny_reason_is_set(self):
bash_tool = self._make_bash(denylist=["vim"])
result = bash_tool.resolve_permission(BashArgs(command="vim file.txt"))
assert isinstance(result, PermissionContext)
assert result.reason is not None
assert "vim" in result.reason
def test_standalone_deny_reason_is_set(self):
bash_tool = self._make_bash(denylist_standalone=["python"])
result = bash_tool.resolve_permission(BashArgs(command="python"))
assert isinstance(result, PermissionContext)
assert result.reason is not None
assert result.permission is ToolPermission.NEVER
assert "python" in result.reason
assert "standalone" in result.reason
def test_allowlist_does_not_match_prefix(self):
bash_tool = self._make_bash(allowlist=["cat"])
result = bash_tool.resolve_permission(BashArgs(command="catalog"))
assert result is not None and result.permission is not ToolPermission.ALWAYS

View file

@ -430,6 +430,21 @@ class TestMCPRegistry:
assert len(registry._cache) == 0
def test_count_loaded_excludes_failed_servers(self):
registry = MCPRegistry()
ok_srv = self._make_http_server("ok", url="http://ok:1")
fail_srv = self._make_http_server("fail", url="http://fail:2")
proxy = create_mcp_http_proxy_tool_class(
url="http://ok:1", remote=RemoteTool(name="t"), alias="ok"
)
registry._cache[registry._server_key(ok_srv)] = {proxy.get_name(): proxy}
assert registry.count_loaded([ok_srv, fail_srv]) == 1
assert registry.count_loaded([ok_srv]) == 1
assert registry.count_loaded([fail_srv]) == 0
assert registry.count_loaded([]) == 0
def test_cache_survives_multiple_get_tools_calls(self):
registry = MCPRegistry()
srv = self._make_http_server("stable")