Skip to content

Commit 2fa8e62

Browse files
committed
add supported modules
1 parent 66119ff commit 2fa8e62

File tree

3 files changed

+111
-18
lines changed

3 files changed

+111
-18
lines changed

cycode/cli/commands/status/status_command.py

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,63 @@
11
import dataclasses
22
import json
33
import platform
4+
from typing import Dict
45

56
import click
67

78
from cycode import __version__
89
from cycode.cli.commands.auth_common import get_authorization_info
910
from cycode.cli.consts import PROGRAM_NAME
1011
from cycode.cli.user_settings.configuration_manager import ConfigurationManager
12+
from cycode.cli.utils.get_api_client import get_scan_cycode_client
13+
from cycode.cyclient import logger
14+
15+
16+
class CliStatusBase:
17+
def as_dict(self) -> Dict[str, any]:
18+
return dataclasses.asdict(self)
19+
20+
def _get_text_message_part(self, key: str, value: any, intent: int = 0) -> str:
21+
message_parts = []
22+
23+
intent_prefix = ' ' * intent * 2
24+
human_readable_key = key.replace('_', ' ').capitalize()
25+
26+
if isinstance(value, dict):
27+
message_parts.append(f'{intent_prefix}{human_readable_key}:')
28+
for sub_key, sub_value in value.items():
29+
message_parts.append(self._get_text_message_part(sub_key, sub_value, intent=intent + 1))
30+
elif isinstance(value, (list, set, tuple)):
31+
message_parts.append(f'{intent_prefix}{human_readable_key}:')
32+
for index, sub_value in enumerate(value):
33+
message_parts.append(self._get_text_message_part(f'#{index}', sub_value, intent=intent + 1))
34+
else:
35+
message_parts.append(f'{intent_prefix}{human_readable_key}: {value}')
36+
37+
return '\n'.join(message_parts)
38+
39+
def as_text(self) -> str:
40+
message_parts = []
41+
for key, value in self.as_dict().items():
42+
message_parts.append(self._get_text_message_part(key, value))
43+
44+
return '\n'.join(message_parts)
45+
46+
def as_json(self) -> str:
47+
return json.dumps(self.as_dict())
48+
49+
50+
@dataclasses.dataclass
51+
class CliSupportedModulesStatus(CliStatusBase):
52+
secret_scanning: bool = False
53+
sca_scanning: bool = False
54+
iac_scanning: bool = False
55+
sast_scanning: bool = False
56+
ai_large_language_model: bool = False
1157

1258

1359
@dataclasses.dataclass
14-
class Status:
60+
class CliStatus(CliStatusBase):
1561
program: str
1662
version: str
1763
os: str
@@ -23,26 +69,30 @@ class Status:
2369
is_authenticated: bool
2470
user_id: str = None
2571
tenant_id: str = None
72+
supported_modules: CliSupportedModulesStatus = None
2673

27-
def as_text(self) -> str:
28-
message_parts = []
29-
for key, value in dataclasses.asdict(self).items():
30-
human_readable_key = key.replace('_', ' ').capitalize()
31-
message_parts.append(f'{human_readable_key}: {value}')
3274

33-
return '\n'.join(message_parts)
34-
35-
def as_json(self) -> str:
36-
return json.dumps(dataclasses.asdict(self))
37-
38-
39-
def get_cli_status() -> Status:
75+
def get_cli_status() -> CliStatus:
4076
configuration_manager = ConfigurationManager()
77+
4178
auth_info = get_authorization_info()
79+
is_authenticated = auth_info is not None
80+
81+
supported_modules_status = CliSupportedModulesStatus()
82+
if is_authenticated:
83+
try:
84+
client = get_scan_cycode_client()
85+
supported_modules_preferences = client.get_supported_modules_preferences()
4286

43-
# TODO: Add supported modules; add AI status
87+
supported_modules_status.secret_scanning = supported_modules_preferences.secret_scanning
88+
supported_modules_status.sca_scanning = supported_modules_preferences.sca_scanning
89+
supported_modules_status.iac_scanning = supported_modules_preferences.iac_scanning
90+
supported_modules_status.sast_scanning = supported_modules_preferences.sast_scanning
91+
supported_modules_status.ai_large_language_model = supported_modules_preferences.ai_large_language_model
92+
except Exception as e:
93+
logger.debug('Failed to get supported modules preferences', exc_info=e)
4494

