cycode 3.4.3.dev1__py3-none-any.whl → 3.4.4.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cycode/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '3.4.3.dev1' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
1
+ __version__ = '3.4.4.dev1' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
@@ -3,12 +3,15 @@ import typer
3
3
  from cycode.cli.apps.scan.commit_history.commit_history_command import commit_history_command
4
4
  from cycode.cli.apps.scan.path.path_command import path_command
5
5
  from cycode.cli.apps.scan.pre_commit.pre_commit_command import pre_commit_command
6
+ from cycode.cli.apps.scan.pre_push.pre_push_command import pre_push_command
6
7
  from cycode.cli.apps.scan.pre_receive.pre_receive_command import pre_receive_command
7
8
  from cycode.cli.apps.scan.repository.repository_command import repository_command
8
9
  from cycode.cli.apps.scan.scan_command import scan_command, scan_command_result_callback
9
10
 
10
11
  app = typer.Typer(name='scan', no_args_is_help=True)
11
12
 
13
+ _AUTOMATION_COMMANDS_RICH_HELP_PANEL = 'Automation commands'
14
+
12
15
  _scan_command_docs = 'https://github.com/cycodehq/cycode-cli/blob/main/README.md#scan-command'
13
16
  _scan_command_epilog = f'[bold]Documentation:[/] [link={_scan_command_docs}]{_scan_command_docs}[/link]'
14
17
 
@@ -26,16 +29,22 @@ app.command(name='commit-history', short_help='Scan commit history or perform di
26
29
  app.command(
27
30
  name='pre-commit',
28
31
  short_help='Use this command in pre-commit hook to scan any content that was not committed yet.',
29
- rich_help_panel='Automation commands',
32
+ rich_help_panel=_AUTOMATION_COMMANDS_RICH_HELP_PANEL,
30
33
  )(pre_commit_command)
34
+ app.command(
35
+ name='pre-push',
36
+ short_help='Use this command in pre-push hook to scan commits before pushing them to the remote repository.',
37
+ rich_help_panel=_AUTOMATION_COMMANDS_RICH_HELP_PANEL,
38
+ )(pre_push_command)
31
39
  app.command(
32
40
  name='pre-receive',
33
41
  short_help='Use this command in pre-receive hook '
34
42
  'to scan commits on the server side before pushing them to the repository.',
35
- rich_help_panel='Automation commands',
43
+ rich_help_panel=_AUTOMATION_COMMANDS_RICH_HELP_PANEL,
36
44
  )(pre_receive_command)
37
45
 
38
46
  # backward compatibility
39
47
  app.command(hidden=True, name='commit_history')(commit_history_command)
40
48
  app.command(hidden=True, name='pre_commit')(pre_commit_command)
49
+ app.command(hidden=True, name='pre_push')(pre_push_command)
41
50
  app.command(hidden=True, name='pre_receive')(pre_receive_command)
File without changes
@@ -0,0 +1,68 @@
1
+ import logging
2
+ import os
3
+ from typing import Annotated, Optional
4
+
5
+ import typer
6
+
7
+ from cycode.cli import consts
8
+ from cycode.cli.apps.scan.commit_range_scanner import (
9
+ is_verbose_mode_requested_in_pre_receive_scan,
10
+ scan_commit_range,
11
+ should_skip_pre_receive_scan,
12
+ )
13
+ from cycode.cli.config import configuration_manager
14
+ from cycode.cli.console import console
15
+ from cycode.cli.exceptions.handle_scan_errors import handle_scan_exception
16
+ from cycode.cli.files_collector.commit_range_documents import (
17
+ calculate_pre_push_commit_range,
18
+ parse_pre_push_input,
19
+ )
20
+ from cycode.cli.logger import logger
21
+ from cycode.cli.utils import scan_utils
22
+ from cycode.cli.utils.sentry import add_breadcrumb
23
+ from cycode.cli.utils.task_timer import TimeoutAfter
24
+ from cycode.logger import set_logging_level
25
+
26
+
27
+ def pre_push_command(
28
+ ctx: typer.Context,
29
+ _: Annotated[Optional[list[str]], typer.Argument(help='Ignored arguments', hidden=True)] = None,
30
+ ) -> None:
31
+ try:
32
+ add_breadcrumb('pre_push')
33
+
34
+ if should_skip_pre_receive_scan():
35
+ logger.info(
36
+ 'A scan has been skipped as per your request. '
37
+ 'Please note that this may leave your system vulnerable to secrets that have not been detected.'
38
+ )
39
+ return
40
+
41
+ if is_verbose_mode_requested_in_pre_receive_scan():
42
+ ctx.obj['verbose'] = True
43
+ set_logging_level(logging.DEBUG)
44
+ logger.debug('Verbose mode enabled: all log levels will be displayed.')
45
+
46
+ command_scan_type = ctx.info_name
47
+ timeout = configuration_manager.get_pre_push_command_timeout(command_scan_type)
48
+ with TimeoutAfter(timeout):
49
+ push_update_details = parse_pre_push_input()
50
+ commit_range = calculate_pre_push_commit_range(push_update_details)
51
+ if not commit_range:
52
+ logger.info(
53
+ 'No new commits found for pushed branch, %s',
54
+ {'push_update_details': push_update_details},
55
+ )
56
+ return
57
+
58
+ scan_commit_range(
59
+ ctx=ctx,
60
+ repo_path=os.getcwd(),
61
+ commit_range=commit_range,
62
+ max_commits_count=configuration_manager.get_pre_push_max_commits_to_scan_count(command_scan_type),
63
+ )
64
+
65
+ if scan_utils.is_scan_failed(ctx):
66
+ console.print(consts.PRE_RECEIVE_AND_PUSH_REMEDIATION_MESSAGE)
67
+ except Exception as e:
68
+ handle_scan_exception(ctx, e)
@@ -63,6 +63,6 @@ def pre_receive_command(
63
63
  )
64
64
 
65
65
  if scan_utils.is_scan_failed(ctx):
66
- console.print(consts.PRE_RECEIVE_REMEDIATION_MESSAGE)
66
+ console.print(consts.PRE_RECEIVE_AND_PUSH_REMEDIATION_MESSAGE)
67
67
  except Exception as e:
68
68
  handle_scan_exception(ctx, e)
cycode/cli/consts.py CHANGED
@@ -240,7 +240,14 @@ PRE_RECEIVE_MAX_COMMITS_TO_SCAN_COUNT_ENV_VAR_NAME = 'PRE_RECEIVE_MAX_COMMITS_TO
240
240
  DEFAULT_PRE_RECEIVE_MAX_COMMITS_TO_SCAN_COUNT = 50
241
241
  PRE_RECEIVE_COMMAND_TIMEOUT_ENV_VAR_NAME = 'PRE_RECEIVE_COMMAND_TIMEOUT'
242
242
  DEFAULT_PRE_RECEIVE_COMMAND_TIMEOUT_IN_SECONDS = 60
243
- PRE_RECEIVE_REMEDIATION_MESSAGE = """
243
+ # pre push scan
244
+ PRE_PUSH_MAX_COMMITS_TO_SCAN_COUNT_ENV_VAR_NAME = 'PRE_PUSH_MAX_COMMITS_TO_SCAN_COUNT'
245
+ DEFAULT_PRE_PUSH_MAX_COMMITS_TO_SCAN_COUNT = 50
246
+ PRE_PUSH_COMMAND_TIMEOUT_ENV_VAR_NAME = 'PRE_PUSH_COMMAND_TIMEOUT'
247
+ DEFAULT_PRE_PUSH_COMMAND_TIMEOUT_IN_SECONDS = 60
248
+ CYCODE_DEFAULT_BRANCH_ENV_VAR_NAME = 'CYCODE_DEFAULT_BRANCH'
249
+ # pre push and pre receive common
250
+ PRE_RECEIVE_AND_PUSH_REMEDIATION_MESSAGE = """
244
251
  Cycode Secrets Push Protection
245
252
  ------------------------------------------------------------------------------
246
253
  Resolve the following secrets by rewriting your local commit history before pushing again.
@@ -211,6 +211,129 @@ def parse_pre_receive_input() -> str:
211
211
  return pre_receive_input.splitlines()[0]
212
212
 
213
213
 
214
+ def parse_pre_push_input() -> str:
215
+ """Parse input to pre-push hook details.
216
+
217
+ Example input:
218
+ local_ref local_object_name remote_ref remote_object_name
219
+ ---------------------------------------------------------
220
+ refs/heads/main 9cf90954ef26e7c58284f8ebf7dcd0fcf711152a refs/heads/main 973a96d3e925b65941f7c47fa16129f1577d499f
221
+ refs/heads/feature-branch 3378e52dcfa47fb11ce3a4a520bea5f85d5d0bf3 refs/heads/feature-branch 59564ef68745bca38c42fc57a7822efd519a6bd9
222
+
223
+ :return: First, push update details (input's first line)
224
+ """ # noqa: E501
225
+ pre_push_input = sys.stdin.read().strip()
226
+ if not pre_push_input:
227
+ raise ValueError(
228
+ 'Pre push input was not found. Make sure that you are using this command only in pre-push hook'
229
+ )
230
+
231
+ # each line represents a branch push request, handle the first one only
232
+ return pre_push_input.splitlines()[0]
233
+
234
+
235
+ def _get_default_branches_for_merge_base(repo: 'Repo') -> list[str]:
236
+ """Get a list of default branches to try for merge base calculation.
237
+
238
+ Priority order:
239
+ 1. Environment variable CYCODE_DEFAULT_BRANCH
240
+ 2. Git remote HEAD (git symbolic-ref refs/remotes/origin/HEAD)
241
+ 3. Fallback to common default branch names
242
+
243
+ Args:
244
+ repo: Git repository object
245
+
246
+ Returns:
247
+ List of branch names to try for merge base calculation
248
+ """
249
+ default_branches = []
250
+
251
+ # 1. Check environment variable first
252
+ env_default_branch = os.getenv(consts.CYCODE_DEFAULT_BRANCH_ENV_VAR_NAME)
253
+ if env_default_branch:
254
+ logger.debug('Using default branch from environment variable: %s', env_default_branch)
255
+ default_branches.append(env_default_branch)
256
+
257
+ # 2. Try to get the actual default branch from remote HEAD
258
+ try:
259
+ remote_head = repo.git.symbolic_ref('refs/remotes/origin/HEAD')
260
+ # symbolic-ref returns something like "refs/remotes/origin/main"
261
+ if remote_head.startswith('refs/remotes/origin/'):
262
+ default_branch = remote_head.replace('refs/remotes/origin/', '')
263
+ logger.debug('Found remote default branch: %s', default_branch)
264
+ # Add both the remote tracking branch and local branch variants
265
+ default_branches.extend([f'origin/{default_branch}', default_branch])
266
+ except Exception as e:
267
+ logger.debug('Failed to get remote HEAD via symbolic-ref: %s', exc_info=e)
268
+
269
+ # Try an alternative method: git remote show origin
270
+ try:
271
+ remote_info = repo.git.remote('show', 'origin')
272
+ for line in remote_info.splitlines():
273
+ if 'HEAD branch:' in line:
274
+ default_branch = line.split('HEAD branch:')[1].strip()
275
+ logger.debug('Found default branch via remote show: %s', default_branch)
276
+ default_branches.extend([f'origin/{default_branch}', default_branch])
277
+ break
278
+ except Exception as e2:
279
+ logger.debug('Failed to get remote info via remote show: %s', exc_info=e2)
280
+
281
+ # 3. Add fallback branches (avoiding duplicates)
282
+ fallback_branches = ['origin/main', 'origin/master', 'main', 'master']
283
+ for branch in fallback_branches:
284
+ if branch not in default_branches:
285
+ default_branches.append(branch)
286
+
287
+ logger.debug('Default branches to try: %s', default_branches)
288
+ return default_branches
289
+
290
+
291
+ def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]:
292
+ """Calculate the commit range for pre-push hook scanning.
293
+
294
+ Args:
295
+ push_update_details: String in format "local_ref local_object_name remote_ref remote_object_name"
296
+
297
+ Returns:
298
+ Commit range string for scanning, or None if no scanning is needed
299
+
300
+ Environment Variables:
301
+ CYCODE_DEFAULT_BRANCH: Override the default branch for merge base calculation
302
+ """
303
+ local_ref, local_object_name, remote_ref, remote_object_name = push_update_details.split()
304
+
305
+ if remote_object_name == consts.EMPTY_COMMIT_SHA:
306
+ try:
307
+ repo = git_proxy.get_repo(os.getcwd())
308
+ default_branches = _get_default_branches_for_merge_base(repo)
309
+
310
+ merge_base = None
311
+ for default_branch in default_branches:
312
+ try:
313
+ merge_base = repo.git.merge_base(local_object_name, default_branch)
314
+ logger.debug('Found merge base %s with branch %s', merge_base, default_branch)
315
+ break
316
+ except Exception as e:
317
+ logger.debug('Failed to find merge base with %s: %s', default_branch, exc_info=e)
318
+ continue
319
+
320
+ if merge_base:
321
+ return f'{merge_base}..{local_object_name}'
322
+
323
+ logger.debug('Failed to find merge base with any default branch')
324
+ return '--all'
325
+ except Exception as e:
326
+ logger.debug('Failed to get repo for pre-push commit range calculation: %s', exc_info=e)
327
+ return '--all'
328
+
329
+ # If deleting a branch (local_object_name is all zeros), no need to scan
330
+ if local_object_name == consts.EMPTY_COMMIT_SHA:
331
+ return None
332
+
333
+ # For updates to existing branches, scan from remote to local
334
+ return f'{remote_object_name}..{local_object_name}'
335
+
336
+
214
337
  def get_diff_file_path(diff: 'Diff', relative: bool = False, repo: Optional['Repo'] = None) -> Optional[str]:
