cycode 3.24.1.dev1__py3-none-any.whl → 3.24.1.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cycode/__init__.py CHANGED
@@ -5,4 +5,4 @@ import time as _time
5
5
  # end-to-end scan duration from the moment the user actually triggered it.
6
6
  _BOOT_WALL: float = _time.time()
7
7
 
8
- __version__ = '3.24.1.dev1' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
8
+ __version__ = '3.24.1.dev2' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
@@ -30,7 +30,7 @@ from cycode.cli.apps.ai_guardrails.scan.types import (
30
30
  AIHookOutcome,
31
31
  BlockReason,
32
32
  )
33
- from cycode.cli.apps.ai_guardrails.scan.utils import is_denied_path, truncate_utf8
33
+ from cycode.cli.apps.ai_guardrails.scan.utils import build_violation_summary, is_denied_path, truncate_utf8
34
34
  from cycode.cli.apps.scan.code_scanner import _get_scan_documents_thread_func
35
35
  from cycode.cli.apps.scan.scan_parameters import get_scan_parameters
36
36
  from cycode.cli.cli_types import ScanTypeOption, SeverityOption
@@ -38,7 +38,6 @@ from cycode.cli.files_collector.file_excluder import is_path_configured_in_exclu
38
38
  from cycode.cli.models import Document
39
39
  from cycode.cli.utils.host_info import get_hostname, get_serial_number
40
40
  from cycode.cli.utils.progress_bar import DummyProgressBar, ScanProgressBarSection
41
- from cycode.cli.utils.scan_utils import build_violation_summary
42
41
  from cycode.logger import get_logger
43
42
 
44
43
  logger = get_logger('AI Guardrails')
@@ -76,7 +75,7 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
76
75
  block_reason = SECRETS_BLOCK_REASON_BY_EVENT_TYPE[AiHookEventType.PROMPT]
77
76
  if effective_mode == GuardrailsMode.BLOCK:
78
77
  outcome = AIHookOutcome.BLOCKED
79
- user_message = f'{violation_summary}. Remove secrets before sending.'
78
+ user_message = f'Remove secrets before sending. {violation_summary}'
80
79
  return HookDecision.deny(AiHookEventType.PROMPT, user_message)
81
80
  outcome = AIHookOutcome.WARNED
82
81
  return HookDecision.allow(AiHookEventType.PROMPT)
@@ -283,7 +282,7 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
283
282
  event_type=AiHookEventType.MCP_EXECUTION,
284
283
  deny_message=lambda v: f'Cycode blocked MCP tool call "{tool}". {v}',
285
284
  deny_agent_message='Do not pass secrets to tools. Use secret references (name/id) instead.',
286
- ask_message=lambda v: f'{v} in MCP tool call "{tool}". Allow execution?',
285
+ ask_message=lambda v: f'Allow MCP tool call "{tool}"? {v}',
287
286
  ask_agent_message='Possible secrets detected in tool arguments; proceed with caution.',
288
287
  ),
289
288
  scan_text=args_text,
@@ -1,15 +1,24 @@
1
1
  """
2
2
  Utility functions for AI guardrails.
3
3
 
4
- Includes JSON parsing, path matching, and text handling utilities.
4
+ Includes JSON parsing, path matching, text handling and hook-message utilities.
5
5
  """
6
6
 
7
7
  import json
8
8
  import os
9
9
  import sys
10
+ from collections import defaultdict
10
11
  from pathlib import Path
12
+ from typing import TYPE_CHECKING
11
13
 
12
14
  from cycode.cli.apps.ai_guardrails.scan.policy import get_policy_value
15
+ from cycode.cli.cli_types import SeverityOption
16
+
17
+ if TYPE_CHECKING:
18
+ from cycode.cli.models import LocalScanResult
19
+
20
+ # Keeps the hook message readable when a single file trips dozens of detections
21
+ MAX_VIOLATION_DETAIL_LINES = 5
13
22
 
14
23
 
15
24
  def read_stdin_text() -> str:
@@ -87,3 +96,52 @@ def is_denied_path(file_path: str, policy: dict) -> bool:
87
96
  def output_json(obj: dict) -> None:
88
97
  """Write JSON response to stdout (for IDE to read)."""
89
98
  print(json.dumps(obj), end='') # noqa: T201
99
+
100
+
101
+ def _build_detection_lines(
102
+ local_scan_results: list['LocalScanResult'], max_lines: int = MAX_VIOLATION_DETAIL_LINES
103
+ ) -> str:
104
+ """One line per distinct finding: what it is, and the value hash identifying it.
105
+
106
+ The value hash is safe to display; the value itself is not. Detections excluded by an existing
107
+ ignore rule are already gone from `document_detections`, so only what actually blocked is listed.
108
+ """
109
+ type_by_sha = {}
110
+ for local_scan_result in local_scan_results:
111
+ for document_detections in local_scan_result.document_detections:
112
+ for detection in document_detections.detections:
113
+ sha = detection.detection_details.get('sha512')
114
+ if sha and sha not in type_by_sha:
115
+ type_by_sha[sha] = detection.type or detection.message
116
+
117
+ if not type_by_sha:
118
+ return ''
119
+
120
+ lines = [f' - {detection_type}: {sha}' for sha, detection_type in list(type_by_sha.items())[:max_lines]]
121
+ remaining = len(type_by_sha) - len(lines)
122
+ if remaining:
123
+ lines.append(f' - ...and {remaining} more')
124
+
125
+ return '\n' + '\n'.join(lines)
126
+
127
+
128
+ def build_violation_summary(local_scan_results: list['LocalScanResult']) -> str:
129
+ """Build violation summary string with severity breakdown and emojis."""
130
+ detections_count = 0
131
+ severity_counts = defaultdict(int)
132
+
133
+ for local_scan_result in local_scan_results:
134
+ for document_detections in local_scan_result.document_detections:
135
+ for detection in document_detections.detections:
136
+ if detection.severity:
137
+ detections_count += 1
138
+ severity_counts[SeverityOption(detection.severity)] += 1
139
+
140
+ severity_parts = []
141
+ for severity in reversed(SeverityOption):
142
+ emoji = SeverityOption.get_member_unicode_emoji(severity)
143
+ count = severity_counts[severity]
144
+ severity_parts.append(f'{emoji} {severity.upper()} - {count}')
145
+
146
+ summary = f'Cycode found {detections_count} violations: {" | ".join(severity_parts)}'
147
+ return summary + _build_detection_lines(local_scan_results)
@@ -1,12 +1,10 @@
1
1
  import os
2
- from collections import defaultdict
3
2
  from typing import TYPE_CHECKING, Optional
4
3
  from uuid import UUID, uuid4
5
4
 
6
5
  import typer
7
6
 
8
7
  from cycode.cli import consts
9
- from cycode.cli.cli_types import SeverityOption
10
8
 
11
9
  if TYPE_CHECKING:
12
10
  from cycode.cli.models import LocalScanResult
@@ -41,24 +39,3 @@ def generate_unique_scan_id() -> UUID:
41
39
  return UUID(os.environ['PYTEST_TEST_UNIQUE_ID'])
42
40
 
43
41
  return uuid4()
44
-
45
-
46
- def build_violation_summary(local_scan_results: list['LocalScanResult']) -> str:
47
- """Build violation summary string with severity breakdown and emojis."""
48
- detections_count = 0
49
- severity_counts = defaultdict(int)
50
-
51
- for local_scan_result in local_scan_results:
52
- for document_detections in local_scan_result.document_detections:
53
- for detection in document_detections.detections:
54
- if detection.severity:
55
- detections_count += 1
56
- severity_counts[SeverityOption(detection.severity)] += 1
57
-
58
- severity_parts = []
59
- for severity in reversed(SeverityOption):
60
- emoji = SeverityOption.get_member_unicode_emoji(severity)
61
- count = severity_counts[severity]
62
- severity_parts.append(f'{emoji} {severity.upper()} - {count}')
63
-
64
- return f'Cycode found {detections_count} violations: {" | ".join(severity_parts)}'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cycode
3
- Version: 3.24.1.dev1
3
+ Version: 3.24.1.dev2
4
4
  Summary: Boost security in your dev lifecycle via SAST, SCA, Secrets & IaC scanning.