45-
return Status(
95+
return CliStatus(
4696
program=PROGRAM_NAME,
4797
version=__version__,
4898
os=platform.system(),
@@ -51,9 +101,10 @@ def get_cli_status() -> Status:
51101
installation_id=configuration_manager.get_or_create_installation_id(),
52102
app_url=configuration_manager.get_cycode_app_url(),
53103
api_url=configuration_manager.get_cycode_api_url(),
54-
is_authenticated=auth_info is not None,
104+
is_authenticated=is_authenticated,
55105
user_id=auth_info.user_id if auth_info else None,
56106
tenant_id=auth_info.tenant_id if auth_info else None,
107+
supported_modules=supported_modules_status,
57108
)
58109

59110

cycode/cyclient/models.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,3 +478,41 @@ class Meta:
478478
@post_load
479479
def build_dto(self, data: Dict[str, Any], **_) -> ScanResultsSyncFlow:
480480
return ScanResultsSyncFlow(**data)
481+
482+
483+
@dataclass
484+
class SupportedModulesPreferences:
485+
secret_scanning: bool
486+
leak_scanning: bool
487+
iac_scanning: bool
488+
sca_scanning: bool
489+
ci_cd_scanning: bool
490+
sast_scanning: bool
491+
container_scanning: bool
492+
access_review: bool
493+
asoc: bool
494+
cimon: bool
495+
ai_machine_learning: bool
496+
ai_large_language_model: bool
497+
498+
499+
class SupportedModulesPreferencesSchema(Schema):
500+
class Meta:
501+
unknown = EXCLUDE
502+
503+
secret_scanning = fields.Boolean()
504+
leak_scanning = fields.Boolean()
505+
iac_scanning = fields.Boolean()
506+
sca_scanning = fields.Boolean()
507+
ci_cd_scanning = fields.Boolean()
508+
sast_scanning = fields.Boolean()
509+
container_scanning = fields.Boolean()
510+
access_review = fields.Boolean()
511+
asoc = fields.Boolean()
512+
cimon = fields.Boolean()
513+
ai_machine_learning = fields.Boolean()
514+
ai_large_language_model = fields.Boolean()
515+
516+
@post_load
517+
def build_dto(self, data: Dict[str, Any], **_) -> 'SupportedModulesPreferences':
518+
return SupportedModulesPreferences(**data)

cycode/cyclient/scan_client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(
2727
self._DETECTIONS_SERVICE_CONTROLLER_PATH = 'api/v1/detections'
2828
self._DETECTIONS_SERVICE_CLI_CONTROLLER_PATH = 'api/v1/detections/cli'
2929

30-
self.POLICIES_SERVICE_CONTROLLER_PATH_V3 = 'api/v3/policies'
30+
self._POLICIES_SERVICE_CONTROLLER_PATH_V3 = 'api/v3/policies'
3131

3232
self._hide_response_log = hide_response_log
3333

@@ -198,10 +198,14 @@ def get_scan_details(self, scan_type: str, scan_id: str) -> models.ScanDetailsRe
198198
def get_detection_rules_path(self) -> str:
199199
return (
200200
f'{self.scan_config.get_detections_prefix()}/'
201-
f'{self.POLICIES_SERVICE_CONTROLLER_PATH_V3}/'
201+
f'{self._POLICIES_SERVICE_CONTROLLER_PATH_V3}/'
202202
f'detection_rules/byIds'
203203
)
204204

205+
def get_supported_modules_preferences(self) -> models.SupportedModulesPreferences:
206+
response = self.scan_cycode_client.get(url_path='preferences/api/v1/supportedmodules')
207+
return models.SupportedModulesPreferencesSchema().load(response.json())
208+
205209
@staticmethod
206210
def _get_policy_type_by_scan_type(scan_type: str) -> str:
207211
scan_type_to_policy_type = {

0 commit comments

Comments
 (0)