215
338
  """Get the file path from a git Diff object.
216
339
 
@@ -135,10 +135,10 @@ class ConfigurationManager:
135
135
  )
136
136
  )
137
137
 
138
- def get_pre_receive_max_commits_to_scan_count(self, command_scan_type: str) -> int:
139
- max_commits = self._get_value_from_environment_variables(
140
- consts.PRE_RECEIVE_MAX_COMMITS_TO_SCAN_COUNT_ENV_VAR_NAME
141
- )
138
+ def _get_git_hook_max_commits_to_scan_count(
139
+ self, command_scan_type: str, env_var_name: str, default_count: int
140
+ ) -> int:
141
+ max_commits = self._get_value_from_environment_variables(env_var_name)
142
142
  if max_commits is not None:
143
143
  return int(max_commits)
144
144
 
@@ -150,10 +150,24 @@ class ConfigurationManager:
150
150
  if max_commits is not None:
151
151
  return max_commits
152
152
 
153
- return consts.DEFAULT_PRE_RECEIVE_MAX_COMMITS_TO_SCAN_COUNT
153
+ return default_count
154
154
 
155
- def get_pre_receive_command_timeout(self, command_scan_type: str) -> int:
156
- command_timeout = self._get_value_from_environment_variables(consts.PRE_RECEIVE_COMMAND_TIMEOUT_ENV_VAR_NAME)
155
+ def get_pre_receive_max_commits_to_scan_count(self, command_scan_type: str) -> int:
156
+ return self._get_git_hook_max_commits_to_scan_count(
157
+ command_scan_type,
158
+ consts.PRE_RECEIVE_MAX_COMMITS_TO_SCAN_COUNT_ENV_VAR_NAME,
159
+ consts.DEFAULT_PRE_RECEIVE_MAX_COMMITS_TO_SCAN_COUNT,
160
+ )
161
+
162
+ def get_pre_push_max_commits_to_scan_count(self, command_scan_type: str) -> int:
163
+ return self._get_git_hook_max_commits_to_scan_count(
164
+ command_scan_type,
165
+ consts.PRE_PUSH_MAX_COMMITS_TO_SCAN_COUNT_ENV_VAR_NAME,
166
+ consts.DEFAULT_PRE_PUSH_MAX_COMMITS_TO_SCAN_COUNT,
167
+ )
168
+
169
+ def _get_git_hook_command_timeout(self, command_scan_type: str, env_var_name: str, default_timeout: int) -> int:
170
+ command_timeout = self._get_value_from_environment_variables(env_var_name)
157
171
  if command_timeout is not None:
158
172
  return int(command_timeout)
159
173
 
@@ -165,7 +179,21 @@ class ConfigurationManager:
165
179
  if command_timeout is not None:
166
180
  return command_timeout
167
181
 
168
- return consts.DEFAULT_PRE_RECEIVE_COMMAND_TIMEOUT_IN_SECONDS
182
+ return default_timeout
183
+
184
+ def get_pre_receive_command_timeout(self, command_scan_type: str) -> int:
185
+ return self._get_git_hook_command_timeout(
186
+ command_scan_type,
187
+ consts.PRE_RECEIVE_COMMAND_TIMEOUT_ENV_VAR_NAME,
188
+ consts.DEFAULT_PRE_RECEIVE_COMMAND_TIMEOUT_IN_SECONDS,
189
+ )
190
+
191
+ def get_pre_push_command_timeout(self, command_scan_type: str) -> int:
192
+ return self._get_git_hook_command_timeout(
193
+ command_scan_type,
194
+ consts.PRE_PUSH_COMMAND_TIMEOUT_ENV_VAR_NAME,
195
+ consts.DEFAULT_PRE_PUSH_COMMAND_TIMEOUT_IN_SECONDS,
196
+ )
169
197
 
170
198
  def get_should_exclude_detections_in_deleted_lines(self, command_scan_type: str) -> bool:
171
199
  exclude_detections_in_deleted_lines = self._get_value_from_environment_variables(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cycode
3
- Version: 3.4.3.dev1
3
+ Version: 3.4.4.dev1
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
@@ -81,6 +81,7 @@ This guide walks you through both installation and usage.
81
81
  4. [Commit History Scan](#commit-history-scan)
82
82
  1. [Commit Range Option (Diff Scanning)](#commit-range-option-diff-scanning)
83
83
  5. [Pre-Commit Scan](#pre-commit-scan)
84
+ 6. [Pre-Push Scan](#pre-push-scan)
84
85
  2. [Scan Results](#scan-results)
85
86
  1. [Show/Hide Secrets](#showhide-secrets)
86
87
  2. [Soft Fail](#soft-fail)
@@ -257,13 +258,15 @@ export CYCODE_CLIENT_SECRET={your Cycode Secret Key}
257
258
 
258
259
  ## Install Pre-Commit Hook
259
260
 
260
- Cycodes pre-commit hook can be set up within your local repository so that the Cycode CLI application will identify any issues with your code automatically before you commit it to your codebase.
261
+ Cycode's pre-commit and pre-push hooks can be set up within your local repository so that the Cycode CLI application will identify any issues with your code automatically before you commit or push it to your codebase.
261
262
 
262
263
  > [!NOTE]
263
- > pre-commit hook is not available for IaC scans.
264
+ > pre-commit and pre-push hooks are not available for IaC scans.
264
265
 
265
266
  Perform the following steps to install the pre-commit hook:
266
267
 
268
+ ### Installing Pre-Commit Hook
269
+
267
270
  1. Install the pre-commit framework (Python 3.9 or higher must be installed):
268
271
 
269
272
  ```bash
@@ -277,11 +280,10 @@ Perform the following steps to install the pre-commit hook:
277
280
  ```yaml
278
281
  repos:
279
282
  - repo: https://github.com/cycodehq/cycode-cli
280
- rev: v3.4.2
283
+ rev: v3.5.0
281
284
  hooks:
282
285
  - id: cycode
283
- stages:
284
- - pre-commit
286
+ stages: [pre-commit]
285
287
  ```
286
288
 
287
289
  4. Modify the created file for your specific needs. Use hook ID `cycode` to enable scan for Secrets. Use hook ID `cycode-sca` to enable SCA scan. Use hook ID `cycode-sast` to enable SAST scan. If you want to enable all scanning types, use this configuration:
@@ -289,17 +291,14 @@ Perform the following steps to install the pre-commit hook:
289
291
  ```yaml
290
292
  repos:
291
293
  - repo: https://github.com/cycodehq/cycode-cli
292
- rev: v3.4.2
294
+ rev: v3.5.0
293
295
  hooks:
294
296
  - id: cycode
295
- stages:
296
- - pre-commit
297
+ stages: [pre-commit]
297
298
  - id: cycode-sca
298
- stages:
299
- - pre-commit
299
+ stages: [pre-commit]
300
300
  - id: cycode-sast
301
- stages:
302
- - pre-commit
301
+ stages: [pre-commit]
303
302
  ```
304
303
 
305
304
  5. Install Cycode’s hook:
@@ -322,6 +321,37 @@ Perform the following steps to install the pre-commit hook:
322
321
  > Trigger happens on `git commit` command.
323
322
  > Hook triggers only on the files that are staged for commit.
324
323
 
324
+ ### Installing Pre-Push Hook
325
+
326
+ To install the pre-push hook in addition to or instead of the pre-commit hook:
327
+
328
+ 1. Add the pre-push hooks to your `.pre-commit-config.yaml` file:
329
+
330
+ ```yaml
331
+ repos:
332
+ - repo: https://github.com/cycodehq/cycode-cli
333
+ rev: v3.5.0
334
+ hooks:
335
+ - id: cycode-pre-push
336
+ stages: [pre-push]
337
+ ```
338
+
339
+ 2. Install the pre-push hook:
340
+
341
+ ```bash
342
+ pre-commit install --hook-type pre-push
343
+ ```
344
+
345
+ 3. For both pre-commit and pre-push hooks, use:
346
+
347
+ ```bash
348
+ pre-commit install
349
+ pre-commit install --hook-type pre-push
350
+ ```
351
+
352
+ > [!NOTE]
353
+ > Pre-push hooks trigger on `git push` command and scan only the commits about to be pushed.
354
+
325
355
  # Cycode CLI Commands
326
356
 
327
357
  The following are the options and commands available with the Cycode CLI application:
@@ -830,6 +860,107 @@ After installing the pre-commit hook, you may occasionally wish to skip scanning
830
860
  SKIP=cycode git commit -m <your commit message>`
831
861
  ```
832
862
 
863
+ ### Pre-Push Scan
864
+
865
+ A pre-push scan automatically identifies any issues before you push changes to the remote repository. This hook runs on the client side and scans only the commits that are about to be pushed, making it efficient for catching issues before they reach the remote repository.
866
+
867
+ > [!NOTE]
868
+ > Pre-push hook is not available for IaC scans.
869
+
870
+ The pre-push hook integrates with the pre-commit framework and can be configured to run before any `git push` operation.
871
+
872
+ #### Installing Pre-Push Hook
873
+
874
+ To set up the pre-push hook using the pre-commit framework:
875
+
876
+ 1. Install the pre-commit framework (if not already installed):
877
+
878
+ ```bash
879
+ pip3 install pre-commit
880
+ ```
881
+
882
+ 2. Create or update your `.pre-commit-config.yaml` file to include the pre-push hooks:
883
+
884
+ ```yaml
885
+ repos:
886
+ - repo: https://github.com/cycodehq/cycode-cli
887
+ rev: v3.5.0
888
+ hooks:
889
+ - id: cycode-pre-push
890
+ stages: [pre-push]
891
+ ```
892
+
893
+ 3. For multiple scan types, use this configuration:
894
+
895
+ ```yaml
896
+ repos:
897
+ - repo: https://github.com/cycodehq/cycode-cli
898
+ rev: v3.5.0
899
+ hooks:
900
+ - id: cycode-pre-push # Secrets scan
901
+ stages: [pre-push]
902
+ - id: cycode-sca-pre-push # SCA scan
903
+ stages: [pre-push]
904
+ - id: cycode-sast-pre-push # SAST scan
905
+ stages: [pre-push]
906
+ ```
907
+
908
+ 4. Install the pre-push hook:
909
+
910
+ ```bash
911
+ pre-commit install --hook-type pre-push
912
+ ```
913
+
914
+ A successful installation will result in the message: `Pre-push installed at .git/hooks/pre-push`.
915
+
916
+ 5. Keep the pre-push hook up to date:
917
+
918
+ ```bash
919
+ pre-commit autoupdate
920
+ ```
921
+
922
+ #### How Pre-Push Scanning Works
923
+
924
+ The pre-push hook:
925
+ - Receives information about what commits are being pushed
926
+ - Calculates the appropriate commit range to scan
927
+ - For new branches: scans all commits from the merge base with the default branch
928
+ - For existing branches: scans only the new commits since the last push
929
+ - Runs the same comprehensive scanning as other Cycode scan modes
930
+
931
+ #### Smart Default Branch Detection
932
+
933
+ The pre-push hook intelligently detects the default branch for merge base calculation using this priority order:
934
+
935
+ 1. **Environment Variable**: `CYCODE_DEFAULT_BRANCH` - allows manual override
936
+ 2. **Git Remote HEAD**: Uses `git symbolic-ref refs/remotes/origin/HEAD` to detect the actual remote default branch
937
+ 3. **Git Remote Info**: Falls back to `git remote show origin` if symbolic-ref fails
938
+ 4. **Hardcoded Fallbacks**: Uses common default branch names (origin/main, origin/master, main, master)
939
+
940
+ **Setting a Custom Default Branch:**
941
+ ```bash
942
+ export CYCODE_DEFAULT_BRANCH=origin/develop
943
+ ```
944
+
945
+ This smart detection ensures the pre-push hook works correctly regardless of whether your repository uses `main`, `master`, `develop`, or any other default branch name.
946
+
947
+ #### Skipping Pre-Push Scans
948
+
949
+ To skip the pre-push scan for a specific push operation, use:
950
+
951
+ ```bash
952
+ SKIP=cycode-pre-push git push
953
+ ```
954
+
955
+ Or to skip all pre-push hooks:
956
+
957
+ ```bash
958
+ git push --no-verify
959
+ ```
960
+
961
+ > [!TIP]
962
+ > The pre-push hook is triggered on `git push` command and scans only the commits that are about to be pushed, making it more efficient than scanning the entire repository.
963
+
833
964
  ## Scan Results
834
965
 
835
966
  Each scan will complete with a message stating if any issues were found or not.
@@ -1,4 +1,4 @@
1
- cycode/__init__.py,sha256=zAIW50L6NyQsZB1PsBUHoMMngFFuQzbuK1ksKCTylUY,114
1
+ cycode/__init__.py,sha256=xkSwL52QCWMEPvCqdH0QLKU8B8toTZK2evWAZt5XQ4w,114
2
2
  cycode/__main__.py,sha256=Z3bD5yrA7yPvAChcADQrqCaZd0ChGI1gdiwALwbWJ6U,104
3
3
  cycode/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  cycode/cli/app.py,sha256=UC5A5TKIvlxOYKERfJykN8apTT0VyMY5pUjRh_LM-dw,6098
@@ -31,7 +31,7 @@ cycode/cli/apps/report/sbom/repository_url/__init__.py,sha256=47DEQpj8HBSa-_TImW
31
31
  cycode/cli/apps/report/sbom/repository_url/repository_url_command.py,sha256=VO4jSR748BEpCuOrAvOK4_rNLw63lO4iHCTfWkWdfMQ,2179
32
32
  cycode/cli/apps/report/sbom/sbom_command.py,sha256=bykQnmO0CCNInkih6bGmCcq5HFH-ItkFHPoxz683HCc,2229
33
33
  cycode/cli/apps/report/sbom/sbom_report_file.py,sha256=uyaJRvmg1K4DvJaMppbCf6yCj6UU-NdvNg-ZVZk0jx4,1576
34
- cycode/cli/apps/scan/__init__.py,sha256=yHbx8sLMlE5Sqy0_UQfjDws9FMy0n1RTdG6IsRoN4Cw,1990
34
+ cycode/cli/apps/scan/__init__.py,sha256=-q1AIBnrQ4GP0CVKFLr_2CdWf9TBQC90ejSL4I7rxuA,2444
35
35
  cycode/cli/apps/scan/aggregation_report.py,sha256=8f9kPfO7biNf5OsDZG6UhMPqG6ymoFrX5GBtlEIfFAg,1540
36
36
  cycode/cli/apps/scan/code_scanner.py,sha256=lWAcdtdeOmLe9zaXnItlbL-WIgnJ6d4iUeYiprxUJ34,11205
37
37
  cycode/cli/apps/scan/commit_history/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -42,8 +42,10 @@ cycode/cli/apps/scan/path/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
42
42
  cycode/cli/apps/scan/path/path_command.py,sha256=7M8nnohjB4SaNA7jv3mFODyX1wwwSmROe45e7E5GLbY,670
43
43
  cycode/cli/apps/scan/pre_commit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
44
  cycode/cli/apps/scan/pre_commit/pre_commit_command.py,sha256=bnOITHRmXXgmXQhXKbUqulf4s_Kcvq3x96eMPg7H7-Q,550
45
+ cycode/cli/apps/scan/pre_push/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ cycode/cli/apps/scan/pre_push/pre_push_command.py,sha256=Z0OJc3JDuI-pE1yLYYjuQh-kZhdZIExUPfaub73TZZE,2505
45
47
  cycode/cli/apps/scan/pre_receive/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- cycode/cli/apps/scan/pre_receive/pre_receive_command.py,sha256=A4XJV0BIZB7KYVhkCzxvbDkujMGqv7bHai3j-juUq84,2528
48
+ cycode/cli/apps/scan/pre_receive/pre_receive_command.py,sha256=3gF9Jj0Yo8RW9Q72HQDHX5SogX-xyOaWkVlbzfRVYu0,2537
47
49
  cycode/cli/apps/scan/remote_url_resolver.py,sha256=EzHgV7XaJ3TedY35vDrFVGN7qjpaNl8lnYRNSRs3fvQ,5155
48
50
  cycode/cli/apps/scan/repository/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
51
  cycode/cli/apps/scan/repository/repository_command.py,sha256=YwN8kut3PCw7ZYKIwPu1Ll0SluyygiURjdF8ArUxAXk,2926
@@ -61,7 +63,7 @@ cycode/cli/apps/status/version_command.py,sha256=c6Iko_rmZo9T_kQSd3HUloBi40Qv7cj
61
63
  cycode/cli/cli_types.py,sha256=cI9_XPG9LDofh6e2qyPtegD76KZYzcPwLj8jFK3Kmp4,2790
62
64
  cycode/cli/config.py,sha256=EblYUlUA4lTp_lrL3gMG-cW7FUOTE1jtGIOljcLnEzk,250
63
65
  cycode/cli/console.py,sha256=vp-DHwlkwpwdsPyfwGdjsPF-6-Bi3f8W7G-W_YXCMH8,1914
64
- cycode/cli/consts.py,sha256=9K4uGu0n-SpTP1fMv0xzZGJD2cN301jEgzGG0LYQ5c8,8473
66
+ cycode/cli/consts.py,sha256=_gYe0xKTP2EJDLGPFsW1fCTYxAGnnlyMZTlxY_0Nr2w,8844
65
67
  cycode/cli/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
68
  cycode/cli/exceptions/custom_exceptions.py,sha256=Uh4Lqp4moTIFRTRtnT5b8dqb07L3wSAnAuFonS6-omQ,3610
67
69
  cycode/cli/exceptions/handle_ai_remediation_errors.py,sha256=mA70upSYXK3rL_fmanzKYeUzLENhpXdkW8k3aIHrKzU,785
@@ -70,7 +72,7 @@ cycode/cli/exceptions/handle_errors.py,sha256=9ZiDbHswXLe0TscUqZL9Or5Jq2AlYtzGb6
70
72
  cycode/cli/exceptions/handle_report_sbom_errors.py,sha256=bi0EizHtQLL-ovhHRH98CZ7qXdDPLTYnI59Jn1Y5c0E,926
71
73
  cycode/cli/exceptions/handle_scan_errors.py,sha256=-QIYvbBXmZVOvAdNwGYwAdmBma6Z_pPpS0a77aDICp8,1916
72
74
  cycode/cli/files_collector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
- cycode/cli/files_collector/commit_range_documents.py,sha256=yDtUXXZ0Xk0k2Fau4cJ_Dzv1XV5QLLaLVbew2R0nxp4,12919
75
+ cycode/cli/files_collector/commit_range_documents.py,sha256=OYzfT9sAa9Lwq5aQYCExI73yITc1pbOH_UKHwssMcUU,18153
74
76
  cycode/cli/files_collector/file_excluder.py,sha256=FhBnYF889BOwx3fRjNyBaFix6-65jCejglf8MmiVxSk,7300
75
77
  cycode/cli/files_collector/iac/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
78
  cycode/cli/files_collector/iac/tf_content_generator.py,sha256=a65zA0Ejv_LSA5jac2omHck4IKoNS5MX6v6ltF2wo4E,2873
@@ -121,7 +123,7 @@ cycode/cli/printers/utils/rich_helpers.py,sha256=HhPVpxZNBR7qNB_06VbVdshrgAwUIry
121
123
  cycode/cli/user_settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
124
  cycode/cli/user_settings/base_file_manager.py,sha256=SLA5xRMTqSY-PtbeKCtVc9rP_ljxUYe3z3ZU-XE2x0w,844
123
125
  cycode/cli/user_settings/config_file_manager.py,sha256=KqMogXjtgO-ZbEGW0eN_ZUHn3tLQFJteV8zYDgEhIks,4954
124
- cycode/cli/user_settings/configuration_manager.py,sha256=sTJu1Zenxzm397Cbdg-0kN4z1K27dRBJJPfd4TqKP8A,8130
126
+ cycode/cli/user_settings/configuration_manager.py,sha256=8nTogrLRAMXKM13Zd_lvaL9nsfhYRK-IPii25v6ONck,9277
125
127
  cycode/cli/user_settings/credentials_manager.py,sha256=jT8pTjldk6WmDyTJPidYQxREuPiObexaP1HEmYpCcWU,3152
126
128
  cycode/cli/user_settings/jwt_creator.py,sha256=xEkFLFqhwbNJnXuIi02XDxoj2E-4Nw-m10uJaHl3luA,745
127
129
  cycode/cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -157,8 +159,8 @@ cycode/cyclient/report_client.py,sha256=h12pz3vWCwDF73BhqFX7iDSxBgQDFwkiGh3hmul2
157
159
  cycode/cyclient/scan_client.py,sha256=nQJyt34Bne8UAQNj9OHSgvoCfI1EJFKNaEeeGPnrKcg,12471
158
160
  cycode/cyclient/scan_config_base.py,sha256=mXsPZGYCtp85rv5GIige40yQZXuRcEKUW-VQJ0vgFzk,1201
159
161
  cycode/logger.py,sha256=xAzpkWLZhixO4egRcYn4HXM9lIfx5wHdpkHxNc5jrX8,2225
160
- cycode-3.4.3.dev1.dist-info/LICENCE,sha256=2Wx4N6mD_4xB7-E3hPkZ3MPhpJy__k_I8MaCSO-PDRo,1068
161
- cycode-3.4.3.dev1.dist-info/METADATA,sha256=Yx4WIy510N2Od0G0pCxC5zTae_9gHfviLQzKfFpDdyI,71912
162
- cycode-3.4.3.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
163
- cycode-3.4.3.dev1.dist-info/entry_points.txt,sha256=iDcVJM8ByLElVgvBgtYxDjw1kT7O8Mo0LcWZIT5L3Ig,45
164
- cycode-3.4.3.dev1.dist-info/RECORD,,
162
+ cycode-3.4.4.dev1.dist-info/LICENCE,sha256=2Wx4N6mD_4xB7-E3hPkZ3MPhpJy__k_I8MaCSO-PDRo,1068
163
+ cycode-3.4.4.dev1.dist-info/METADATA,sha256=5CfDAJCri6PIBiQmiCqUYn3AosgfDv9lBbfM7threKw,75849
164
+ cycode-3.4.4.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
165
+ cycode-3.4.4.dev1.dist-info/entry_points.txt,sha256=iDcVJM8ByLElVgvBgtYxDjw1kT7O8Mo0LcWZIT5L3Ig,45
166
+ cycode-3.4.4.dev1.dist-info/RECORD,,