cycode 3.25.2.dev1__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 (123) 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/_plugin_utils.py +6 -5
  7. cycode/cli/apps/ai_guardrails/ides/_skill_utils.py +1 -2
  8. cycode/cli/apps/ai_guardrails/ides/base.py +9 -11
  9. cycode/cli/apps/ai_guardrails/ides/claude_code.py +14 -14
  10. cycode/cli/apps/ai_guardrails/ides/codex.py +11 -11
  11. cycode/cli/apps/ai_guardrails/ides/copilot.py +7 -7
  12. cycode/cli/apps/ai_guardrails/ides/cursor.py +5 -5
  13. cycode/cli/apps/ai_guardrails/install_command.py +3 -3
  14. cycode/cli/apps/ai_guardrails/scan/guardrail_config.py +10 -11
  15. cycode/cli/apps/ai_guardrails/scan/handlers.py +15 -14
  16. cycode/cli/apps/ai_guardrails/scan/mcp_server_status.py +6 -6
  17. cycode/cli/apps/ai_guardrails/scan/payload.py +13 -14
  18. cycode/cli/apps/ai_guardrails/scan/policy.py +3 -3
  19. cycode/cli/apps/ai_guardrails/scan/scan_command.py +4 -4
  20. cycode/cli/apps/ai_guardrails/session_start_command.py +8 -8
  21. cycode/cli/apps/ai_guardrails/status_command.py +2 -2
  22. cycode/cli/apps/ai_guardrails/uninstall_command.py +2 -2
  23. cycode/cli/apps/api/__init__.py +3 -3
  24. cycode/cli/apps/api/api_command.py +6 -6
  25. cycode/cli/apps/api/openapi_spec.py +4 -5
  26. cycode/cli/apps/auth/auth_common.py +4 -4
  27. cycode/cli/apps/configure/configure_command.py +2 -4
  28. cycode/cli/apps/configure/messages.py +2 -4
  29. cycode/cli/apps/configure/prompts.py +5 -7
  30. cycode/cli/apps/ignore/ignore_command.py +7 -7
  31. cycode/cli/apps/mcp/mcp_command.py +11 -11
  32. cycode/cli/apps/report/sbom/common.py +5 -5
  33. cycode/cli/apps/report/sbom/sbom_command.py +2 -2
  34. cycode/cli/apps/report/sbom/sbom_report_file.py +1 -2
  35. cycode/cli/apps/report_import/sbom/sbom_command.py +3 -3
  36. cycode/cli/apps/sca_options.py +3 -3
  37. cycode/cli/apps/scan/aggregation_report.py +4 -4
  38. cycode/cli/apps/scan/code_scanner.py +8 -7
  39. cycode/cli/apps/scan/commit_range_scanner.py +10 -10
  40. cycode/cli/apps/scan/detection_excluder.py +2 -4
  41. cycode/cli/apps/scan/pre_commit/pre_commit_command.py +3 -3
  42. cycode/cli/apps/scan/pre_push/pre_push_command.py +3 -3
  43. cycode/cli/apps/scan/pre_receive/pre_receive_command.py +2 -2
  44. cycode/cli/apps/scan/remote_url_resolver.py +6 -8
  45. cycode/cli/apps/scan/repository/repository_command.py +2 -2
  46. cycode/cli/apps/scan/scan_command.py +2 -2
  47. cycode/cli/apps/scan/scan_parameters.py +1 -3
  48. cycode/cli/apps/scan/scan_result.py +5 -5
  49. cycode/cli/console.py +2 -2
  50. cycode/cli/exceptions/custom_exceptions.py +1 -1
  51. cycode/cli/exceptions/handle_errors.py +1 -3
  52. cycode/cli/exceptions/handle_scan_errors.py +1 -2
  53. cycode/cli/files_collector/commit_range_documents.py +14 -14
  54. cycode/cli/files_collector/models/in_memory_zip.py +3 -3
  55. cycode/cli/files_collector/repository_documents.py +3 -3
  56. cycode/cli/files_collector/sca/base_restore_dependencies.py +7 -8
  57. cycode/cli/files_collector/sca/go/restore_go_dependencies.py +1 -2
  58. cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +2 -3
  59. cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +3 -4
  60. cycode/cli/files_collector/sca/npm/restore_bun_dependencies.py +3 -4
  61. cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +1 -2
  62. cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +2 -3
  63. cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +2 -3
  64. cycode/cli/files_collector/sca/npm/workspace/coverage.py +5 -5
  65. cycode/cli/files_collector/sca/npm/workspace/files.py +2 -3
  66. cycode/cli/files_collector/sca/npm/workspace/globs.py +1 -2
  67. cycode/cli/files_collector/sca/npm/workspace/resolvers.py +14 -16
  68. cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +1 -2
  69. cycode/cli/files_collector/sca/python/restore_pip_dependencies.py +2 -3
  70. cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +1 -2
  71. cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +2 -3
  72. cycode/cli/files_collector/sca/python/restore_uv_dependencies.py +2 -3
  73. cycode/cli/files_collector/sca/sca_file_collector.py +5 -5
  74. cycode/cli/files_collector/zip_documents.py +1 -2
  75. cycode/cli/models.py +8 -8
  76. cycode/cli/printers/console_printer.py +9 -9
  77. cycode/cli/printers/json_printer.py +2 -2
  78. cycode/cli/printers/printer_base.py +3 -3
  79. cycode/cli/printers/rich_printer.py +2 -2
  80. cycode/cli/printers/tables/table.py +4 -4
  81. cycode/cli/printers/tables/table_models.py +2 -2
  82. cycode/cli/printers/tables/table_printer_base.py +2 -2
  83. cycode/cli/printers/text_printer.py +4 -4
  84. cycode/cli/printers/utils/code_snippet_syntax.py +1 -1
  85. cycode/cli/printers/utils/detection_data.py +3 -3
  86. cycode/cli/printers/utils/sca_ossf.py +4 -4
  87. cycode/cli/printers/utils/sca_policy_details.py +2 -1
  88. cycode/cli/user_settings/config_file_manager.py +10 -10
  89. cycode/cli/user_settings/configuration_manager.py +6 -6
  90. cycode/cli/user_settings/credentials_manager.py +5 -6
  91. cycode/cli/utils/get_api_client.py +5 -5
  92. cycode/cli/utils/git_proxy.py +8 -8
  93. cycode/cli/utils/host_info.py +21 -38
  94. cycode/cli/utils/ignore_utils.py +13 -15
  95. cycode/cli/utils/jwt_utils.py +2 -4
  96. cycode/cli/utils/path_utils.py +6 -6
  97. cycode/cli/utils/progress_bar.py +4 -4
  98. cycode/cli/utils/scan_batch.py +2 -1
  99. cycode/cli/utils/scan_utils.py +2 -2
  100. cycode/cli/utils/shell_executor.py +3 -4
  101. cycode/cli/utils/task_timer.py +4 -4
  102. cycode/cli/utils/trust_store.py +0 -17
  103. cycode/cli/utils/url_utils.py +1 -2
  104. cycode/cli/utils/version_checker.py +4 -5
  105. cycode/cli/utils/yaml_utils.py +1 -1
  106. cycode/config.py +1 -2
  107. cycode/cyclient/ai_security_manager_client.py +17 -17
  108. cycode/cyclient/auth_client.py +2 -4
  109. cycode/cyclient/base_token_auth_client.py +2 -2
  110. cycode/cyclient/client_creator.py +4 -6
  111. cycode/cyclient/cycode_client_base.py +10 -9
  112. cycode/cyclient/cycode_dev_based_client.py +1 -3
  113. cycode/cyclient/headers.py +1 -2
  114. cycode/cyclient/import_sbom_client.py +3 -4
  115. cycode/cyclient/models.py +33 -33
  116. cycode/cyclient/report_client.py +1 -2
  117. cycode/cyclient/scan_client.py +8 -7
  118. cycode/logger.py +4 -4
  119. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/METADATA +12 -19
  120. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/RECORD +123 -123
  121. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/WHEEL +1 -1
  122. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/entry_points.txt +0 -0
  123. {cycode-3.25.2.dev1.dist-info → cycode-3.25.2.dev2.dist-info}/licenses/LICENCE +0 -0
