Skip to content

Commit fa88540

Browse files
Altruistusclaude
andcommitted
CM-73403: Simplify
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 2c901f0 commit fa88540

5 files changed

Lines changed: 40 additions & 40 deletions

File tree

‎cycode/cli/apps/ai_guardrails/ides/cursor.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ def _resolve_mcp_server_name(raw_payload: dict) -> Optional[str]:
8686
if not url and not command:
8787
return None
8888

89+
lowered_command = command.lower() if command else None
8990
for config_path in _cursor_mcp_config_paths(raw_payload.get('workspace_roots')):
9091
servers = (_load_cursor_mcp_config(config_path) or {}).get('mcpServers')
9192
if not isinstance(servers, dict):
@@ -95,7 +96,7 @@ def _resolve_mcp_server_name(raw_payload: dict) -> Optional[str]:
9596
continue
9697
if url and server.get('url') == url:
9798
return name
98-
if command and (command == _server_command_line(server) or command.lower() == str(name).lower()):
99+
if command and (command == _server_command_line(server) or lowered_command == str(name).lower()):
99100
return name
100101

101102
return command

‎cycode/cli/apps/ai_guardrails/scan/guardrail_config.py‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,20 @@ def __post_init__(self) -> None:
7070
if guardrail.get('key') in _KNOWN_GUARDRAIL_KEYS
7171
}
7272

73+
@property
74+
def ttl_seconds(self) -> float:
75+
return self.payload.get('ttl_seconds') or DEFAULT_TTL_SECONDS
76+
77+
def _agents(self, guardrail_key: str) -> dict:
78+
return (self._guardrails.get(guardrail_key) or {}).get('agents') or {}
79+
7380
def mode_for(self, guardrail_key: str, ide_name: Optional[str]) -> str:
7481
"""The platform keys the cells by our --ide names, so the lookup is direct."""
75-
agents = (self._guardrails.get(guardrail_key) or {}).get('agents') or {}
82+
agents = self._agents(guardrail_key)
7683
return str(agents.get((ide_name or '').lower(), default_mode_for(guardrail_key))).lower()
7784

7885
def is_off_for_every_agent(self, guardrail_key: str) -> bool:
79-
agents = (self._guardrails.get(guardrail_key) or {}).get('agents') or {}
80-
return all(str(mode).lower() == GuardrailCellMode.OFF for mode in agents.values())
86+
return all(str(mode).lower() == GuardrailCellMode.OFF for mode in self._agents(guardrail_key).values())
8187

8288
def _modes_for_event(self, event_name: str, ide_name: Optional[str]) -> list:
8389
return [
@@ -101,8 +107,7 @@ def sensitive_globs(self) -> list:
101107
return globs if isinstance(globs, list) and globs else _default_sensitive_globs()
102108

103109
def is_expired(self) -> bool:
104-
ttl = self.payload.get('ttl_seconds') or DEFAULT_TTL_SECONDS
105-
return time.time() - self.fetched_at > ttl
110+
return time.time() - self.fetched_at > self.ttl_seconds
106111

107112
def needs_refresh(self, tenant_id: Optional[str]) -> bool:
108113
"""Expired, or fetched for another tenant (the user switched tenants since)."""

‎cycode/cli/apps/ai_guardrails/scan/mcp_server_status.py‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,11 @@ def match(self, alias: str) -> Optional[McpServerMatch]:
9797
return exact
9898

9999
wanted = _normalize_alias(alias)
100-
normalized = [known for key, known in self._by_alias.items() if _normalize_alias(key) == wanted]
100+
candidates = [(_normalize_alias(key), known) for key, known in self._by_alias.items()]
101+
normalized = [known for key, known in candidates if key == wanted]
101102
if not normalized and wanted.startswith(_PLUGIN_ALIAS_PREFIX):
102103
# The plugin name may itself contain '_', so the server is the longest known suffix.
103-
suffixes = [
104-
(len(_normalize_alias(key)), known)
105-
for key, known in self._by_alias.items()
106-
if wanted.endswith(f'_{_normalize_alias(key)}')
107-
]
104+
suffixes = [(len(key), known) for key, known in candidates if wanted.endswith(f'_{key}')]
108105
longest = max((length for length, _ in suffixes), default=None)
109106
normalized = [known for length, known in suffixes if length == longest]
110107

‎cycode/cli/apps/ai_guardrails/session_start_command.py‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
get_ide,
1717
)
1818
from cycode.cli.apps.ai_guardrails.scan.guardrail_config import (
19-
DEFAULT_TTL_SECONDS,
19+
GuardrailConfig,
2020
load_guardrail_config,
2121
save_guardrail_config,
2222
)
@@ -181,13 +181,13 @@ def session_start_command(
181181
context_reported = _report_session_context(ai_client, session_payload.ide_user_email, auth_info.tenant_id)
182182

183183
# SessionStart precedes the first prompt hook in every IDE, so scans normally find a cache.
184-
_sync_guardrail_config(ai_client, auth_info.tenant_id)
184+
config = _sync_guardrail_config(ai_client, auth_info.tenant_id)
185185

186186
# After the report, so the statuses cover the MCP configs the platform just ingested.
187-
_sync_mcp_server_statuses(ai_client, auth_info.tenant_id, force=context_reported)
187+
_sync_mcp_server_statuses(ai_client, config, auth_info.tenant_id, force=context_reported)
188188

189189

190-
def _sync_guardrail_config(ai_client: 'AISecurityManagerClient', tenant_id: Optional[str]) -> None:
190+
def _sync_guardrail_config(ai_client: 'AISecurityManagerClient', tenant_id: Optional[str]) -> Optional[GuardrailConfig]:
191191
"""Refresh the guardrail config cache when it is expired or belongs to another tenant.
192192
193193
Every step here swallows its own failures - a broken cache or an unreachable platform
@@ -196,17 +196,21 @@ def _sync_guardrail_config(ai_client: 'AISecurityManagerClient', tenant_id: Opti
196196
cached = load_guardrail_config()
197197
if cached is not None and not cached.needs_refresh(tenant_id):
198198
logger.debug('Guardrail config cache is fresh, skipping fetch')
199-
return
199+
return cached
200200

201201
resolved = ai_client.get_resolved_guardrails()
202-
if resolved:
203-
save_guardrail_config(resolved, tenant_id)
204-
logger.debug('Guardrail config cache updated')
202+
if not resolved:
203+
return cached
204+
205+
save_guardrail_config(resolved, tenant_id)
206+
logger.debug('Guardrail config cache updated')
207+
return GuardrailConfig(payload=resolved, fetched_at=time.time(), tenant_id=tenant_id)
205208

206209

207-
def _sync_mcp_server_statuses(ai_client: 'AISecurityManagerClient', tenant_id: Optional[str], force: bool) -> None:
210+
def _sync_mcp_server_statuses(
211+
ai_client: 'AISecurityManagerClient', config: Optional[GuardrailConfig], tenant_id: Optional[str], force: bool
212+
) -> None:
208213
"""``force``: a just-reported session context may have brought servers the cache doesn't cover."""
209-
config = load_guardrail_config()
210214
if config is None or config.is_off_for_every_agent(BlockReason.UNAUTHORIZED_MCP_SERVER):
211215
logger.debug('Unauthorized MCP server guardrail is off, skipping MCP server statuses fetch')
212216
return
@@ -221,7 +225,7 @@ def _sync_mcp_server_statuses(ai_client: 'AISecurityManagerClient', tenant_id: O
221225
save_mcp_server_statuses(
222226
response.servers,
223227
tenant_id,
224-
config.payload.get('ttl_seconds') or DEFAULT_TTL_SECONDS,
228+
config.ttl_seconds,
225229
response.should_treat_unreviewed_as_unauthorized,
226230
)
227231
logger.debug('MCP server statuses cache updated')

‎tests/cli/commands/ai_guardrails/test_session_start_command.py‎

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import time
55
from io import StringIO
66
from pathlib import Path
7+
from typing import Optional
78
from unittest.mock import ANY, MagicMock, patch
89

910
import pytest
@@ -743,26 +744,22 @@ def _guardrail_config_with_mcp_server(agents: dict) -> GuardrailConfig:
743744
],
744745
)
745746
def test_mcp_server_statuses_not_fetched_while_the_guardrail_is_off(
746-
config: object, monkeypatch: pytest.MonkeyPatch, mock_save_mcp_server_statuses: MagicMock
747+
config: Optional[GuardrailConfig], mock_save_mcp_server_statuses: MagicMock
747748
) -> None:
748-
monkeypatch.setattr(_session_start_mod, 'load_guardrail_config', MagicMock(return_value=config))
749749
ai_client = MagicMock()
750750

