cycode 3.25.1.dev6__py3-none-any.whl → 3.25.2.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.
Files changed (125) hide show
  1. cycode/__init__.py +1 -1
  2. cycode/cli/app.py +9 -10
  3. cycode/cli/apps/activation_manager.py +6 -6
  4. cycode/cli/apps/ai_guardrails/command_utils.py +1 -2
  5. cycode/cli/apps/ai_guardrails/hooks_manager.py +6 -7
  6. cycode/cli/apps/ai_guardrails/ides/__init__.py +8 -9
  7. cycode/cli/apps/ai_guardrails/ides/_plugin_utils.py +11 -10
  8. cycode/cli/apps/ai_guardrails/ides/_skill_utils.py +1 -2
  9. cycode/cli/apps/ai_guardrails/ides/base.py +19 -21
  10. cycode/cli/apps/ai_guardrails/ides/claude_code.py +54 -16
  11. cycode/cli/apps/ai_guardrails/ides/codex.py +54 -20
  12. cycode/cli/apps/ai_guardrails/ides/copilot.py +23 -25
  13. cycode/cli/apps/ai_guardrails/ides/cursor.py +48 -10
  14. cycode/cli/apps/ai_guardrails/install_command.py +3 -3
  15. cycode/cli/apps/ai_guardrails/scan/guardrail_config.py +44 -22
  16. cycode/cli/apps/ai_guardrails/scan/handlers.py +79 -14
  17. cycode/cli/apps/ai_guardrails/scan/mcp_server_status.py +103 -0
  18. cycode/cli/apps/ai_guardrails/scan/payload.py +13 -14
  19. cycode/cli/apps/ai_guardrails/scan/policy.py +3 -3
  20. cycode/cli/apps/ai_guardrails/scan/scan_command.py +4 -4
  21. cycode/cli/apps/ai_guardrails/scan/types.py +1 -0
  22. cycode/cli/apps/ai_guardrails/session_start_command.py +46 -21
  23. cycode/cli/apps/ai_guardrails/status_command.py +2 -2
  24. cycode/cli/apps/ai_guardrails/uninstall_command.py +2 -2
  25. cycode/cli/apps/api/__init__.py +3 -3
  26. cycode/cli/apps/api/api_command.py +6 -6
  27. cycode/cli/apps/api/openapi_spec.py +4 -5
  28. cycode/cli/apps/auth/auth_common.py +4 -4
  29. cycode/cli/apps/configure/configure_command.py +2 -4
  30. cycode/cli/apps/configure/messages.py +2 -4
  31. cycode/cli/apps/configure/prompts.py +5 -7
  32. cycode/cli/apps/ignore/ignore_command.py +7 -7
  33. cycode/cli/apps/mcp/mcp_command.py +11 -11
  34. cycode/cli/apps/report/sbom/common.py +5 -5
  35. cycode/cli/apps/report/sbom/sbom_command.py +2 -2
  36. cycode/cli/apps/report/sbom/sbom_report_file.py +1 -2
  37. cycode/cli/apps/report_import/sbom/sbom_command.py +3 -3
  38. cycode/cli/apps/sca_options.py +3 -3
  39. cycode/cli/apps/scan/aggregation_report.py +4 -4
  40. cycode/cli/apps/scan/code_scanner.py +8 -7
  41. cycode/cli/apps/scan/commit_range_scanner.py +10 -10
  42. cycode/cli/apps/scan/detection_excluder.py +2 -4
  43. cycode/cli/apps/scan/pre_commit/pre_commit_command.py +3 -3
  44. cycode/cli/apps/scan/pre_push/pre_push_command.py +3 -3
  45. cycode/cli/apps/scan/pre_receive/pre_receive_command.py +2 -2
  46. cycode/cli/apps/scan/remote_url_resolver.py +6 -8
  47. cycode/cli/apps/scan/repository/repository_command.py +2 -2
  48. cycode/cli/apps/scan/scan_command.py +2 -2
  49. cycode/cli/apps/scan/scan_parameters.py +1 -3
  50. cycode/cli/apps/scan/scan_result.py +5 -5
  51. cycode/cli/console.py +2 -2
  52. cycode/cli/exceptions/custom_exceptions.py +1 -1
  53. cycode/cli/exceptions/handle_errors.py +1 -3
  54. cycode/cli/exceptions/handle_scan_errors.py +1 -2
  55. cycode/cli/files_collector/commit_range_documents.py +14 -14
  56. cycode/cli/files_collector/models/in_memory_zip.py +3 -3
  57. cycode/cli/files_collector/repository_documents.py +3 -3
  58. cycode/cli/files_collector/sca/base_restore_dependencies.py +7 -8
  59. cycode/cli/files_collector/sca/go/restore_go_dependencies.py +1 -2
  60. cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +2 -3
  61. cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +3 -4
  62. cycode/cli/files_collector/sca/npm/restore_bun_dependencies.py +3 -4
  63. cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +1 -2
  64. cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +2 -3
  65. cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +2 -3
  66. cycode/cli/files_collector/sca/npm/workspace/coverage.py +5 -5
  67. cycode/cli/files_collector/sca/npm/workspace/files.py +2 -3
  68. cycode/cli/files_collector/sca/npm/workspace/globs.py +1 -2
  69. cycode/cli/files_collector/sca/npm/workspace/resolvers.py +14 -16
  70. cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +1 -2
  71. cycode/cli/files_collector/sca/python/restore_pip_dependencies.py +2 -3
  72. cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +1 -2
  73. cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +2 -3
  74. cycode/cli/files_collector/sca/python/restore_uv_dependencies.py +2 -3
  75. cycode/cli/files_collector/sca/sca_file_collector.py +5 -5
  76. cycode/cli/files_collector/zip_documents.py +1 -2
  77. cycode/cli/models.py +8 -8
  78. cycode/cli/printers/console_printer.py +9 -9
  79. cycode/cli/printers/json_printer.py +2 -2
  80. cycode/cli/printers/printer_base.py +3 -3
  81. cycode/cli/printers/rich_printer.py +2 -2
  82. cycode/cli/printers/tables/table.py +4 -4
  83. cycode/cli/printers/tables/table_models.py +2 -2
  84. cycode/cli/printers/tables/table_printer_base.py +2 -2
  85. cycode/cli/printers/text_printer.py +4 -4
  86. cycode/cli/printers/utils/code_snippet_syntax.py +1 -1
  87. cycode/cli/printers/utils/detection_data.py +3 -3
  88. cycode/cli/printers/utils/sca_ossf.py +4 -4
  89. cycode/cli/printers/utils/sca_policy_details.py +2 -1
  90. cycode/cli/user_settings/config_file_manager.py +10 -10
  91. cycode/cli/user_settings/configuration_manager.py +6 -6
  92. cycode/cli/user_settings/credentials_manager.py +5 -6
  93. cycode/cli/utils/get_api_client.py +5 -5
  94. cycode/cli/utils/git_proxy.py +8 -8
  95. cycode/cli/utils/host_info.py +21 -38
  96. cycode/cli/utils/ignore_utils.py +13 -15
  97. cycode/cli/utils/jwt_utils.py +2 -4
  98. cycode/cli/utils/path_utils.py +6 -6
  99. cycode/cli/utils/progress_bar.py +4 -4
  100. cycode/cli/utils/scan_batch.py +2 -1
  101. cycode/cli/utils/scan_utils.py +2 -2
  102. cycode/cli/utils/shell_executor.py +3 -4
  103. cycode/cli/utils/task_timer.py +4 -4
  104. cycode/cli/utils/trust_store.py +0 -17
  105. cycode/cli/utils/url_utils.py +1 -2
  106. cycode/cli/utils/version_checker.py +4 -5
  107. cycode/cli/utils/yaml_utils.py +1 -1
  108. cycode/config.py +1 -2
  109. cycode/cyclient/ai_security_manager_client.py +27 -21
  110. cycode/cyclient/auth_client.py +2 -4
  111. cycode/cyclient/base_token_auth_client.py +2 -2
  112. cycode/cyclient/client_creator.py +4 -6
  113. cycode/cyclient/cycode_client_base.py +10 -9
  114. cycode/cyclient/cycode_dev_based_client.py +1 -3
  115. cycode/cyclient/headers.py +1 -2
  116. cycode/cyclient/import_sbom_client.py +3 -4
  117. cycode/cyclient/models.py +88 -30
  118. cycode/cyclient/report_client.py +1 -2
  119. cycode/cyclient/scan_client.py +8 -7
  120. cycode/logger.py +4 -4
  121. {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/METADATA +12 -19
  122. {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/RECORD +125 -124
  123. {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/WHEEL +1 -1
  124. {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/entry_points.txt +0 -0
  125. {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/licenses/LICENCE +0 -0
@@ -3,7 +3,6 @@
3
3
  import json
4
4
  import os
5
5
  from pathlib import Path
6
- from typing import Optional
7
6
 
8
7
  from cycode.cli.utils.path_utils import get_absolute_path
9
8
  from cycode.logger import get_logger
@@ -17,7 +16,7 @@ def resolved_path(path: object) -> str:
17
16
  return os.path.realpath(get_absolute_path(str(path)))
18
17
 
19
18
 
20
- def file_stamp(path: Path) -> Optional[FileStamp]:
19
+ def file_stamp(path: Path) -> FileStamp | None:
21
20
  try:
22
21
  stat_result = path.stat()
23
22
  except OSError:
@@ -26,7 +25,7 @@ def file_stamp(path: Path) -> Optional[FileStamp]:
26
25
  return str(path), stat_result.st_mtime_ns, stat_result.st_size
27
26
 
28
27
 
29
- def read_json_object(path: Path) -> Optional[dict]:
28
+ def read_json_object(path: Path) -> dict | None:
30
29
  try:
31
30
  content = json.loads(path.read_text(encoding='UTF-8'))
32
31
  except FileNotFoundError:
@@ -72,8 +72,7 @@ def _split_workspace_patterns(declared: list) -> _WorkspacePatterns:
72
72
  stripped = entry.strip()
73
73
  is_excluded = stripped.startswith(_NEGATION_PREFIX)
74
74
  normalized = (stripped[1:] if is_excluded else stripped).strip()
75
- if normalized.startswith('./'):
76
- normalized = normalized[2:]
75
+ normalized = normalized.removeprefix('./')
77
76
 
78
77
  normalized = normalized.rstrip('/')
79
78
  if not normalized:
@@ -9,7 +9,6 @@ import json
9
9
  import re
10
10
  from abc import ABC, abstractmethod
11
11
  from pathlib import Path
12
- from typing import Optional
13
12
 
14
13
  import yaml
15
14
 
@@ -38,17 +37,16 @@ _YARN_WORKSPACE_PROTOCOL = re.compile(r'@workspace:([^"\',\s]+)')
38
37
  _BUN_LOCKFILE_WORKSPACES_SECTION = 'workspaces'
39
38
  _TRAILING_COMMA = re.compile(r',(\s*[}\]])')
40
39
 
41
- _member_names_cache: dict[FileStamp, Optional[frozenset[str]]] = {}
40
+ _member_names_cache: dict[FileStamp, frozenset[str] | None] = {}
42
41
 
43
42
 
44
43
  def clear_cache() -> None:
45
44
  _member_names_cache.clear()
46
45
 
47
46
 
48
- def _normalize_member_path(member_path: str) -> Optional[str]:
47
+ def _normalize_member_path(member_path: str) -> str | None:
49
48
  normalized = member_path.strip()
50
- if normalized.startswith('./'):
51
- normalized = normalized[2:]
49
+ normalized = normalized.removeprefix('./')
52
50
 
53
51
  normalized = normalized.rstrip('/')
54
52
  if not normalized or normalized == _PNPM_LOCKFILE_ROOT_IMPORTER:
@@ -57,7 +55,7 @@ def _normalize_member_path(member_path: str) -> Optional[str]:
57
55
  return normalized
58
56
 
59
57
 
60
- def _npm_lockfile_member_names(lock_file: Path) -> Optional[frozenset]:
58
+ def _npm_lockfile_member_names(lock_file: Path) -> frozenset | None:
61
59
  content = read_json_object(lock_file)
62
60
  packages = content.get(_LOCKFILE_PACKAGES_SECTION) if content is not None else None
63
61
  if not isinstance(packages, dict):
@@ -96,7 +94,7 @@ def _read_pnpm_importers_section(lock_file: Path) -> str:
96
94
  return '\n'.join(section)
97
95
 
98
96
 
99
- def _pnpm_lockfile_member_names(lock_file: Path) -> Optional[frozenset]:
97
+ def _pnpm_lockfile_member_names(lock_file: Path) -> frozenset | None:
100
98
  section = _read_pnpm_importers_section(lock_file)
101
99
  if not section:
102
100
  return None
@@ -124,7 +122,7 @@ def _pnpm_lockfile_member_names(lock_file: Path) -> Optional[frozenset]:
124
122
  return frozenset(member_names) or None
125
123
 
126
124
 
127
- def _bun_lockfile_member_names(lock_file: Path) -> Optional[frozenset]:
125
+ def _bun_lockfile_member_names(lock_file: Path) -> frozenset | None:
128
126
  """bun.lock is JSON with trailing commas, and names its members under "workspaces"."""
129
127
  try:
130
128
  text = lock_file.read_text(encoding='UTF-8')
@@ -151,7 +149,7 @@ def _bun_lockfile_member_names(lock_file: Path) -> Optional[frozenset]:
151
149
  return frozenset(member_names)
152
150
 
153
151
 
154
- def _yarn_lockfile_member_names(lock_file: Path) -> Optional[frozenset]:
152
+ def _yarn_lockfile_member_names(lock_file: Path) -> frozenset | None:
155
153
  """Yarn berry records every member as "<name>@workspace:<path>"; classic yarn records nothing."""
156
154
  try:
157
155
  text = lock_file.read_text(encoding='UTF-8', errors='replace')
@@ -201,7 +199,7 @@ class WorkspaceMemberResolver(ABC):
201
199
  """
202
200
  return False
203
201
 
204
- def resolve(self, lock_file: Path) -> Optional[frozenset]:
202
+ def resolve(self, lock_file: Path) -> frozenset | None:
205
203
  """Member paths this lockfile resolves, or None when it cannot name them.
206
204
 
207
205
  Caches on the file's identity so one scan parses each root lockfile once.
@@ -218,14 +216,14 @@ class WorkspaceMemberResolver(ABC):
218
216
  return member_names
219
217
 
220
218
  @abstractmethod
221
- def _read_member_names(self, lock_file: Path) -> Optional[frozenset]: ...
219
+ def _read_member_names(self, lock_file: Path) -> frozenset | None: ...
222
220
 
223
221
 
224
222
  class NpmLockfileResolver(WorkspaceMemberResolver):
225
223
  package_manager = NPM_PACKAGE_MANAGER
226
224
  lock_file_names = (NPM_LOCK_FILE_NAME, NPM_SHRINKWRAP_FILE_NAME)
227
225
 
228
- def _read_member_names(self, lock_file: Path) -> Optional[frozenset]:
226
+ def _read_member_names(self, lock_file: Path) -> frozenset | None:
229
227
  return _npm_lockfile_member_names(lock_file)
230
228
 
231
229
 
@@ -233,7 +231,7 @@ class PnpmLockfileResolver(WorkspaceMemberResolver):
233
231
  package_manager = PNPM_PACKAGE_MANAGER
234
232
  lock_file_names = (PNPM_LOCK_FILE_NAME,)
235
233
 
236
- def _read_member_names(self, lock_file: Path) -> Optional[frozenset]:
234
+ def _read_member_names(self, lock_file: Path) -> frozenset | None:
237
235
  return _pnpm_lockfile_member_names(lock_file)
238
236
 
239
237
 
@@ -244,7 +242,7 @@ class YarnLockfileResolver(WorkspaceMemberResolver):
244
242
  lock_file_names = (YARN_LOCK_FILE_NAME,)
245
243
  may_use_workspace_globs = True
246
244
 
247
- def _read_member_names(self, lock_file: Path) -> Optional[frozenset]:
245
+ def _read_member_names(self, lock_file: Path) -> frozenset | None:
248
246
  return _yarn_lockfile_member_names(lock_file)
249
247
 
250
248
 
@@ -254,7 +252,7 @@ class BunLockfileResolver(WorkspaceMemberResolver):
254
252
  package_manager = BUN_PACKAGE_MANAGER
255
253
  lock_file_names = (BUN_LOCK_FILE_NAME,)
256
254
 
257
- def _read_member_names(self, lock_file: Path) -> Optional[frozenset]:
255
+ def _read_member_names(self, lock_file: Path) -> frozenset | None:
258
256
  return _bun_lockfile_member_names(lock_file)
259
257
 
260
258
 
@@ -275,7 +273,7 @@ class OpaqueLockfileResolver(WorkspaceMemberResolver):
275
273
  def lock_file_names(self) -> tuple:
276
274
  return self._lock_file_names
277
275
 
278
- def _read_member_names(self, lock_file: Path) -> Optional[frozenset]:
276
+ def _read_member_names(self, lock_file: Path) -> frozenset | None:
279
277
  return None
280
278
 
281
279
 
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Optional
3
2
 
4
3
  import typer
5
4
 
@@ -21,7 +20,7 @@ class RestoreComposerDependencies(BaseRestoreDependencies):
21
20
  def is_project(self, document: Document) -> bool:
22
21
  return Path(document.path).name == COMPOSER_MANIFEST_FILE_NAME
23
22
 
24
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
23
+ def try_restore_dependencies(self, document: Document) -> Document | None:
25
24
  manifest_dir = self.get_manifest_dir(document)
26
25
  lockfile_path = Path(manifest_dir) / COMPOSER_LOCK_FILE_NAME if manifest_dir else None
27
26
 
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Optional
3
2
 
4
3
  import typer
5
4
 
@@ -18,7 +17,7 @@ _POETRY_TOOL_SECTION = '[tool.poetry]'
18
17
  _UV_TOOL_SECTION = '[tool.uv]'
19
18
 
20
19
 
21
- def _indicates_plain_pip(pyproject_content: Optional[str]) -> bool:
20
+ def _indicates_plain_pip(pyproject_content: str | None) -> bool:
22
21
  """Return True if pyproject.toml content signals a plain-pip project (no Poetry, no uv)."""
23
22
  if not pyproject_content:
24
23
  return False
@@ -44,7 +43,7 @@ class RestorePipDependencies(BaseRestoreDependencies):
44
43
 
45
44
  return _indicates_plain_pip(document.content)
46
45
 
47
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
46
+ def try_restore_dependencies(self, document: Document) -> Document | None:
48
47
  manifest_dir = self.get_manifest_dir(document)
49
48
  lockfile_path = Path(manifest_dir) / PIP_LOCK_FILE_NAME if manifest_dir else None
50
49
 
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Optional
3
2
 
4
3
  import typer
5
4
 
@@ -21,7 +20,7 @@ class RestorePipenvDependencies(BaseRestoreDependencies):
21
20
  def is_project(self, document: Document) -> bool:
22
21
  return Path(document.path).name == PIPENV_MANIFEST_FILE_NAME
23
22
 
24
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
23
+ def try_restore_dependencies(self, document: Document) -> Document | None:
25
24
  manifest_dir = self.get_manifest_dir(document)
26
25
  lockfile_path = Path(manifest_dir) / PIPENV_LOCK_FILE_NAME if manifest_dir else None
27
26
 
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Optional
3
2
 
4
3
  import typer
5
4
 
@@ -17,7 +16,7 @@ POETRY_LOCK_FILE_NAME = 'poetry.lock'
17
16
  _POETRY_TOOL_SECTION = '[tool.poetry]'
18
17
 
19
18
 
20
- def _indicates_poetry(pyproject_content: Optional[str]) -> bool:
19
+ def _indicates_poetry(pyproject_content: str | None) -> bool:
21
20
  """Return True if pyproject.toml content signals that this project uses Poetry."""
22
21
  if not pyproject_content:
23
22
  return False
@@ -38,7 +37,7 @@ class RestorePoetryDependencies(BaseRestoreDependencies):
38
37
 
39
38
  return _indicates_poetry(document.content)
40
39
 
41
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
40
+ def try_restore_dependencies(self, document: Document) -> Document | None:
42
41
  manifest_dir = self.get_manifest_dir(document)
43
42
  lockfile_path = Path(manifest_dir) / POETRY_LOCK_FILE_NAME if manifest_dir else None
44
43
 
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Optional
3
2
 
4
3
  import typer
5
4
 
@@ -16,7 +15,7 @@ UV_LOCK_FILE_NAME = 'uv.lock'
16
15
  _UV_TOOL_SECTION = '[tool.uv]'
17
16
 
18
17
 
19
- def _indicates_uv(pyproject_content: Optional[str]) -> bool:
18
+ def _indicates_uv(pyproject_content: str | None) -> bool:
20
19
  """Return True if pyproject.toml content signals that this project uses UV."""
21
20
  if not pyproject_content:
22
21
  return False
@@ -37,7 +36,7 @@ class RestoreUvDependencies(BaseRestoreDependencies):
37
36
 
38
37
  return _indicates_uv(document.content)
39
38
 
40
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
39
+ def try_restore_dependencies(self, document: Document) -> Document | None:
41
40
  manifest_dir = self.get_manifest_dir(document)
42
41
  lockfile_path = Path(manifest_dir) / UV_LOCK_FILE_NAME if manifest_dir else None
43
42
 
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import TYPE_CHECKING, Optional
2
+ from typing import TYPE_CHECKING
3
3
 
4
4
  import typer
5
5
 
@@ -39,7 +39,7 @@ logger = get_logger('SCA File Collector')
39
39
 
40
40
 
41
41
  def _add_ecosystem_related_files_if_exists(
42
- documents: list[Document], repo: Optional['Repo'] = None, commit_rev: Optional[str] = None
42
+ documents: list[Document], repo: 'Repo | None' = None, commit_rev: str | None = None
43
43
  ) -> None:
44
44
  documents_to_add: list[Document] = []
45
45
  for doc in documents:
@@ -81,7 +81,7 @@ def perform_sca_pre_commit_scan_actions(
81
81
 
82
82
 
83
83
  def _get_doc_ecosystem_related_project_files(
84
- doc: Document, documents: list[Document], ecosystem: str, commit_rev: Optional[str], repo: Optional['Repo']
84
+ doc: Document, documents: list[Document], ecosystem: str, commit_rev: str | None, repo: 'Repo | None'
85
85
  ) -> list[Document]:
86
86
  documents_to_add: list[Document] = []
87
87
  for ecosystem_project_file in consts.PROJECT_FILES_BY_ECOSYSTEM_MAP.get(ecosystem):
@@ -102,7 +102,7 @@ def _is_project_file_exists_in_documents(documents: list[Document], file: str) -
102
102
  return any(doc for doc in documents if file == doc.path)
103
103
 
104
104
 
105
- def _get_project_file_ecosystem(document: Document) -> Optional[str]:
105
+ def _get_project_file_ecosystem(document: Document) -> str | None:
106
106
  for ecosystem, project_files in consts.PROJECT_FILES_BY_ECOSYSTEM_MAP.items():
107
107
  for project_file in project_files:
108
108
  if document.path.endswith(project_file):
@@ -118,7 +118,7 @@ def _try_restore_dependencies(
118
118
  ctx: typer.Context,
119
119
  restore_dependencies: 'BaseRestoreDependencies',
120
120
  document: Document,
121
- ) -> Optional[Document]:
121
+ ) -> Document | None:
122
122
  if not restore_dependencies.is_project(document):
123
123
  return None
124
124
 
@@ -1,6 +1,5 @@
1
1
  import timeit
2
2
  from pathlib import Path
3
- from typing import Optional
4
3
 
5
4
  from cycode.cli import consts
6
5
  from cycode.cli.exceptions import custom_exceptions
@@ -20,7 +19,7 @@ def _validate_zip_file_size(scan_type: str, zip_file_size: int) -> None:
20
19
  def zip_documents(
21
20
  scan_type: str,
22
21
  documents: list[Document],
23
- zip_file: Optional[InMemoryZip] = None,
22
+ zip_file: InMemoryZip | None = None,
24
23
  ) -> InMemoryZip:
25
24
  if zip_file is None:
26
25
  zip_file = InMemoryZip()
cycode/cli/models.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import NamedTuple, Optional
2
+ from typing import NamedTuple
3
3
 
4
4
  from cycode.cyclient.models import Detection
5
5
 
@@ -10,8 +10,8 @@ class Document:
10
10
  path: str,
11
11
  content: str,
12
12
  is_git_diff_format: bool = False,
13
- unique_id: Optional[str] = None,
14
- absolute_path: Optional[str] = None,
13
+ unique_id: str | None = None,
14
+ absolute_path: str | None = None,
15
15
  ) -> None:
16
16
  self.path = path
17
17
  self.content = content
@@ -48,25 +48,25 @@ CliErrors = dict[type[BaseException], CliError]
48
48
  class CliResult(NamedTuple):
49
49
  success: bool
50
50
  message: str
51
- data: Optional[dict[str, any]] = None
51
+ data: dict[str, any] | None = None
52
52
 
53
53
 
54
54
  class LocalScanResult(NamedTuple):
55
55
  scan_id: str
56
- report_url: Optional[str]
56
+ report_url: str | None
57
57
  document_detections: list[DocumentDetections]
58
58
  issue_detected: bool
59
59
  detections_count: int
60
60
  relevant_detections_count: int
61
- verdict: Optional[str] = None
61
+ verdict: str | None = None
62
62
 
63
63
 
64
64
  @dataclass
65
65
  class ResourceChange:
66
- module_address: Optional[str]
66
+ module_address: str | None
67
67
  resource_type: str
68
68
  name: str
69
- index: Optional[int]
69
+ index: int | None
70
70
  actions: list[str]
71
71
  values: dict[str, str]
72
72
 
@@ -1,5 +1,5 @@
1
1
  import io
2
- from typing import TYPE_CHECKING, ClassVar, Optional
2
+ from typing import TYPE_CHECKING, ClassVar
3
3
 
4
4
  import typer
5
5
  from rich.console import Console
@@ -35,18 +35,18 @@ class ConsolePrinter:
35
35
  def __init__(
36
36
  self,
37
37
  ctx: typer.Context,
38
- console_override: Optional['Console'] = None,
39
- console_err_override: Optional['Console'] = None,
40
- output_type_override: Optional[str] = None,
38
+ console_override: 'Console | None' = None,
39
+ console_err_override: 'Console | None' = None,
40
+ output_type_override: str | None = None,
41
41
  ) -> None:
42
42
  self.ctx = ctx
43
43
  self.console = console_override or console
44
44
  self.console_err = console_err_override or console_err
45
45
  self.output_type = output_type_override or self.ctx.obj.get('output')
46
46
 
47
- self.export_type: Optional[str] = None
48
- self.export_file: Optional[Path] = None
49
- self.console_record: Optional[ConsolePrinter] = None
47
+ self.export_type: str | None = None
48
+ self.export_file: Path | None = None
49
+ self.console_record: ConsolePrinter | None = None
50
50
 
51
51
  @property
52
52
  def scan_type(self) -> str:
@@ -87,7 +87,7 @@ class ConsolePrinter:
87
87
  def print_scan_results(
88
88
  self,
89
89
  local_scan_results: list['LocalScanResult'],
90
- errors: Optional[dict[str, 'CliError']] = None,
90
+ errors: dict[str, 'CliError'] | None = None,
91
91
  ) -> None:
92
92
  if self.console_record:
93
93
  self.console_record.print_scan_results(local_scan_results, errors)
@@ -103,7 +103,7 @@ class ConsolePrinter:
103
103
  self.console_record.print_error(error)
104
104
  self.printer.print_error(error)
105
105
 
106
- def print_exception(self, e: Optional[BaseException] = None, force_print: bool = False) -> None:
106
+ def print_exception(self, e: BaseException | None = None, force_print: bool = False) -> None:
107
107
  """Print traceback message in stderr if verbose mode is set."""
108
108
  if force_print or self.ctx.obj.get('verbose', False):
109
109
  if self.console_record:
@@ -1,5 +1,5 @@
1
1
  import json
2
- from typing import TYPE_CHECKING, Optional
2
+ from typing import TYPE_CHECKING
3
3
 
4
4
  from cycode.cli.models import CliError, CliResult
5
5
  from cycode.cli.printers.printer_base import PrinterBase
@@ -21,7 +21,7 @@ class JsonPrinter(PrinterBase):
21
21
  self.console.print_json(self.get_data_json(result))
22
22
 
23
23
  def print_scan_results(
24
- self, local_scan_results: list['LocalScanResult'], errors: Optional[dict[str, 'CliError']] = None
24
+ self, local_scan_results: list['LocalScanResult'], errors: dict[str, 'CliError'] | None = None
25
25
  ) -> None:
26
26
  scan_ids = []
27
27
  report_urls = []
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
  from abc import ABC, abstractmethod
3
3
  from collections import defaultdict
4
- from typing import TYPE_CHECKING, Optional
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  import typer
7
7
 
@@ -51,7 +51,7 @@ class PrinterBase(ABC):
51
51
 
52
52
  @abstractmethod
53
53
  def print_scan_results(
54
- self, local_scan_results: list['LocalScanResult'], errors: Optional[dict[str, 'CliError']] = None
54
+ self, local_scan_results: list['LocalScanResult'], errors: dict[str, 'CliError'] | None = None
55
55
  ) -> None:
56
56
  pass
57
57
 
@@ -63,7 +63,7 @@ class PrinterBase(ABC):
63
63
  def print_error(self, error: CliError) -> None:
64
64
  pass
65
65
 
66
- def print_exception(self, e: Optional[BaseException] = None) -> None:
66
+ def print_exception(self, e: BaseException | None = None) -> None:
67
67
  """We are printing it in stderr so, we don't care about supporting JSON and TABLE outputs.
68
68
 
69
69
  Note:
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Optional
1
+ from typing import TYPE_CHECKING
2
2
 
3
3
  from rich.console import Group
4
4
  from rich.panel import Panel
@@ -26,7 +26,7 @@ class RichPrinter(TextPrinter):
26
26
  MAX_PATH_LENGTH = 60
27
27
 
28
28
  def print_scan_results(
29
- self, local_scan_results: list['LocalScanResult'], errors: Optional[dict[str, 'CliError']] = None
29
+ self, local_scan_results: list['LocalScanResult'], errors: dict[str, 'CliError'] | None = None
30
30
  ) -> None:
31
31
  if not errors and all(result.issue_detected == 0 for result in local_scan_results):
32
32
  self.console.print(self.NO_DETECTIONS_MESSAGE)
@@ -1,5 +1,5 @@
1
1
  import urllib.parse
2
- from typing import TYPE_CHECKING, Optional
2
+ from typing import TYPE_CHECKING
3
3
 
4
4
  from rich.markup import escape
5
5
  from rich.table import Table as RichTable
@@ -11,7 +11,7 @@ if TYPE_CHECKING:
11
11
  class Table:
12
12
  """Helper class to manage columns and their values in the right order and only if the column should be presented."""
13
13
 
14
- def __init__(self, column_infos: Optional[list['ColumnInfo']] = None) -> None:
14
+ def __init__(self, column_infos: list['ColumnInfo'] | None = None) -> None:
15
15
  self._group_separator_indexes: set[int] = set()
16
16
 
17
17
  self._columns: dict[ColumnInfo, list[str]] = {}
@@ -26,7 +26,7 @@ class Table:
26
26
  if column in self._columns:
27
27
  self._columns[column].append(value)
28
28
 
29
- def add_cell(self, column: 'ColumnInfo', value: str, color: Optional[str] = None) -> None:
29
+ def add_cell(self, column: 'ColumnInfo', value: str, color: str | None = None) -> None:
30
30
  if color:
31
31
  value = f'[{color}]{value}[/]'
32
32
 
@@ -49,7 +49,7 @@ class Table:
49
49
 
50
50
  def get_rows(self) -> list[str]:
51
51
  column_values = [self._columns[column_info] for column_info in self._get_ordered_columns()]
52
- return list(zip(*column_values))
52
+ return list(zip(*column_values, strict=False))
53
53
 
54
54
  def get_table(self) -> 'RichTable':
55
55
  table = RichTable(expand=True, highlight=True)
@@ -1,4 +1,4 @@
1
- from typing import NamedTuple, Optional
1
+ from typing import NamedTuple
2
2
 
3
3
 
4
4
  class ColumnInfoBuilder:
@@ -14,7 +14,7 @@ class ColumnInfoBuilder:
14
14
  class ColumnInfo(NamedTuple):
15
15
  name: str
16
16
  index: int # Represents the order of the columns, starting from the left
17
- column_opts: Optional[dict] = None
17
+ column_opts: dict | None = None
18
18
 
19
19
  def __hash__(self) -> int:
20
20
  return hash((self.name, self.index))
@@ -1,5 +1,5 @@
1
1
  import abc
2
- from typing import TYPE_CHECKING, Optional
2
+ from typing import TYPE_CHECKING
3
3
 
4
4
  from cycode.cli.models import CliError, CliResult
5
5
  from cycode.cli.printers.printer_base import PrinterBase
@@ -22,7 +22,7 @@ class TablePrinterBase(PrinterBase, abc.ABC):
22
22
  self.text_printer.print_error(error)
23
23
 
24
24
  def print_scan_results(
25
- self, local_scan_results: list['LocalScanResult'], errors: Optional[dict[str, 'CliError']] = None
25
+ self, local_scan_results: list['LocalScanResult'], errors: dict[str, 'CliError'] | None = None
26
26
  ) -> None:
27
27
  if not errors and all(result.issue_detected == 0 for result in local_scan_results):
28
28
  self.console.print(self.NO_DETECTIONS_MESSAGE)
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Optional
1
+ from typing import TYPE_CHECKING
2
2
 
3
3
  from cycode.cli import consts
4
4
  from cycode.cli.cli_types import SeverityOption
@@ -32,7 +32,7 @@ class TextPrinter(PrinterBase):
32
32
  self.console.print(f'[red]Error: {error.message}[/]', highlight=False)
33
33
 
34
34
  def print_scan_results(
35
- self, local_scan_results: list['LocalScanResult'], errors: Optional[dict[str, 'CliError']] = None
35
+ self, local_scan_results: list['LocalScanResult'], errors: dict[str, 'CliError'] | None = None
36
36
  ) -> None:
37
37
  if not errors and all(result.issue_detected == 0 for result in local_scan_results):
38
38
  self.console.print(self.NO_DETECTIONS_MESSAGE)
@@ -97,7 +97,7 @@ class TextPrinter(PrinterBase):
97
97
  )
98
98
 
99
99
  def print_report_urls_and_errors(
100
- self, local_scan_results: list['LocalScanResult'], errors: Optional[dict[str, 'CliError']] = None
100
+ self, local_scan_results: list['LocalScanResult'], errors: dict[str, 'CliError'] | None = None
101
101
  ) -> None:
102
102
  report_urls = [scan_result.report_url for scan_result in local_scan_results if scan_result.report_url]
103
103
 
@@ -110,7 +110,7 @@ class TextPrinter(PrinterBase):
110
110
  self.console.print(f'- {scan_id}: ', end='')
111
111
  self.print_error(error)
112
112
 
113
- def print_report_urls(self, report_urls: list[str], aggregation_report_url: Optional[str] = None) -> None:
113
+ def print_report_urls(self, report_urls: list[str], aggregation_report_url: str | None = None) -> None:
114
114
  if not report_urls and not aggregation_report_url:
115
115
  return
116
116
 
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
14
14
 
15
15
  def _get_code_segment_start_line(detection_line: int, lines_to_display_before: int) -> int:
16
16
  start_line = detection_line - lines_to_display_before
17
- return 0 if start_line < 0 else start_line
17
+ return max(start_line, 0)
18
18
 
19
19
 
20
20
  def get_detection_line(scan_type: str, detection: 'Detection') -> int:
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import TYPE_CHECKING, Optional
2
+ from typing import TYPE_CHECKING
3
3
 
4
4
  from cycode.cli import consts
5
5
 
@@ -7,7 +7,7 @@ if TYPE_CHECKING:
7
7
  from cycode.cyclient.models import Detection
8
8
 
9
9
 
10
- def get_cwe_cve_link(cwe_cve: Optional[str]) -> Optional[str]:
10
+ def get_cwe_cve_link(cwe_cve: str | None) -> str | None:
11
11
  if not cwe_cve:
12
12
  return None
13
13
 
@@ -63,7 +63,7 @@ def get_detection_clickable_cwe_cve(scan_type: str, detection: 'Detection') -> s
63
63
  return ''
64
64
 
65
65
 
66
- def get_detection_cwe_cve(scan_type: str, detection: 'Detection') -> Optional[str]:
66
+ def get_detection_cwe_cve(scan_type: str, detection: 'Detection') -> str | None:
67
67
  if scan_type == consts.SCA_SCAN_TYPE:
68
68
  return detection.detection_details.get('vulnerability_id')
69
69
  if scan_type == consts.SAST_SCAN_TYPE:
@@ -1,4 +1,4 @@
1
- from typing import Any, Optional
1
+ from typing import Any
2
2
 
3
3
  _MAINTAINED_CHECK_NAME = 'maintained'
4
4
 
@@ -7,15 +7,15 @@ def _get_ossf_details(detection_details: dict) -> dict:
7
7
  return detection_details.get('ossf') or {}
8
8
 
9
9
 
10
- def get_ossf_score(detection_details: dict) -> Optional[Any]:
10
+ def get_ossf_score(detection_details: dict) -> Any | None:
11
11
  return _get_ossf_details(detection_details).get('score')
12
12
 
13
13
 
14
- def get_ossf_report_url(detection_details: dict) -> Optional[str]:
14
+ def get_ossf_report_url(detection_details: dict) -> str | None:
15
15
  return _get_ossf_details(detection_details).get('scorecard_report_url')
16
16
 
17
17
 
18
- def get_maintained_score(detection_details: dict) -> Optional[Any]:
18
+ def get_maintained_score(detection_details: dict) -> Any | None:
19
19
  for check in _get_ossf_details(detection_details).get('checks') or []:
20
20
  if str(check.get('name', '')).lower() == _MAINTAINED_CHECK_NAME:
21
21
  return check.get('score')
@@ -1,4 +1,5 @@
1
- from typing import TYPE_CHECKING, Callable
1
+ from collections.abc import Callable
2
+ from typing import TYPE_CHECKING
2
3
 
3
4
  from cycode.cli.consts import (
4
5
  LICENSE_COMPLIANCE_POLICY_ID,