@@ -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
 
@@ -26,8 +26,8 @@ logger = get_logger('Scan Results')
26
26
 
27
27
 
28
28
  def _get_document_by_file_name(
29
- documents: list[Document], file_name: str, unique_id: Optional[str] = None
30
- ) -> Optional[Document]:
29
+ documents: list[Document], file_name: str, unique_id: str | None = None
30
+ ) -> Document | None:
31
31
  normalized_file_name = normalize_file_path(file_name)
32
32
  for document in documents:
33
33
  if normalize_file_path(document.path) == normalized_file_name and document.unique_id == unique_id:
@@ -132,7 +132,7 @@ def _map_detections_per_file_and_commit_id(scan_type: str, raw_detections: list[
132
132
 
133
133
  file_name = _get_file_name_from_detection(scan_type, raw_detection)
134
134
  detection: Detection = DetectionSchema().load(raw_detection)
135
- commit_id: Optional[str] = detection.detection_details.get('commit_id') # could be None
135
+ commit_id: str | None = detection.detection_details.get('commit_id') # could be None
136
136
  group_by_key = (file_name, commit_id)
137
137
 
138
138
  if group_by_key in detections_per_files:
@@ -187,7 +187,7 @@ def get_sync_scan_result(scan_type: str, scan_results: 'ScanResultsSyncFlow') ->
187
187
 
188
188
 
189
189
  def print_local_scan_results(
190
- ctx: typer.Context, local_scan_results: list[LocalScanResult], errors: Optional[dict[str, 'CliError']] = None
190
+ ctx: typer.Context, local_scan_results: list[LocalScanResult], errors: dict[str, 'CliError'] | None = None
191
191
  ) -> None:
192
192
  printer = ctx.obj.get('console_printer')
193
193
  printer.update_ctx(ctx)
cycode/cli/console.py CHANGED
@@ -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
  from rich.console import Console, RenderResult
5
5
  from rich.markdown import Heading, Markdown
@@ -14,7 +14,7 @@ console_err = Console(stderr=True)
14
14
  console = console_out # alias
15
15
 
16
16
 
17
- def is_dark_console() -> Optional[bool]:
17
+ def is_dark_console() -> bool | None:
18
18
  """Detect if the console is dark or light.
19
19
 
20
20
  This function checks the environment variables and terminal type to determine if the console is dark or light.
@@ -129,7 +129,7 @@ _SSL_ERROR_TRUST_HINT = (
129
129
  'If you use an on-premises installation or a proxy that intercepts SSL traffic, '
130
130
  f'set {consts.ENABLE_TRUSTSTORE_ENV_VAR_NAME}=1 to trust the CA certificates installed in your '
131
131
  f'machine certificate store, or {_SSL_ERROR_CA_BUNDLE_HINT}'
132
- if trust_store.is_supported() and not trust_store.is_enabled()
132
+ if not trust_store.is_enabled()
133
133
  else f'If you use an on-premises installation or a proxy that intercepts SSL traffic, {_SSL_ERROR_CA_BUNDLE_HINT}'
134
134
  )
135
135
 
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  import click
4
2
  import typer
5
3
 
@@ -8,7 +6,7 @@ from cycode.cli.models import CliError, CliErrors
8
6
 
9
7
  def handle_errors(
10
8
  ctx: typer.Context, err: BaseException, cli_errors: CliErrors, *, return_exception: bool = False
11
- ) -> Optional['CliError']:
9
+ ) -> CliError | None:
12
10
  printer = ctx.obj.get('console_printer')
13
11
  printer.print_exception(err)
14
12
 
@@ -1,5 +1,4 @@
1
1
  import zipfile
2
- from typing import Optional
3
2
 
4
3
  import typer
5
4
 
@@ -10,7 +9,7 @@ from cycode.cli.models import CliError, CliErrors
10
9
  from cycode.cli.utils.git_proxy import git_proxy
11
10
 
12
11
 
13
- def handle_scan_exception(ctx: typer.Context, err: Exception, *, return_exception: bool = False) -> Optional[CliError]:
12
+ def handle_scan_exception(ctx: typer.Context, err: Exception, *, return_exception: bool = False) -> CliError | None:
14
13
  ctx.obj['did_fail'] = True
15
14
 
16
15
  errors: CliErrors = {
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import sys
3
- from typing import TYPE_CHECKING, Optional
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  import typer
6
6
 
@@ -47,7 +47,7 @@ def get_safe_head_reference_for_diff(repo: 'Repo') -> str:
47
47
  return consts.GIT_EMPTY_TREE_OBJECT
48
48
 
49
49
 
50
- def get_staged_diff_index(repo: 'Repo', paths: Optional[list[str]] = None) -> tuple[str, 'DiffIndex']:
50
+ def get_staged_diff_index(repo: 'Repo', paths: list[str] | None = None) -> tuple[str, 'DiffIndex']:
51
51
  """Diff the index against HEAD, or against the empty tree in repositories with no commits.
52
52
 
53
53
  GitPython only inverts the `R` flag for HEAD, so `R` must be off for the empty tree to keep
@@ -65,7 +65,7 @@ def get_staged_diff_index(repo: 'Repo', paths: Optional[list[str]] = None) -> tu
65
65
  return head_reference, repo.index.diff(head_reference, create_patch=True, R=reverse, paths=paths or None)
66
66
 
67
67
 
68
- def _does_reach_to_max_commits_to_scan_limit(commit_ids: list[str], max_commits_count: Optional[int]) -> bool:
68
+ def _does_reach_to_max_commits_to_scan_limit(commit_ids: list[str], max_commits_count: int | None) -> bool:
69
69
  if max_commits_count is None:
70
70
  return False
71
71
 
@@ -73,7 +73,7 @@ def _does_reach_to_max_commits_to_scan_limit(commit_ids: list[str], max_commits_
73
73
 
74
74
 
75
75
  def collect_commit_range_diff_documents(
76
- ctx: typer.Context, path: str, commit_range: str, max_commits_count: Optional[int] = None
76
+ ctx: typer.Context, path: str, commit_range: str, max_commits_count: int | None = None
77
77
  ) -> list[Document]:
78
78
  """Collects documents from a specified commit range in a Git repository.
79
79
 
@@ -138,7 +138,7 @@ def collect_commit_range_diff_documents(
138
138
  return commit_documents_to_scan
139
139
 
140
140
 
141
- def calculate_pre_receive_commit_range(repo_path: str, branch_update_details: str) -> Optional[str]:
141
+ def calculate_pre_receive_commit_range(repo_path: str, branch_update_details: str) -> str | None:
142
142
  end_commit = _get_end_commit_from_branch_update_details(branch_update_details)
143
143
 
144
144
  # branch is deleted, no need to perform scan
@@ -168,7 +168,7 @@ def _get_end_commit_from_branch_update_details(update_details: str) -> str:
168
168
  return end_commit
169
169
 
170
170
 
171
- def _get_oldest_unupdated_commit_for_branch(repo: 'Repo', commit: str) -> Optional[str]:
171
+ def _get_oldest_unupdated_commit_for_branch(repo: 'Repo', commit: str) -> str | None:
172
172
  # get a list of commits by chronological order that are not in the remote repository yet
173
173
  # more info about rev-list command: https://git-scm.com/docs/git-rev-list
174
174
 
@@ -181,12 +181,12 @@ def _get_oldest_unupdated_commit_for_branch(repo: 'Repo', commit: str) -> Option
181
181
  return commits[0]
182
182
 
183
183
 
184
- def _get_file_content_from_commit_diff(repo: 'Repo', commit: str, diff: 'Diff') -> Optional[str]:
184
+ def _get_file_content_from_commit_diff(repo: 'Repo', commit: str, diff: 'Diff') -> str | None:
185
185
  file_path = get_diff_file_path(diff, relative=True)
186
186
  return get_file_content_from_commit_path(repo, commit, file_path)
187
187
 
188
188
 
189
- def _get_blob_content(blob: Optional['Blob']) -> Optional[str]:
189
+ def _get_blob_content(blob: 'Blob | None') -> str | None:
190
190
  """Read a diff-side blob's content in-process, with no subprocess spawn.
191
191
 
192
192
  `diff.a_blob`/`diff.b_blob` already hold whatever git loaded to compute the patch, so this is
@@ -266,7 +266,7 @@ def parse_pre_receive_input() -> str:
266
266
  return pre_receive_input.splitlines()[0]
267
267
 
268
268
 
269
- def parse_pre_push_input() -> Optional[str]:
269
+ def parse_pre_push_input() -> str | None:
270
270
  """Parse input to pre-push hook details.
271
271
 
272
272
  Example input:
@@ -289,7 +289,7 @@ def is_invoked_by_pre_commit_framework() -> bool:
289
289
  return os.getenv(consts.PRE_COMMIT_FRAMEWORK_ENV_VAR_NAME) == '1'
290
290
 
291
291
 
292
- def get_pre_commit_framework_push_range() -> Optional[str]:
292
+ def get_pre_commit_framework_push_range() -> str | None:
293
293
  """Get the commit range to scan when invoked as a pre-push hook by the pre-commit framework.
294
294
 
295
295
  The pre-commit framework reads git's pre-push stdin itself and never forwards it to hooks.
@@ -379,7 +379,7 @@ def _get_default_branches_for_merge_base(repo: 'Repo') -> list[str]:
379
379
  return default_branches
380
380
 
381
381
 
382
- def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]:
382
+ def calculate_pre_push_commit_range(push_update_details: str) -> str | None:
383
383
  """Calculate the commit range for pre-push hook scanning.
384
384
 
385
385
  Args:
@@ -430,7 +430,7 @@ def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]:
430
430
  return f'{remote_object_name}..{local_object_name}'
431
431
 
432
432
 
433
- def get_diff_file_path(diff: 'Diff', relative: bool = False, repo: Optional['Repo'] = None) -> Optional[str]:
433
+ def get_diff_file_path(diff: 'Diff', relative: bool = False, repo: 'Repo | None' = None) -> str | None:
434
434
  """Get the file path from a git Diff object.
435
435
 
436
436
  Args:
@@ -470,7 +470,7 @@ def get_pre_commit_modified_documents(
470
470
  repo_path: str,
471
471
  base_ref: str = consts.GIT_HEAD_COMMIT_REV,
472
472
  include_unstaged: bool = False,
473
- paths: Optional[list[str]] = None,
473
+ paths: list[str] | None = None,
474
474
  collect_file_contents: bool = True,
475
475
  ) -> tuple[list[Document], list[Document], list[Document]]:
476
476
  """Diffs `base_ref` against the staged index (default) or the full working tree.
@@ -562,7 +562,7 @@ def get_pre_commit_modified_documents(
562
562
  return from_ref_documents, working_copy_documents, diff_documents
563
563
 
564
564
 
565
- def parse_commit_range(commit_range: str, path: str) -> tuple[Optional[str], Optional[str], Optional[str]]:
565
+ def parse_commit_range(commit_range: str, path: str) -> tuple[str | None, str | None, str | None]:
566
566
  """Parses a git commit range string and returns the full SHAs for the 'from' and 'to' commits.
567
567
  Also, it returns the separator in the commit range.
568
568
 
@@ -3,7 +3,7 @@ import tempfile
3
3
  from collections import defaultdict
4
4
  from os import SEEK_END
5
5
  from pathlib import Path
6
- from typing import IO, Optional
6
+ from typing import IO
7
7
  from zipfile import ZIP_DEFLATED, ZipFile
8
8
 
9
9
  from cycode.cli import consts
@@ -17,7 +17,7 @@ logger = get_logger('ZIP')
17
17
  _SPOOL_DIRECTORY_NAME = 'tmp'
18
18
 
19
19
 
20
- def _get_spool_directory(configuration_manager: ConfigurationManager) -> Optional[str]:
20
+ def _get_spool_directory(configuration_manager: ConfigurationManager) -> str | None:
21
21
  """Directory to spill big ZIPs into. None falls back to the system temp directory."""
22
22
  try:
23
23
  directory = Path(configuration_manager.global_config_file_manager.get_config_directory_path())
@@ -47,7 +47,7 @@ class InMemoryZip:
47
47
  self._files_count = 0
48
48
  self._extension_statistics = defaultdict(int)
49
49
 
50
- def append(self, filename: str, unique_id: Optional[str], content: str) -> None:
50
+ def append(self, filename: str, unique_id: str | None, content: str) -> None:
51
51
  self._files_count += 1
52
52
  self._extension_statistics[Path(filename).suffix] += 1
53
53
 
@@ -1,5 +1,5 @@
1
1
  from collections.abc import Iterator
2
- from typing import TYPE_CHECKING, Optional, Union
2
+ from typing import TYPE_CHECKING
3
3
 
4
4
  from cycode.cli.utils.git_proxy import git_proxy
5
5
 
@@ -15,11 +15,11 @@ def _should_process_git_object(obj: 'Blob', _: int) -> bool:
15
15
 
16
16
  def get_git_repository_tree_file_entries(
17
17
  path: str, branch: str
18
- ) -> Union[Iterator['IndexObjUnion'], Iterator['TraversedTreeTup']]:
18
+ ) -> Iterator['IndexObjUnion'] | Iterator['TraversedTreeTup']:
19
19
  return git_proxy.get_repo(path).tree(branch).traverse(predicate=_should_process_git_object)
20
20
 
21
21
 
22
- def get_file_content_from_commit_path(repo: 'Repo', commit: str, file_path: str) -> Optional[str]:
22
+ def get_file_content_from_commit_path(repo: 'Repo', commit: str, file_path: str) -> str | None:
23
23
  try:
24
24
  return repo.git.show(f'{commit}:{file_path}')
25
25
  except git_proxy.get_git_command_error():
@@ -1,6 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from pathlib import Path
3
- from typing import Optional
4
3
 
5
4
  import typer
6
5
 
@@ -19,9 +18,9 @@ def build_dep_tree_path(path: str, generated_file_name: str) -> str:
19
18
  def execute_commands(
20
19
  commands: list[list[str]],
21
20
  timeout: int,
22
- output_file_path: Optional[str] = None,
23
- working_directory: Optional[str] = None,
24
- ) -> Optional[str]:
21
+ output_file_path: str | None = None,
22
+ working_directory: str | None = None,
23
+ ) -> str | None:
25
24
  logger.debug(
26
25
  'Executing restore commands, %s',
27
26
  {
@@ -67,7 +66,7 @@ class BaseRestoreDependencies(ABC):
67
66
  self.command_timeout = command_timeout
68
67
  self.create_output_file_manually = create_output_file_manually
69
68
 
70
- def restore(self, document: Document) -> Optional[Document]:
69
+ def restore(self, document: Document) -> Document | None:
71
70
  return self.try_restore_dependencies(document)
72
71
 
73
72
  def get_manifest_file_path(self, document: Document) -> str:
@@ -75,7 +74,7 @@ class BaseRestoreDependencies(ABC):
75
74
  join_paths(get_path_from_context(self.ctx), document.path) if self.ctx.obj.get('monitor') else document.path
76
75
  )
77
76
 
78
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
77
+ def try_restore_dependencies(self, document: Document) -> Document | None:
79
78
  manifest_file_path = self.get_manifest_file_path(document)
80
79
  restore_file_paths = [
81
80
  build_dep_tree_path(document.absolute_path, restore_file_path_item)
@@ -122,7 +121,7 @@ class BaseRestoreDependencies(ABC):
122
121
 
123
122
  return Document(relative_restore_file_path, restore_file_content, self.is_git_diff)
124
123
 
125
- def get_manifest_dir(self, document: Document) -> Optional[str]:
124
+ def get_manifest_dir(self, document: Document) -> str | None:
126
125
  """Return the directory containing the manifest file, resolving monitor-mode paths.
127
126
 
128
127
  Uses the same path resolution as get_manifest_file_path() to ensure consistency.
@@ -143,7 +142,7 @@ class BaseRestoreDependencies(ABC):
143
142
 
144
143
  return None
145
144
 
146
- def get_working_directory(self, document: Document) -> Optional[str]:
145
+ def get_working_directory(self, document: Document) -> str | None:
147
146
  return str(Path(document.absolute_path).parent)
148
147
 
149
148
  def get_restored_lock_file_name(self, restore_file_path: str) -> str:
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from typing import Optional
3
2
 
4
3
  import typer
5
4
 
@@ -19,7 +18,7 @@ class RestoreGoDependencies(BaseRestoreDependencies):
19
18
  def __init__(self, ctx: typer.Context, is_git_diff: bool, command_timeout: int) -> None:
20
19
  super().__init__(ctx, is_git_diff, command_timeout, create_output_file_manually=True)
21
20
 
22
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
21
+ def try_restore_dependencies(self, document: Document) -> Document | None:
23
22
  manifest_exists = (Path(self.get_working_directory(document)) / BUILD_GO_FILE_NAME).is_file()
24
23
  lock_exists = (Path(self.get_working_directory(document)) / BUILD_GO_LOCK_FILE_NAME).is_file()
25
24
 
@@ -1,7 +1,6 @@
1
1
  import os
2
2
  import platform
3
3
  import re
4
- from typing import Optional
5
4
 
6
5
  import typer
7
6
 
@@ -22,7 +21,7 @@ GRADLEW_BAT_FILE_NAME = 'gradlew.bat'
22
21
 
23
22
  class RestoreGradleDependencies(BaseRestoreDependencies):
24
23
  def __init__(
25
- self, ctx: typer.Context, is_git_diff: bool, command_timeout: int, projects: Optional[set[str]] = None
24
+ self, ctx: typer.Context, is_git_diff: bool, command_timeout: int, projects: set[str] | None = None
26
25
  ) -> None:
27
26
  super().__init__(ctx, is_git_diff, command_timeout, create_output_file_manually=True)
28
27
  self.gradle_executable = self._resolve_gradle_executable()
@@ -58,7 +57,7 @@ class RestoreGradleDependencies(BaseRestoreDependencies):
58
57
  def get_lock_file_names(self) -> list[str]:
59
58
  return [self.get_lock_file_name()]
60
59
 
61
- def get_working_directory(self, document: Document) -> Optional[str]:
60
+ def get_working_directory(self, document: Document) -> str | None:
62
61
  return get_path_from_context(self.ctx) if self.is_gradle_sub_projects() else None
63
62
 
64
63
  def get_all_projects(self) -> set[str]:
@@ -1,7 +1,6 @@
1
1
  import json
2
2
  from os import path
3
3
  from pathlib import Path
4
- from typing import Optional
5
4
 
6
5
  import typer
7
6
 
@@ -21,7 +20,7 @@ MAVEN_CYCLONE_DEP_TREE_FILE_NAME = 'bom.json'
21
20
  MAVEN_DEP_TREE_FILE_NAME = 'bcde.mvndeps'
22
21
 
23
22
 
24
- def _has_dependency_graph(bom_content: Optional[str]) -> bool:
23
+ def _has_dependency_graph(bom_content: str | None) -> bool:
25
24
  try:
26
25
  if not bom_content:
27
26
  return False
@@ -52,7 +51,7 @@ class RestoreMavenDependencies(BaseRestoreDependencies):
52
51
  def get_lock_file_names(self) -> list[str]:
53
52
  return [self.get_lock_file_name()]
54
53
 
55
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
54
+ def try_restore_dependencies(self, document: Document) -> Document | None:
56
55
  manifest_file_path = self.get_manifest_file_path(document)
57
56
  if document.content is None:
58
57
  return self.restore_from_secondary_command(document, manifest_file_path)
@@ -68,7 +67,7 @@ class RestoreMavenDependencies(BaseRestoreDependencies):
68
67
 
69
68
  return restore_dependencies_document
70
69
 
71
- def restore_from_secondary_command(self, document: Document, manifest_file_path: str) -> Optional[Document]:
70
+ def restore_from_secondary_command(self, document: Document, manifest_file_path: str) -> Document | None:
72
71
  restore_content = execute_commands(
73
72
  commands=self.create_secondary_restore_commands(manifest_file_path),
74
73
  timeout=self.command_timeout,
@@ -1,7 +1,6 @@
1
1
  import json
2
2
  import re
3
3
  from pathlib import Path
4
- from typing import Optional
5
4
 
6
5
  import typer
7
6
 
@@ -26,7 +25,7 @@ MINIMUM_BUN_VERSION = (1, 2)
26
25
  BUN_VERSION_COMMAND = ['bun', '--version']
27
26
 
28
27
 
29
- def _indicates_bun(package_json_content: Optional[str]) -> bool:
28
+ def _indicates_bun(package_json_content: str | None) -> bool:
30
29
  """Return True if package.json content signals that this project uses Bun."""
31
30
  if not package_json_content:
32
31
  return False
@@ -43,7 +42,7 @@ def _indicates_bun(package_json_content: Optional[str]) -> bool:
43
42
  return isinstance(engines, dict) and 'bun' in engines
44
43
 
45
44
 
46
- def _parse_bun_version(raw_version: Optional[str]) -> Optional[tuple[int, int]]:
45
+ def _parse_bun_version(raw_version: str | None) -> tuple[int, int] | None:
47
46
  """Parse the (major, minor) version from `bun --version` output (e.g. '1.2.3')."""
48
47
  if not raw_version:
49
48
  return None
@@ -91,7 +90,7 @@ class RestoreBunDependencies(BaseRestoreDependencies):
91
90
  return False
92
91
  return True
93
92
 
94
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
93
+ def try_restore_dependencies(self, document: Document) -> Document | None:
95
94
  manifest_dir = self.get_manifest_dir(document)
96
95
  lockfile_path = Path(manifest_dir) / BUN_LOCK_FILE_NAME if manifest_dir else None
97
96
 
@@ -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 RestoreDenoDependencies(BaseRestoreDependencies):
21
20
  def is_project(self, document: Document) -> bool:
22
21
  return Path(document.path).name in DENO_MANIFEST_FILE_NAMES
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
  if not manifest_dir:
27
26
  return None
@@ -1,6 +1,5 @@
1
1
  import json
2
2
  from pathlib import Path
3
- from typing import Optional
4
3
 
5
4
  import typer
6
5
 
@@ -19,7 +18,7 @@ logger = get_logger('Pnpm Restore Dependencies')
19
18
  PNPM_MANIFEST_FILE_NAME = MANIFEST_FILE_NAME
20
19
 
21
20
 
22
- def _indicates_pnpm(package_json_content: Optional[str]) -> bool:
21
+ def _indicates_pnpm(package_json_content: str | None) -> bool:
23
22
  """Return True if package.json content signals that this project uses pnpm."""
24
23
  if not package_json_content:
25
24
  return False
@@ -53,7 +52,7 @@ class RestorePnpmDependencies(BaseRestoreDependencies):
53
52
 
54
53
  return _indicates_pnpm(document.content)
55
54
 
56
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
55
+ def try_restore_dependencies(self, document: Document) -> Document | None:
57
56
  manifest_dir = self.get_manifest_dir(document)
58
57
  lockfile_path = Path(manifest_dir) / PNPM_LOCK_FILE_NAME if manifest_dir else None
59
58
 
@@ -1,6 +1,5 @@
1
1
  import json
2
2
  from pathlib import Path
3
- from typing import Optional
4
3
 
5
4
  import typer
6
5
 
@@ -19,7 +18,7 @@ logger = get_logger('Yarn Restore Dependencies')
19
18
  YARN_MANIFEST_FILE_NAME = MANIFEST_FILE_NAME
20
19
 
21
20
 
22
- def _indicates_yarn(package_json_content: Optional[str]) -> bool:
21
+ def _indicates_yarn(package_json_content: str | None) -> bool:
23
22
  """Return True if package.json content signals that this project uses Yarn."""
24
23
  if not package_json_content:
25
24
  return False
@@ -53,7 +52,7 @@ class RestoreYarnDependencies(BaseRestoreDependencies):
53
52
 
54
53
  return _indicates_yarn(document.content)
55
54
 
56
- def try_restore_dependencies(self, document: Document) -> Optional[Document]:
55
+ def try_restore_dependencies(self, document: Document) -> Document | None:
57
56
  manifest_dir = self.get_manifest_dir(document)
58
57
  lockfile_path = Path(manifest_dir) / YARN_LOCK_FILE_NAME if manifest_dir else None
59
58
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  import os
4
4
  from pathlib import Path
5
- from typing import TYPE_CHECKING, NamedTuple, Optional
5
+ from typing import TYPE_CHECKING, NamedTuple
6
6
 
7
7
  from cycode.cli.files_collector.sca.npm.workspace.files import logger, resolved_path
8
8
  from cycode.cli.files_collector.sca.npm.workspace.globs import declares_workspace_member
@@ -49,7 +49,7 @@ def _containing_scan_roots(manifest_dir: Path, scan_roots: tuple) -> list:
49
49
  ]
50
50
 
51
51
 
52
- def _resolve_walk_boundary(manifest_dir: Path, scan_roots: tuple) -> Optional[Path]:
52
+ def _resolve_walk_boundary(manifest_dir: Path, scan_roots: tuple) -> Path | None:
53
53
  for root_dir in manifest_dir.parents:
54
54
  if (root_dir / _GIT_DIR_NAME).exists():
55
55
  return root_dir
@@ -76,7 +76,7 @@ def _workspace_root_candidates(manifest_dir: Path, scan_roots: tuple) -> 'Iterat
76
76
  return
77
77
 
78
78
 
79
- def _find_covering_workspace(manifest_dir: Path, scan_roots: tuple) -> Optional[WorkspaceCoverage]:
79
+ def _find_covering_workspace(manifest_dir: Path, scan_roots: tuple) -> WorkspaceCoverage | None:
80
80
  for root_dir in _workspace_root_candidates(manifest_dir, scan_roots):
81
81
  member_path = manifest_dir.relative_to(root_dir).as_posix()
82
82
 
@@ -98,7 +98,7 @@ def _find_covering_workspace(manifest_dir: Path, scan_roots: tuple) -> Optional[
98
98
  return None
99
99
 
100
100
 
101
- def find_covering_workspace(manifest_dir: Optional[str], scan_roots: tuple = ()) -> Optional[WorkspaceCoverage]:
101
+ def find_covering_workspace(manifest_dir: str | None, scan_roots: tuple = ()) -> WorkspaceCoverage | None:
102
102
  if not manifest_dir:
103
103
  return None
104
104
 
@@ -115,7 +115,7 @@ def _is_inside_scanned_paths(scan_roots: tuple, root_dir: Path) -> bool:
115
115
  return any(is_sub_path(directory, resolved_root_dir) for directory in directories)
116
116
 
117
117
 
118
- def is_covered_workspace_member(manifest_dir: Optional[str], document_path: str, scan_roots: tuple = ()) -> bool:
118
+ def is_covered_workspace_member(manifest_dir: str | None, document_path: str, scan_roots: tuple = ()) -> bool:
119
119
  coverage = find_covering_workspace(manifest_dir, scan_roots)
120
120
  if coverage is None:
121
121
  return False
@@ -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: