cycode 1.9.6.dev1__tar.gz → 1.10.0__tar.gz

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 (111) hide show
  1. {cycode-1.9.6.dev1 → cycode-1.10.0}/PKG-INFO +1 -1
  2. cycode-1.10.0/cycode/__init__.py +1 -0
  3. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/auth/auth_manager.py +11 -11
  4. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/sbom/common.py +1 -1
  5. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/code_scanner.py +72 -42
  6. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/pre_commit/pre_commit_command.py +2 -2
  7. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/pre_receive/pre_receive_command.py +5 -4
  8. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/exceptions/handle_scan_errors.py +8 -5
  9. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/excluder.py +38 -11
  10. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/path_documents.py +1 -1
  11. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/repository_documents.py +7 -7
  12. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/sca/maven/base_restore_maven_dependencies.py +1 -1
  13. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/sca/sca_code_scanner.py +15 -13
  14. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/zip_documents.py +7 -6
  15. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/console_printer.py +4 -2
  16. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/json_printer.py +4 -2
  17. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/printer_base.py +1 -1
  18. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/tables/sca_table_printer.py +2 -3
  19. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/tables/table_printer.py +1 -1
  20. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/tables/table_printer_base.py +8 -2
  21. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/text_printer.py +19 -13
  22. cycode-1.10.0/cycode/cli/utils/git_proxy.py +76 -0
  23. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/progress_bar.py +11 -6
  24. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/shell_executor.py +2 -2
  25. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/config.py +18 -9
  26. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/cycode_client_base.py +8 -2
  27. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/headers.py +1 -1
  28. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/scan_client.py +13 -1
  29. {cycode-1.9.6.dev1 → cycode-1.10.0}/pyproject.toml +2 -1
  30. cycode-1.9.6.dev1/cycode/__init__.py +0 -1
  31. {cycode-1.9.6.dev1 → cycode-1.10.0}/README.md +0 -0
  32. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/__init__.py +0 -0
  33. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/__init__.py +0 -0
  34. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/auth/__init__.py +0 -0
  35. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/auth/auth_command.py +0 -0
  36. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/configure/__init__.py +0 -0
  37. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/configure/configure_command.py +0 -0
  38. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/ignore/__init__.py +0 -0
  39. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/ignore/ignore_command.py +0 -0
  40. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/main_cli.py +0 -0
  41. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/__init__.py +0 -0
  42. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/report_command.py +0 -0
  43. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/sbom/__init__.py +0 -0
  44. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/sbom/path/__init__.py +0 -0
  45. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/sbom/path/path_command.py +0 -0
  46. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/sbom/repository_url/__init__.py +0 -0
  47. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/sbom/repository_url/repository_url_command.py +0 -0
  48. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/sbom/sbom_command.py +0 -0
  49. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/report/sbom/sbom_report_file.py +0 -0
  50. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/__init__.py +0 -0
  51. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/commit_history/__init__.py +0 -0
  52. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/commit_history/commit_history_command.py +0 -0
  53. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/path/__init__.py +0 -0
  54. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/path/path_command.py +0 -0
  55. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/pre_commit/__init__.py +0 -0
  56. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/pre_receive/__init__.py +0 -0
  57. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/repository/__init__.py +0 -0
  58. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/repository/repository_command.py +0 -0
  59. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/scan_ci/__init__.py +0 -0
  60. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/scan_ci/ci_integrations.py +0 -0
  61. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/scan_ci/scan_ci_command.py +0 -0
  62. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/scan/scan_command.py +0 -0
  63. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/version/__init__.py +0 -0
  64. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/commands/version/version_command.py +0 -0
  65. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/config.py +0 -0
  66. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/config.yaml +0 -0
  67. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/consts.py +0 -0
  68. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/exceptions/__init__.py +0 -0
  69. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/exceptions/custom_exceptions.py +0 -0
  70. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
  71. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/__init__.py +0 -0
  72. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/iac/__init__.py +0 -0
  73. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
  74. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/models/__init__.py +0 -0
  75. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
  76. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/sca/__init__.py +0 -0
  77. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
  78. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
  79. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
  80. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/main.py +0 -0
  81. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/models.py +0 -0
  82. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/__init__.py +0 -0
  83. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/tables/__init__.py +0 -0
  84. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/tables/table.py +0 -0
  85. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/printers/tables/table_models.py +0 -0
  86. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/user_settings/__init__.py +0 -0
  87. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/user_settings/base_file_manager.py +0 -0
  88. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/user_settings/config_file_manager.py +0 -0
  89. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/user_settings/configuration_manager.py +0 -0
  90. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/user_settings/credentials_manager.py +0 -0
  91. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/user_settings/jwt_creator.py +0 -0
  92. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/__init__.py +0 -0
  93. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/enum_utils.py +0 -0
  94. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/get_api_client.py +0 -0
  95. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/path_utils.py +0 -0
  96. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/scan_batch.py +0 -0
  97. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/scan_utils.py +0 -0
  98. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/string_utils.py +0 -0
  99. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/task_timer.py +0 -0
  100. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cli/utils/yaml_utils.py +0 -0
  101. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/__init__.py +0 -0
  102. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/auth_client.py +0 -0
  103. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/client_creator.py +0 -0
  104. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/config.yaml +0 -0
  105. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/config_dev.py +0 -0
  106. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/cycode_client.py +0 -0
  107. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/cycode_dev_based_client.py +0 -0
  108. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/cycode_token_based_client.py +0 -0
  109. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/models.py +0 -0
  110. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/report_client.py +0 -0
  111. {cycode-1.9.6.dev1 → cycode-1.10.0}/cycode/cyclient/scan_config_base.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cycode
3
- Version: 1.9.6.dev1
3
+ Version: 1.10.0
4
4
  Summary: Boost security in your dev lifecycle via SAST, SCA, Secrets & IaC scanning.
5
5
  Home-page: https://github.com/cycodehq/cycode-cli
6
6
  License: MIT
@@ -0,0 +1 @@
1
+ __version__ = '1.10.0' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
@@ -29,20 +29,20 @@ class AuthManager:
29
29
  self.auth_client = AuthClient()
30
30
 
31
31
  def authenticate(self) -> None:
32
- logger.debug('generating pkce code pair')
32
+ logger.debug('Generating PKCE code pair')
33
33
  code_challenge, code_verifier = self._generate_pkce_code_pair()
34
34
 
35
- logger.debug('starting authentication session')
35
+ logger.debug('Starting authentication session')
36
36
  session_id = self.start_session(code_challenge)
37
- logger.debug('authentication session created, %s', {'session_id': session_id})
37
+ logger.debug('Authentication session created, %s', {'session_id': session_id})
38
38
 
39
- logger.debug('opening browser and redirecting to cycode login page')
39
+ logger.debug('Opening browser and redirecting to Cycode login page')
40
40
  self.redirect_to_login_page(code_challenge, session_id)
41
41
 
42
- logger.debug('starting get api token process')
42
+ logger.debug('Getting API token')
43
43
  api_token = self.get_api_token(session_id, code_verifier)
44
44
 
45
- logger.debug('saving get api token')
45
+ logger.debug('Saving API token')
46
46
  self.save_api_token(api_token)
47
47
 
48
48
  def start_session(self, code_challenge: str) -> str:
@@ -56,20 +56,20 @@ class AuthManager:
56
56
  def get_api_token(self, session_id: str, code_verifier: str) -> 'ApiToken':
57
57
  api_token = self.get_api_token_polling(session_id, code_verifier)
58
58
  if api_token is None:
59
- raise AuthProcessError('getting api token is completed, but the token is missing')
59
+ raise AuthProcessError('API token pulling is completed, but the token is missing')
60
60
  return api_token
61
61
 
62
62
  def get_api_token_polling(self, session_id: str, code_verifier: str) -> 'ApiToken':
63
63
  end_polling_time = time.time() + self.POLLING_TIMEOUT_IN_SECONDS
64
64
  while time.time() < end_polling_time:
65
- logger.debug('trying to get api token...')
65
+ logger.debug('Trying to get API token...')
66
66
  api_token_polling_response = self.auth_client.get_api_token(session_id, code_verifier)
67
67
  if self._is_api_token_process_completed(api_token_polling_response):
68
- logger.debug('get api token process completed')
68
+ logger.debug('Got API token process completion response')
69
69
  return api_token_polling_response.api_token
70
70
  if self._is_api_token_process_failed(api_token_polling_response):
71
- logger.debug('get api token process failed')
72
- raise AuthProcessError('error during getting api token')
71
+ logger.debug('Got API token process failure response')
72
+ raise AuthProcessError('Error while obtaining API token')
73
73
  time.sleep(self.POLLING_WAIT_INTERVAL_IN_SECONDS)
74
74
 
75
75
  raise AuthProcessError('session expired')
@@ -70,7 +70,7 @@ def send_report_feedback(
70
70
 
71
71
  client.report_status(report_execution_id, scan_status)
72
72
  except Exception as e:
73
- logger.debug(f'Failed to send report feedback: {e}')
73
+ logger.debug('Failed to send report feedback', exc_info=e)
74
74
 
75
75
 
76
76
  def create_sbom_report(
@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
7
7
  from uuid import UUID, uuid4
8
8
 
9
9
  import click
10
- from git import NULL_TREE, Repo
11
10
 
12
11
  from cycode.cli import consts
13
12
  from cycode.cli.config import configuration_manager
@@ -28,9 +27,8 @@ from cycode.cli.files_collector.zip_documents import zip_documents
28
27
  from cycode.cli.models import CliError, Document, DocumentDetections, LocalScanResult, Severity
29
28
  from cycode.cli.printers import ConsolePrinter
30
29
  from cycode.cli.utils import scan_utils
31
- from cycode.cli.utils.path_utils import (
32
- get_path_by_os,
33
- )
30
+ from cycode.cli.utils.git_proxy import git_proxy
31
+ from cycode.cli.utils.path_utils import get_path_by_os
34
32
  from cycode.cli.utils.progress_bar import ScanProgressBarSection
35
33
  from cycode.cli.utils.scan_batch import run_parallel_batched_scan
36
34
  from cycode.cli.utils.scan_utils import set_issue_detected
@@ -148,14 +146,14 @@ def _enrich_scan_result_with_data_from_detection_rules(
148
146
 
149
147
  def _get_scan_documents_thread_func(
150
148
  context: click.Context, is_git_diff: bool, is_commit_range: bool, scan_parameters: dict
151
- ) -> Callable[[List[Document]], Tuple[str, CliError, LocalScanResult]]:
149
+ ) -> Tuple[Callable[[List[Document]], Tuple[str, CliError, LocalScanResult]], str]:
152
150
  cycode_client = context.obj['client']
153
151
  scan_type = context.obj['scan_type']
154
152
  severity_threshold = context.obj['severity_threshold']
155
153
  sync_option = context.obj['sync']
156
154
  command_scan_type = context.info_name
157
-
158
- scan_parameters['aggregation_id'] = str(_generate_unique_id())
155
+ aggregation_id = str(_generate_unique_id())
156
+ scan_parameters['aggregation_id'] = aggregation_id
159
157
 
160
158
  def _scan_batch_thread_func(batch: List[Document]) -> Tuple[str, CliError, LocalScanResult]:
161
159
  local_scan_result = error = error_message = None
@@ -200,7 +198,7 @@ def _get_scan_documents_thread_func(
200
198
  scan_id = local_scan_result.scan_id
201
199
 
202
200
  logger.debug(
203
- 'Finished scan process, %s',
201
+ 'Processing scan results, %s',
204
202
  {
205
203
  'all_violations_count': detections_count,
206
204
  'relevant_violations_count': relevant_detections_count,
@@ -224,7 +222,7 @@ def _get_scan_documents_thread_func(
224
222
 
225
223
  return scan_id, error, local_scan_result
226
224
 
227
- return _scan_batch_thread_func
225
+ return _scan_batch_thread_func, aggregation_id
228
226
 
229
227
 
230
228
  def scan_commit_range(
@@ -244,16 +242,16 @@ def scan_commit_range(
244
242
  documents_to_scan = []
245
243
  commit_ids_to_scan = []
246
244
 
247
- repo = Repo(path)
245
+ repo = git_proxy.get_repo(path)
248
246
  total_commits_count = int(repo.git.rev_list('--count', commit_range))
249
- logger.debug(f'Calculating diffs for {total_commits_count} commits in the commit range {commit_range}')
247
+ logger.debug('Calculating diffs for %s commits in the commit range %s', total_commits_count, commit_range)
250
248
 
251
249
  progress_bar.set_section_length(ScanProgressBarSection.PREPARE_LOCAL_FILES, total_commits_count)
252
250
 
253
251
  scanned_commits_count = 0
254
252
  for commit in repo.iter_commits(rev=commit_range):
255
253
  if _does_reach_to_max_commits_to_scan_limit(commit_ids_to_scan, max_commits_count):
256
- logger.debug(f'Reached to max commits to scan count. Going to scan only {max_commits_count} last commits')
254
+ logger.debug('Reached to max commits to scan count. Going to scan only %s last commits', max_commits_count)
257
255
  progress_bar.update(ScanProgressBarSection.PREPARE_LOCAL_FILES, total_commits_count - scanned_commits_count)
258
256
  break
259
257
 
@@ -261,7 +259,7 @@ def scan_commit_range(
261
259
 
262
260
  commit_id = commit.hexsha
263
261
  commit_ids_to_scan.append(commit_id)
264
- parent = commit.parents[0] if commit.parents else NULL_TREE
262
+ parent = commit.parents[0] if commit.parents else git_proxy.get_null_tree()
265
263
  diff = commit.diff(parent, create_patch=True, R=True)
266
264
  commit_documents_to_scan = []
267
265
  for blob in diff:
@@ -284,7 +282,7 @@ def scan_commit_range(
284
282
  scanned_commits_count += 1
285
283
 
286
284
  logger.debug('List of commit ids to scan, %s', {'commit_ids': commit_ids_to_scan})
287
- logger.debug('Starting to scan commit range (It may take a few minutes)')
285
+ logger.debug('Starting to scan commit range (it may take a few minutes)')
288
286
 
289
287
  scan_documents(context, documents_to_scan, is_git_diff=True, is_commit_range=True)
290
288
  return None
@@ -307,16 +305,22 @@ def scan_documents(
307
305
  ConsolePrinter(context).print_error(
308
306
  CliError(
309
307
  code='no_relevant_files',
310
- message='Error: The scan could not be completed - relevant files to scan are not found.',
308
+ message='Error: The scan could not be completed - relevant files to scan are not found. '
309
+ 'Enable verbose mode to see more details.',
311
310
  )
312
311
  )
313
312
  return
314
313
 
315
- scan_batch_thread_func = _get_scan_documents_thread_func(context, is_git_diff, is_commit_range, scan_parameters)
314
+ scan_batch_thread_func, aggregation_id = _get_scan_documents_thread_func(
315
+ context, is_git_diff, is_commit_range, scan_parameters
316
+ )
316
317
  errors, local_scan_results = run_parallel_batched_scan(
317
318
  scan_batch_thread_func, documents_to_scan, progress_bar=progress_bar
318
319
  )
319
-
320
+ aggregation_report_url = _try_get_aggregation_report_url_if_needed(
321
+ scan_parameters, context.obj['client'], context.obj['scan_type']
322
+ )
323
+ set_aggregation_report_url(context, aggregation_report_url)
320
324
  progress_bar.set_section_length(ScanProgressBarSection.GENERATE_REPORT, 1)
321
325
  progress_bar.update(ScanProgressBarSection.GENERATE_REPORT)
322
326
  progress_bar.stop()
@@ -325,6 +329,25 @@ def scan_documents(
325
329
  print_results(context, local_scan_results, errors)
326
330
 
327
331
 
332
+ def set_aggregation_report_url(context: click.Context, aggregation_report_url: Optional[str] = None) -> None:
333
+ context.obj['aggregation_report_url'] = aggregation_report_url
334
+
335
+
336
+ def _try_get_aggregation_report_url_if_needed(
337
+ scan_parameters: dict, cycode_client: 'ScanClient', scan_type: str
338
+ ) -> Optional[str]:
339
+ aggregation_id = scan_parameters.get('aggregation_id')
340
+ if not scan_parameters.get('report'):
341
+ return None
342
+ if aggregation_id is None:
343
+ return None
344
+ try:
345
+ report_url_response = cycode_client.get_scan_aggregation_report_url(aggregation_id, scan_type)
346
+ return report_url_response.report_url
347
+ except Exception as e:
348
+ logger.debug('Failed to get aggregation report url: %s', str(e))
349
+
350
+
328
351
  def scan_commit_range_documents(
329
352
  context: click.Context,
330
353
  from_documents_to_scan: List[Document],
@@ -392,7 +415,7 @@ def scan_commit_range_documents(
392
415
  scan_id = local_scan_result.scan_id
393
416
 
394
417
  logger.debug(
395
- 'Finished scan process, %s',
418
+ 'Processing commit range scan results, %s',
396
419
  {
397
420
  'all_violations_count': detections_count,
398
421
  'relevant_violations_count': relevant_detections_count,
@@ -475,7 +498,7 @@ def perform_scan_async(
475
498
  scan_parameters: dict,
476
499
  ) -> ZippedFileScanResult:
477
500
  scan_async_result = cycode_client.zipped_file_scan_async(zipped_documents, scan_type, scan_parameters)
478
- logger.debug('scan request has been triggered successfully, scan id: %s', scan_async_result.scan_id)
501
+ logger.debug('Async scan request has been triggered successfully, %s', {'scan_id': scan_async_result.scan_id})
479
502
 
480
503
  return poll_scan_results(
481
504
  cycode_client,
@@ -492,7 +515,7 @@ def perform_scan_sync(
492
515
  scan_parameters: dict,
493
516
  ) -> ZippedFileScanResult:
494
517
  scan_results = cycode_client.zipped_file_scan_sync(zipped_documents, scan_type, scan_parameters)
495
- logger.debug('scan request has been triggered successfully, scan id: %s', scan_results.id)
518
+ logger.debug('Sync scan request has been triggered successfully, %s', {'scan_id': scan_results.id})
496
519
  return ZippedFileScanResult(
497
520
  did_detect=True,
498
521
  detections_per_file=_map_detections_per_file(scan_results.detection_messages),
@@ -512,7 +535,9 @@ def perform_commit_range_scan_async(
512
535
  from_commit_zipped_documents, to_commit_zipped_documents, scan_type, scan_parameters
513
536
  )
514
537
 
515
- logger.debug('scan request has been triggered successfully, scan id: %s', scan_async_result.scan_id)
538
+ logger.debug(
539
+ 'Async commit range scan request has been triggered successfully, %s', {'scan_id': scan_async_result.scan_id}
540
+ )
516
541
  return poll_scan_results(
517
542
  cycode_client, scan_async_result.scan_id, scan_type, scan_parameters.get('report'), timeout
518
543
  )
@@ -552,11 +577,12 @@ def poll_scan_results(
552
577
 
553
578
 
554
579
  def print_debug_scan_details(scan_details_response: 'ScanDetailsResponse') -> None:
555
- logger.debug(f'Scan update: (scan_id: {scan_details_response.id})')
556
- logger.debug(f'Scan status: {scan_details_response.scan_status}')
580
+ logger.debug(
581
+ 'Scan update, %s', {'scan_id': scan_details_response.id, 'scan_status': scan_details_response.scan_status}
582
+ )
557
583
 
558
584
  if scan_details_response.message:
559
- logger.debug(f'Scan message: {scan_details_response.message}')
585
+ logger.debug('Scan message: %s', scan_details_response.message)
560
586
 
561
587
 
562
588
  def print_results(
@@ -569,14 +595,16 @@ def print_results(
569
595
  def get_document_detections(
570
596
  scan_result: ZippedFileScanResult, documents_to_scan: List[Document]
571
597
  ) -> List[DocumentDetections]:
572
- logger.debug('Get document detections')
598
+ logger.debug('Getting document detections')
573
599
 
574
600
  document_detections = []
575
601
  for detections_per_file in scan_result.detections_per_file:
576
602
  file_name = get_path_by_os(detections_per_file.file_name)
577
603
  commit_id = detections_per_file.commit_id
578
604
 
579
- logger.debug('Going to find document of violated file, %s', {'file_name': file_name, 'commit_id': commit_id})
605
+ logger.debug(
606
+ 'Going to find the document of the violated file., %s', {'file_name': file_name, 'commit_id': commit_id}
607
+ )
580
608
 
581
609
  document = _get_document_by_file_name(documents_to_scan, file_name, commit_id)
582
610
  document_detections.append(DocumentDetections(document=document, detections=detections_per_file.detections))
@@ -658,11 +686,11 @@ def get_scan_parameters(context: click.Context, paths: Tuple[str]) -> dict:
658
686
 
659
687
  def try_get_git_remote_url(path: str) -> Optional[str]:
660
688
  try:
661
- remote_url = Repo(path).remotes[0].config_reader.get('url')
662
- logger.debug(f'Found Git remote URL "{remote_url}" in path "{path}"')
689
+ remote_url = git_proxy.get_repo(path).remotes[0].config_reader.get('url')
690
+ logger.debug('Found Git remote URL, %s', {'remote_url': remote_url, 'path': path})
663
691
  return remote_url
664
692
  except Exception as e:
665
- logger.debug('Failed to get git remote URL. %s', {'exception_message': str(e)})
693
+ logger.debug('Failed to get Git remote URL', exc_info=e)
666
694
  return None
667
695
 
668
696
 
@@ -719,15 +747,15 @@ def _should_exclude_detection(detection: Detection, exclusions: Dict) -> bool:
719
747
  exclusions_by_value = exclusions.get(consts.EXCLUSIONS_BY_VALUE_SECTION_NAME, [])
720
748
  if _is_detection_sha_configured_in_exclusions(detection, exclusions_by_value):
721
749
  logger.debug(
722
- 'Going to ignore violations because is in the values to ignore list, %s',
723
- {'sha': detection.detection_details.get('sha512', '')},
750
+ 'Going to ignore violations because they are on the values-to-ignore list, %s',
751
+ {'value_sha': detection.detection_details.get('sha512', '')},
724
752
  )
725
753
  return True
726
754
 
727
755
  exclusions_by_sha = exclusions.get(consts.EXCLUSIONS_BY_SHA_SECTION_NAME, [])
728
756
  if _is_detection_sha_configured_in_exclusions(detection, exclusions_by_sha):
729
757
  logger.debug(
730
- 'Going to ignore violations because is in the shas to ignore list, %s',
758
+ 'Going to ignore violations because they are on the SHA ignore list, %s',
731
759
  {'sha': detection.detection_details.get('sha512', '')},
732
760
  )
733
761
  return True
@@ -737,7 +765,7 @@ def _should_exclude_detection(detection: Detection, exclusions: Dict) -> bool:
737
765
  detection_rule = detection.detection_rule_id
738
766
  if detection_rule in exclusions_by_rule:
739
767
  logger.debug(
740
- 'Going to ignore violations because is in the shas to ignore list, %s',
768
+ 'Going to ignore violations because they are on the Rule ID ignore list, %s',
741
769
  {'detection_rule': detection_rule},
742
770
  )
743
771
  return True
@@ -747,7 +775,7 @@ def _should_exclude_detection(detection: Detection, exclusions: Dict) -> bool:
747
775
  package = _get_package_name(detection)
748
776
  if package in exclusions_by_package:
749
777
  logger.debug(
750
- 'Going to ignore violations because is in the packages to ignore list, %s', {'package': package}
778
+ 'Going to ignore violations because they are on the packages-to-ignore list, %s', {'package': package}
751
779
  )
752
780
  return True
753
781
 
@@ -810,7 +838,7 @@ def _report_scan_status(
810
838
 
811
839
  cycode_client.report_scan_status(scan_type, scan_id, scan_status, should_use_scan_service)
812
840
  except Exception as e:
813
- logger.debug('Failed to report scan status, %s', {'exception_message': str(e)})
841
+ logger.debug('Failed to report scan status', exc_info=e)
814
842
 
815
843
 
816
844
  def _generate_unique_id() -> UUID:
@@ -868,7 +896,7 @@ def _try_get_report_url_if_needed(
868
896
  report_url_response = cycode_client.get_scan_report_url(scan_id, scan_type)
869
897
  return report_url_response.report_url
870
898
  except Exception as e:
871
- logger.debug('Failed to get report url: %s', str(e))
899
+ logger.debug('Failed to get report URL', exc_info=e)
872
900
 
873
901
 
874
902
  def wait_for_detections_creation(
@@ -883,16 +911,18 @@ def wait_for_detections_creation(
883
911
  while time.time() < end_polling_time:
884
912
  scan_persisted_detections_count = cycode_client.get_scan_detections_count(scan_type, scan_id)
885
913
  logger.debug(
886
- f'Excepted {expected_detections_count} detections, got {scan_persisted_detections_count} detections '
887
- f'({expected_detections_count - scan_persisted_detections_count} more; '
888
- f'{round(end_polling_time - time.time())} seconds left)'
914
+ 'Excepting %s detections, got %s detections (%s more; %s seconds left)',
915
+ expected_detections_count,
916
+ scan_persisted_detections_count,
917
+ expected_detections_count - scan_persisted_detections_count,
918
+ round(end_polling_time - time.time()),
889
919
  )
890
920
  if scan_persisted_detections_count == expected_detections_count:
891
921
  return
892
922
 
893
923
  time.sleep(consts.DETECTIONS_COUNT_VERIFICATION_WAIT_INTERVAL_IN_SECONDS)
894
924
 
895
- logger.debug(f'{scan_persisted_detections_count} detections has been created')
925
+ logger.debug('%s detections has been created', scan_persisted_detections_count)
896
926
  raise custom_exceptions.ScanAsyncError(
897
927
  f'Failed to wait for detections to be created after {polling_timeout} seconds'
898
928
  )
@@ -905,14 +935,14 @@ def _map_detections_per_file(detections: List[dict]) -> List[DetectionsPerFile]:
905
935
  detection['message'] = detection['correlation_message']
906
936
  file_name = _get_file_name_from_detection(detection)
907
937
  if file_name is None:
908
- logger.debug('file name is missing from detection with id %s', detection.get('id'))
938
+ logger.debug('File name is missing from detection with ID %s', detection.get('id'))
909
939
  continue
910
940
  if detections_per_files.get(file_name) is None:
911
941
  detections_per_files[file_name] = [DetectionSchema().load(detection)]
912
942
  else:
913
943
  detections_per_files[file_name].append(DetectionSchema().load(detection))
914
944
  except Exception as e:
915
- logger.debug('Failed to parse detection: %s', str(e))
945
+ logger.debug('Failed to parse detection', exc_info=e)
916
946
  continue
917
947
 
918
948
  return [
@@ -2,7 +2,6 @@ import os
2
2
  from typing import List
3
3
 
4
4
  import click
5
- from git import Repo
6
5
 
7
6
  from cycode.cli import consts
8
7
  from cycode.cli.commands.scan.code_scanner import scan_documents, scan_sca_pre_commit
@@ -12,6 +11,7 @@ from cycode.cli.files_collector.repository_documents import (
12
11
  get_diff_file_path,
13
12
  )
14
13
  from cycode.cli.models import Document
14
+ from cycode.cli.utils.git_proxy import git_proxy
15
15
  from cycode.cli.utils.path_utils import (
16
16
  get_path_by_os,
17
17
  )
@@ -31,7 +31,7 @@ def pre_commit_command(context: click.Context, ignored_args: List[str]) -> None:
31
31
  scan_sca_pre_commit(context)
32
32
  return
33
33
 
34
- diff_files = Repo(os.getcwd()).index.diff('HEAD', create_patch=True, R=True)
34
+ diff_files = git_proxy.get_repo(os.getcwd()).index.diff('HEAD', create_patch=True, R=True)
35
35
 
36
36
  progress_bar.set_section_length(ScanProgressBarSection.PREPARE_LOCAL_FILES, len(diff_files))
37
37
 
@@ -32,14 +32,14 @@ def pre_receive_command(context: click.Context, ignored_args: List[str]) -> None
32
32
 
33
33
  if should_skip_pre_receive_scan():
34
34
  logger.info(
35
- 'A scan has been skipped as per your request.'
36
- ' Please note that this may leave your system vulnerable to secrets that have not been detected'
35
+ 'A scan has been skipped as per your request. '
36
+ 'Please note that this may leave your system vulnerable to secrets that have not been detected.'
37
37
  )
38
38
  return
39
39
 
40
40
  if is_verbose_mode_requested_in_pre_receive_scan():
41
41
  enable_verbose_mode(context)
42
- logger.debug('Verbose mode enabled, all log levels will be displayed')
42
+ logger.debug('Verbose mode enabled: all log levels will be displayed.')
43
43
 
44
44
  command_scan_type = context.info_name
45
45
  timeout = configuration_manager.get_pre_receive_command_timeout(command_scan_type)
@@ -51,7 +51,8 @@ def pre_receive_command(context: click.Context, ignored_args: List[str]) -> None
51
51
  commit_range = calculate_pre_receive_commit_range(branch_update_details)
52
52
  if not commit_range:
53
53
  logger.info(
54
- 'No new commits found for pushed branch, %s', {'branch_update_details': branch_update_details}
54
+ 'No new commits found for pushed branch, %s',
55
+ {'branch_update_details': branch_update_details},
55
56
  )
56
57
  return
57
58
 
@@ -1,11 +1,11 @@
1
1
  from typing import Optional
2
2
 
3
3
  import click
4
- from git import InvalidGitRepositoryError
5
4
 
6
5
  from cycode.cli.exceptions import custom_exceptions
7
6
  from cycode.cli.models import CliError, CliErrors
8
7
  from cycode.cli.printers import ConsolePrinter
8
+ from cycode.cli.utils.git_proxy import git_proxy
9
9
 
10
10
 
11
11
  def handle_scan_exception(
@@ -13,7 +13,7 @@ def handle_scan_exception(
13
13
  ) -> Optional[CliError]:
14
14
  context.obj['did_fail'] = True
15
15
 
16
- ConsolePrinter(context).print_exception()
16
+ ConsolePrinter(context).print_exception(e)
17
17
 
18
18
  errors: CliErrors = {
19
19
  custom_exceptions.NetworkError: CliError(
@@ -49,7 +49,7 @@ def handle_scan_exception(
49
49
  'Please make sure that your file is well formed '
50
50
  'and execute the scan again',
51
51
  ),
52
- InvalidGitRepositoryError: CliError(
52
+ git_proxy.get_invalid_git_repository_error(): CliError(
53
53
  soft_fail=False,
54
54
  code='invalid_git_error',
55
55
  message='The path you supplied does not correlate to a git repository. '
@@ -69,10 +69,13 @@ def handle_scan_exception(
69
69
  ConsolePrinter(context).print_error(error)
70
70
  return None
71
71
 
72
+ unknown_error = CliError(code='unknown_error', message=str(e))
73
+
72
74
  if return_exception:
73
- return CliError(code='unknown_error', message=str(e))
75
+ return unknown_error
74
76
 
75
77
  if isinstance(e, click.ClickException):
76
78
  raise e
77
79
 
78
- raise click.ClickException(str(e))
80
+ ConsolePrinter(context).print_error(unknown_error)
81
+ exit(1)
@@ -62,23 +62,36 @@ def _does_document_exceed_max_size_limit(content: str) -> bool:
62
62
 
63
63
  def _is_relevant_file_to_scan(scan_type: str, filename: str) -> bool:
64
64
  if _is_subpath_of_cycode_configuration_folder(filename):
65
- logger.debug('file is irrelevant because it is in cycode configuration directory, %s', {'filename': filename})
65
+ logger.debug(
66
+ 'The file is irrelevant because it is in the Cycode configuration directory, %s',
67
+ {'filename': filename, 'configuration_directory': consts.CYCODE_CONFIGURATION_DIRECTORY},
68
+ )
66
69
  return False
67
70
 
68
71
  if _is_path_configured_in_exclusions(scan_type, filename):
69
- logger.debug('file is irrelevant because the file path is in the ignore paths list, %s', {'filename': filename})
72
+ logger.debug('The file is irrelevant because its path is in the ignore paths list, %s', {'filename': filename})
70
73
  return False
71
74
 
72
75
  if not _is_file_extension_supported(scan_type, filename):
73
- logger.debug('file is irrelevant because the file extension is not supported, %s', {'filename': filename})
76
+ logger.debug(
77
+ 'The file is irrelevant because its extension is not supported, %s',
78
+ {'scan_type': scan_type, 'filename': filename},
79
+ )
74
80
  return False
75
81
 
76
82
  if is_binary_file(filename):
77
- logger.debug('file is irrelevant because it is binary file, %s', {'filename': filename})
83
+ logger.debug('The file is irrelevant because it is a binary file, %s', {'filename': filename})
78
84
  return False
79
85
 
80
86
  if scan_type != consts.SCA_SCAN_TYPE and _does_file_exceed_max_size_limit(filename):
81
- logger.debug('file is irrelevant because its exceeded max size limit, %s', {'filename': filename})
87
+ logger.debug(
88
+ 'The file is irrelevant because it has exceeded the maximum size limit, %s',
89
+ {
90
+ 'max_file_size': consts.FILE_MAX_SIZE_LIMIT_IN_BYTES,
91
+ 'file_size': get_file_size(filename),
92
+ 'filename': filename,
93
+ },
94
+ )
82
95
  return False
83
96
 
84
97
  if scan_type == consts.SCA_SCAN_TYPE and not _is_file_relevant_for_sca_scan(filename):
@@ -89,7 +102,9 @@ def _is_relevant_file_to_scan(scan_type: str, filename: str) -> bool:
89
102
 
90
103
  def _is_file_relevant_for_sca_scan(filename: str) -> bool:
91
104
  if any(sca_excluded_path in filename for sca_excluded_path in consts.SCA_EXCLUDED_PATHS):
92
- logger.debug("file is irrelevant because it is from node_modules's inner path, %s", {'filename': filename})
105
+ logger.debug(
106
+ 'The file is irrelevant because it is from the inner path of node_modules, %s', {'filename': filename}
107
+ )
93
108
  return False
94
109
 
95
110
  return True
@@ -98,27 +113,39 @@ def _is_file_relevant_for_sca_scan(filename: str) -> bool:
98
113
  def _is_relevant_document_to_scan(scan_type: str, filename: str, content: str) -> bool:
99
114
  if _is_subpath_of_cycode_configuration_folder(filename):
100
115
  logger.debug(
101
- 'document is irrelevant because it is in cycode configuration directory, %s', {'filename': filename}
116
+ 'The document is irrelevant because it is in the Cycode configuration directory, %s',
117
+ {'filename': filename, 'configuration_directory': consts.CYCODE_CONFIGURATION_DIRECTORY},
102
118
  )
103
119
  return False
104
120
 
105
121
  if _is_path_configured_in_exclusions(scan_type, filename):
106
122
  logger.debug(
107
- 'document is irrelevant because the document path is in the ignore paths list, %s', {'filename': filename}
123
+ 'The document is irrelevant because its path is in the ignore paths list, %s', {'filename': filename}
108
124
  )
109
125
  return False
110
126
 
111
127
  if not _is_file_extension_supported(scan_type, filename):
112
- logger.debug('document is irrelevant because the file extension is not supported, %s', {'filename': filename})
128
+ logger.debug(
129
+ 'The document is irrelevant because its extension is not supported, %s',
130
+ {'scan_type': scan_type, 'filename': filename},
131
+ )
113
132
  return False
114
133
 
115
134
  if is_binary_content(content):
116
- logger.debug('document is irrelevant because it is binary, %s', {'filename': filename})
135
+ logger.debug('The document is irrelevant because it is a binary file, %s', {'filename': filename})
117
136
  return False
118
137
 
119
138
  if scan_type != consts.SCA_SCAN_TYPE and _does_document_exceed_max_size_limit(content):
120
- logger.debug('document is irrelevant because its exceeded max size limit, %s', {'filename': filename})
139
+ logger.debug(
140
+ 'The document is irrelevant because it has exceeded the maximum size limit, %s',
141
+ {
142
+ 'max_document_size': consts.FILE_MAX_SIZE_LIMIT_IN_BYTES,
143
+ 'document_size': get_content_size(content),
144
+ 'filename': filename,
145
+ },
146
+ )
121
147
  return False
148
+
122
149
  return True
123
150
 
124
151
 
@@ -74,7 +74,7 @@ def _get_relevant_files(
74
74
  progress_bar.set_section_length(progress_bar_section, progress_bar_section_len)
75
75
 
76
76
  logger.debug(
77
- 'Found all relevant files for scanning %s', {'paths': paths, 'file_to_scan_count': len(relevant_files_to_scan)}
77
+ 'Found all relevant files for scanning, %s', {'paths': paths, 'file_to_scan_count': len(relevant_files_to_scan)}
78
78
  )
79
79
 
80
80
  return relevant_files_to_scan