751-
_session_start_mod._sync_mcp_server_statuses(ai_client, 'tenant-a', force=True)
751+
_session_start_mod._sync_mcp_server_statuses(ai_client, config, 'tenant-a', force=True)
752752

753753
ai_client.get_mcp_server_statuses.assert_not_called()
754754
mock_save_mcp_server_statuses.assert_not_called()
755755

756756

757-
def test_mcp_server_statuses_fetched_when_on_for_some_agent(
758-
monkeypatch: pytest.MonkeyPatch, mock_save_mcp_server_statuses: MagicMock
759-
) -> None:
757+
def test_mcp_server_statuses_fetched_when_on_for_some_agent(mock_save_mcp_server_statuses: MagicMock) -> None:
760758
config = _guardrail_config_with_mcp_server({'cursor': 'Off', 'claude-code': 'Report'})
761-
monkeypatch.setattr(_session_start_mod, 'load_guardrail_config', MagicMock(return_value=config))
762759
ai_client = MagicMock()
763760
ai_client.get_mcp_server_statuses.return_value = _STATUSES_RESPONSE
764761

765-
_session_start_mod._sync_mcp_server_statuses(ai_client, 'tenant-a', force=False)
762+
_session_start_mod._sync_mcp_server_statuses(ai_client, config, 'tenant-a', force=False)
766763

767764
# Cached with the guardrail config's TTL and the policy's strict-mode flag.
768765
mock_save_mcp_server_statuses.assert_called_once_with(_SERVERS, 'tenant-a', 600, True)
@@ -773,32 +770,28 @@ def test_fresh_mcp_server_statuses_skip_the_fetch_unless_forced(
773770
) -> None:
774771
config = _guardrail_config_with_mcp_server({'cursor': 'Block'})
775772
cached = McpServerStatuses(servers=[], fetched_at=time.time(), tenant_id='tenant-a')
776-
monkeypatch.setattr(_session_start_mod, 'load_guardrail_config', MagicMock(return_value=config))
777773
monkeypatch.setattr(_session_start_mod, 'load_mcp_server_statuses', MagicMock(return_value=cached))
778774
ai_client = MagicMock()
779775
ai_client.get_mcp_server_statuses.return_value = _STATUSES_RESPONSE
780776

781-
_session_start_mod._sync_mcp_server_statuses(ai_client, 'tenant-a', force=False)
777+
_session_start_mod._sync_mcp_server_statuses(ai_client, config, 'tenant-a', force=False)
782778
ai_client.get_mcp_server_statuses.assert_not_called()
783779

784780
# Another tenant's cache is refetched.
785-
_session_start_mod._sync_mcp_server_statuses(ai_client, 'tenant-b', force=False)
781+
_session_start_mod._sync_mcp_server_statuses(ai_client, config, 'tenant-b', force=False)
786782
assert ai_client.get_mcp_server_statuses.call_count == 1
787783

788784
# A just-reported session context forces a refetch.
789-
_session_start_mod._sync_mcp_server_statuses(ai_client, 'tenant-a', force=True)
785+
_session_start_mod._sync_mcp_server_statuses(ai_client, config, 'tenant-a', force=True)
790786
assert ai_client.get_mcp_server_statuses.call_count == 2
791787

792788

793-
def test_failed_mcp_server_statuses_fetch_keeps_the_cache(
794-
monkeypatch: pytest.MonkeyPatch, mock_save_mcp_server_statuses: MagicMock
795-
) -> None:
789+
def test_failed_mcp_server_statuses_fetch_keeps_the_cache(mock_save_mcp_server_statuses: MagicMock) -> None:
796790
config = _guardrail_config_with_mcp_server({'cursor': 'Block'})
797-
monkeypatch.setattr(_session_start_mod, 'load_guardrail_config', MagicMock(return_value=config))
798791
ai_client = MagicMock()
799792
ai_client.get_mcp_server_statuses.return_value = None
800793

801-
_session_start_mod._sync_mcp_server_statuses(ai_client, 'tenant-a', force=True)
794+
_session_start_mod._sync_mcp_server_statuses(ai_client, config, 'tenant-a', force=True)
802795

803796
mock_save_mcp_server_statuses.assert_not_called()
804797

0 commit comments

Comments
 (0)