5
5
  License-Expression: MIT
6
6
  License-File: LICENCE
@@ -1,4 +1,4 @@
1
- cycode/__init__.py,sha256=I-iQNkQ_J9hG3RTJGySCJrSBiqdRU1jt3DxF46X8qaw,396
1
+ cycode/__init__.py,sha256=TGnaJP0fWShd83OGAGzngPbjXG-1A9ACQThCYV9aIOc,396
2
2
  cycode/__main__.py,sha256=Z3bD5yrA7yPvAChcADQrqCaZd0ChGI1gdiwALwbWJ6U,104
3
3
  cycode/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  cycode/cli/app.py,sha256=AlR2durAEbsa47PDfIj7JtMvJDWA_Dq6wPtVuMJYSCs,10250
@@ -21,12 +21,12 @@ cycode/cli/apps/ai_guardrails/scan/__init__.py,sha256=qJc82XiQGiAuc1sYY8Ij_A-qXp
21
21
  cycode/cli/apps/ai_guardrails/scan/consts.py,sha256=8H8JXlgm65sYkgTXqsBInHhHe80UPjqY4vumZXB4GGM,1060
22
22
  cycode/cli/apps/ai_guardrails/scan/detach.py,sha256=8BRgBq9Qi8nXEx6NKlE8scSRPkO30XCfsiI5PLvq6Gk,2700
23
23
  cycode/cli/apps/ai_guardrails/scan/guardrail_config.py,sha256=QSBwmkTdlC9zXIqI3gIiOlefviJWrpeJHzFu1Cs-zgE,7051
24
- cycode/cli/apps/ai_guardrails/scan/handlers.py,sha256=vOAvknrlCN-G_AUE7adlZAg0V7BhtGTYKB4r1yZDBu0,17695
24
+ cycode/cli/apps/ai_guardrails/scan/handlers.py,sha256=i8fJ_K3m2v45DRSfkK0Wt4yEZfYIxOaLtZXiGna--5A,17641
25
25
  cycode/cli/apps/ai_guardrails/scan/payload.py,sha256=sWsWq5yXP54MVCajsb180kLuprI_M2kspMOzQGh7r_o,1534
26
26
  cycode/cli/apps/ai_guardrails/scan/policy.py,sha256=3HuDoL_NYE3lyHRAlIMleq6pi-stIDQwotHsKhUFhTQ,5000
27
27
  cycode/cli/apps/ai_guardrails/scan/scan_command.py,sha256=fgV32phgkBPwqSau7oFDISZ58CVpq214JlYwZQM9bnM,8318
28
28
  cycode/cli/apps/ai_guardrails/scan/types.py,sha256=ybQm242QN0l_4SSNX4xMHXxzqEK-MW-hfIOixI7zvGU,1497
29
- cycode/cli/apps/ai_guardrails/scan/utils.py,sha256=QzR_zmivDYwg2-F8g4bFfsycHhNo-pPuJly0P_l0gm8,2879
29
+ cycode/cli/apps/ai_guardrails/scan/utils.py,sha256=4CIZAILS9m5-JsFbUderwBVzFAjqAVVVRhWgE9oLPhM,5292
30
30
  cycode/cli/apps/ai_guardrails/session_start_command.py,sha256=oEW-OsHXkf8P7SX1vKa9Ddlv_hASRiQ42JZR3MnJfRg,7171
31
31
  cycode/cli/apps/ai_guardrails/status_command.py,sha256=Uqss68TEPCYPXpLix6Bh-4J3g-khxWsAqlIGYH5x4bQ,3203
32
32
  cycode/cli/apps/ai_guardrails/uninstall_command.py,sha256=dOmePfZmlHAPy2zEJM1yMtSuDqvzDwtqgmLKYK-T9PI,2698
@@ -186,7 +186,7 @@ cycode/cli/utils/jwt_utils.py,sha256=EGI-0CKhCGY8hIcZ9b9diq9hqtOUf8Ha8ukeVJIf974
186
186
  cycode/cli/utils/path_utils.py,sha256=zc48CSU7hxqjSgfH6h5M1B0kIcKW44BOJrUa_6z-mAo,4317
187
187
  cycode/cli/utils/progress_bar.py,sha256=bKBWHHdZsVkdDdWMJLfgLGR0cBYeB44P_DpRM8pvWqU,9528
188
188
  cycode/cli/utils/scan_batch.py,sha256=5xKGVDVqoRxdKhuZkK11x4QrNqKmU20Q83E_fy8Nndk,5188
189
- cycode/cli/utils/scan_utils.py,sha256=sTj7j9dVHcgeMqfYp8sO78ZiWX8LnhpgjCOi1N1gmAM,2248
189
+ cycode/cli/utils/scan_utils.py,sha256=_VkZ7maLVST6J8dsqDsKYWQj2EVfqI3iPPsA1QJOGEU,1259
190
190
  cycode/cli/utils/shell_executor.py,sha256=VkzzQPZCmTkFvDjhgJrkv-Icej3U1wLW9LLN6k6OahA,1848
191
191
  cycode/cli/utils/string_utils.py,sha256=KyPSAHDRPEGNCCcKTF0v99vad5z9djpVGc8nxpBqdYo,2445
192
192
  cycode/cli/utils/task_timer.py,sha256=wxfM2TtJGjc1F17CIja_Qmt6zd4a1qdMwuz0ltgTDAg,2722
@@ -217,8 +217,8 @@ cycode/cyclient/report_client.py,sha256=Scq30NeJPzgXv0hPLO1U05AdE9i_2iu6cIrSKpEJ
217
217
  cycode/cyclient/scan_client.py,sha256=DqAZ7u6Z_cvw9A9RlLkAQUgLRwPCCAsUq5U9umt4F7Y,16955
218
218
  cycode/cyclient/scan_config_base.py,sha256=mXsPZGYCtp85rv5GIige40yQZXuRcEKUW-VQJ0vgFzk,1201
219
219
  cycode/logger.py,sha256=EfZGRK6VC5rE_LAjIcRrHFiQCueylCDXoG6bvGkrIME,2111
220
- cycode-3.24.1.dev1.dist-info/METADATA,sha256=Q_CfNk0EX1rgA3ndLHucRys9j-AqsPED4kmveNVm5eY,93687
221
- cycode-3.24.1.dev1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
222
- cycode-3.24.1.dev1.dist-info/entry_points.txt,sha256=iDcVJM8ByLElVgvBgtYxDjw1kT7O8Mo0LcWZIT5L3Ig,45
223
- cycode-3.24.1.dev1.dist-info/licenses/LICENCE,sha256=2Wx4N6mD_4xB7-E3hPkZ3MPhpJy__k_I8MaCSO-PDRo,1068
224
- cycode-3.24.1.dev1.dist-info/RECORD,,
220
+ cycode-3.24.1.dev2.dist-info/METADATA,sha256=AxXWfRpzaPJHwREMYa_RvGp_-JGo4iGbXkzEPbZBMGM,93687
221
+ cycode-3.24.1.dev2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
222
+ cycode-3.24.1.dev2.dist-info/entry_points.txt,sha256=iDcVJM8ByLElVgvBgtYxDjw1kT7O8Mo0LcWZIT5L3Ig,45
223
+ cycode-3.24.1.dev2.dist-info/licenses/LICENCE,sha256=2Wx4N6mD_4xB7-E3hPkZ3MPhpJy__k_I8MaCSO-PDRo,1068
224
+ cycode-3.24.1.dev2.dist-info/RECORD,,