Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,24 @@
language_version: python3
entry: cycode
args: [ '-o', 'text', '--no-progress-meter', 'scan', '-t', 'sast', 'pre-commit' ]
- id: cycode-pre-push
name: Cycode Secrets pre-push defender
language: python
language_version: python3
entry: cycode
args: [ '-o', 'text', '--no-progress-meter', 'scan', '-t', 'secret', 'pre-push' ]
stages: [pre-push]
- id: cycode-sca-pre-push
name: Cycode SCA pre-push defender
language: python
language_version: python3
entry: cycode
args: [ '-o', 'text', '--no-progress-meter', 'scan', '-t', 'sca', 'pre-push' ]
stages: [pre-push]
- id: cycode-sast-pre-push
name: Cycode SAST pre-push defender
language: python
language_version: python3
entry: cycode
args: [ '-o', 'text', '--no-progress-meter', 'scan', '-t', 'sast', 'pre-push' ]
stages: [pre-push]
155 changes: 143 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This guide walks you through both installation and usage.
4. [Commit History Scan](#commit-history-scan)
1. [Commit Range Option (Diff Scanning)](#commit-range-option-diff-scanning)
5. [Pre-Commit Scan](#pre-commit-scan)
6. [Pre-Push Scan](#pre-push-scan)
2. [Scan Results](#scan-results)
1. [Show/Hide Secrets](#showhide-secrets)
2. [Soft Fail](#soft-fail)
Expand Down Expand Up @@ -213,13 +214,15 @@ export CYCODE_CLIENT_SECRET={your Cycode Secret Key}

## Install Pre-Commit Hook

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.
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.

> [!NOTE]
> pre-commit hook is not available for IaC scans.
> pre-commit and pre-push hooks are not available for IaC scans.

Perform the following steps to install the pre-commit hook:

### Installing Pre-Commit Hook

1. Install the pre-commit framework (Python 3.9 or higher must be installed):

```bash
Expand All @@ -233,29 +236,25 @@ Perform the following steps to install the pre-commit hook:
```yaml
repos:
- repo: https://github.com/cycodehq/cycode-cli
rev: v3.4.2
rev: v3.5.0
hooks:
- id: cycode
stages:
- pre-commit
stages: [pre-commit]
```

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:

```yaml
repos:
- repo: https://github.com/cycodehq/cycode-cli
rev: v3.4.2
rev: v3.5.0
hooks:
- id: cycode
stages:
- pre-commit
stages: [pre-commit]
- id: cycode-sca
stages:
- pre-commit
stages: [pre-commit]
- id: cycode-sast
stages:
- pre-commit
stages: [pre-commit]
```

5. Install Cycode’s hook:
Expand All @@ -278,6 +277,37 @@ Perform the following steps to install the pre-commit hook:
> Trigger happens on `git commit` command.
> Hook triggers only on the files that are staged for commit.

### Installing Pre-Push Hook

To install the pre-push hook in addition to or instead of the pre-commit hook:

1. Add the pre-push hooks to your `.pre-commit-config.yaml` file:

```yaml
repos:
- repo: https://github.com/cycodehq/cycode-cli
rev: v3.5.0
hooks:
- id: cycode-pre-push
stages: [pre-push]
```

2. Install the pre-push hook:

```bash
pre-commit install --hook-type pre-push
```

3. For both pre-commit and pre-push hooks, use:

```bash
pre-commit install
pre-commit install --hook-type pre-push
```

> [!NOTE]
> Pre-push hooks trigger on `git push` command and scan only the commits about to be pushed.

# Cycode CLI Commands

The following are the options and commands available with the Cycode CLI application:
Expand Down Expand Up @@ -786,6 +816,107 @@ After installing the pre-commit hook, you may occasionally wish to skip scanning
SKIP=cycode git commit -m <your commit message>`
```

### Pre-Push Scan

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.

> [!NOTE]
> Pre-push hook is not available for IaC scans.

The pre-push hook integrates with the pre-commit framework and can be configured to run before any `git push` operation.

#### Installing Pre-Push Hook

To set up the pre-push hook using the pre-commit framework:

1. Install the pre-commit framework (if not already installed):

```bash
pip3 install pre-commit
```

2. Create or update your `.pre-commit-config.yaml` file to include the pre-push hooks:

```yaml
repos:
- repo: https://github.com/cycodehq/cycode-cli
rev: v3.5.0
hooks:
- id: cycode-pre-push
stages: [pre-push]
```

3. For multiple scan types, use this configuration:

```yaml
repos:
- repo: https://github.com/cycodehq/cycode-cli
rev: v3.5.0
hooks:
- id: cycode-pre-push # Secrets scan
stages: [pre-push]
- id: cycode-sca-pre-push # SCA scan
stages: [pre-push]
- id: cycode-sast-pre-push # SAST scan
stages: [pre-push]
```

4. Install the pre-push hook:

```bash
pre-commit install --hook-type pre-push
```

A successful installation will result in the message: `Pre-push installed at .git/hooks/pre-push`.

5. Keep the pre-push hook up to date:

```bash
pre-commit autoupdate
```

#### How Pre-Push Scanning Works

The pre-push hook:
- Receives information about what commits are being pushed
- Calculates the appropriate commit range to scan
- For new branches: scans all commits from the merge base with the default branch
- For existing branches: scans only the new commits since the last push
- Runs the same comprehensive scanning as other Cycode scan modes

#### Smart Default Branch Detection

The pre-push hook intelligently detects the default branch for merge base calculation using this priority order:

1. **Environment Variable**: `CYCODE_DEFAULT_BRANCH` - allows manual override
2. **Git Remote HEAD**: Uses `git symbolic-ref refs/remotes/origin/HEAD` to detect the actual remote default branch
3. **Git Remote Info**: Falls back to `git remote show origin` if symbolic-ref fails
4. **Hardcoded Fallbacks**: Uses common default branch names (origin/main, origin/master, main, master)

**Setting a Custom Default Branch:**
```bash
export CYCODE_DEFAULT_BRANCH=origin/develop
```

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.

#### Skipping Pre-Push Scans

To skip the pre-push scan for a specific push operation, use:

```bash
SKIP=cycode-pre-push git push
```

Or to skip all pre-push hooks:

```bash
git push --no-verify
```

> [!TIP]
> 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.

## Scan Results

Each scan will complete with a message stating if any issues were found or not.
Expand Down
13 changes: 11 additions & 2 deletions cycode/cli/apps/scan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
from cycode.cli.apps.scan.commit_history.commit_history_command import commit_history_command
from cycode.cli.apps.scan.path.path_command import path_command
from cycode.cli.apps.scan.pre_commit.pre_commit_command import pre_commit_command
from cycode.cli.apps.scan.pre_push.pre_push_command import pre_push_command
from cycode.cli.apps.scan.pre_receive.pre_receive_command import pre_receive_command
from cycode.cli.apps.scan.repository.repository_command import repository_command
from cycode.cli.apps.scan.scan_command import scan_command, scan_command_result_callback

app = typer.Typer(name='scan', no_args_is_help=True)

_AUTOMATION_COMMANDS_RICH_HELP_PANEL = 'Automation commands'

_scan_command_docs = 'https://github.com/cycodehq/cycode-cli/blob/main/README.md#scan-command'
_scan_command_epilog = f'[bold]Documentation:[/] [link={_scan_command_docs}]{_scan_command_docs}[/link]'

Expand All @@ -26,16 +29,22 @@
app.command(
name='pre-commit',
short_help='Use this command in pre-commit hook to scan any content that was not committed yet.',
rich_help_panel='Automation commands',
rich_help_panel=_AUTOMATION_COMMANDS_RICH_HELP_PANEL,
)(pre_commit_command)
app.command(
name='pre-push',
short_help='Use this command in pre-push hook to scan commits before pushing them to the remote repository.',
rich_help_panel=_AUTOMATION_COMMANDS_RICH_HELP_PANEL,
)(pre_push_command)
app.command(
name='pre-receive',
short_help='Use this command in pre-receive hook '
'to scan commits on the server side before pushing them to the repository.',
rich_help_panel='Automation commands',
rich_help_panel=_AUTOMATION_COMMANDS_RICH_HELP_PANEL,
)(pre_receive_command)

# backward compatibility
app.command(hidden=True, name='commit_history')(commit_history_command)
app.command(hidden=True, name='pre_commit')(pre_commit_command)
app.command(hidden=True, name='pre_push')(pre_push_command)
app.command(hidden=True, name='pre_receive')(pre_receive_command)
Empty file.
68 changes: 68 additions & 0 deletions cycode/cli/apps/scan/pre_push/pre_push_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import logging
import os
from typing import Annotated, Optional

import typer

from cycode.cli import consts
from cycode.cli.apps.scan.commit_range_scanner import (
is_verbose_mode_requested_in_pre_receive_scan,
scan_commit_range,
should_skip_pre_receive_scan,
)
from cycode.cli.config import configuration_manager
from cycode.cli.console import console
from cycode.cli.exceptions.handle_scan_errors import handle_scan_exception
from cycode.cli.files_collector.commit_range_documents import (
calculate_pre_push_commit_range,
parse_pre_push_input,
)
from cycode.cli.logger import logger
from cycode.cli.utils import scan_utils
from cycode.cli.utils.sentry import add_breadcrumb
from cycode.cli.utils.task_timer import TimeoutAfter
from cycode.logger import set_logging_level


def pre_push_command(
ctx: typer.Context,
_: Annotated[Optional[list[str]], typer.Argument(help='Ignored arguments', hidden=True)] = None,
) -> None:
try:
add_breadcrumb('pre_push')

if should_skip_pre_receive_scan():
logger.info(
'A scan has been skipped as per your request. '
'Please note that this may leave your system vulnerable to secrets that have not been detected.'
)
return

if is_verbose_mode_requested_in_pre_receive_scan():
ctx.obj['verbose'] = True
set_logging_level(logging.DEBUG)
logger.debug('Verbose mode enabled: all log levels will be displayed.')

command_scan_type = ctx.info_name
timeout = configuration_manager.get_pre_push_command_timeout(command_scan_type)
with TimeoutAfter(timeout):
push_update_details = parse_pre_push_input()
commit_range = calculate_pre_push_commit_range(push_update_details)
if not commit_range:
logger.info(
'No new commits found for pushed branch, %s',
{'push_update_details': push_update_details},
)
return

scan_commit_range(
ctx=ctx,
repo_path=os.getcwd(),
commit_range=commit_range,
max_commits_count=configuration_manager.get_pre_push_max_commits_to_scan_count(command_scan_type),
)

if scan_utils.is_scan_failed(ctx):
console.print(consts.PRE_RECEIVE_AND_PUSH_REMEDIATION_MESSAGE)
except Exception as e:
handle_scan_exception(ctx, e)
2 changes: 1 addition & 1 deletion cycode/cli/apps/scan/pre_receive/pre_receive_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ def pre_receive_command(
)

if scan_utils.is_scan_failed(ctx):
console.print(consts.PRE_RECEIVE_REMEDIATION_MESSAGE)
console.print(consts.PRE_RECEIVE_AND_PUSH_REMEDIATION_MESSAGE)
except Exception as e:
handle_scan_exception(ctx, e)
9 changes: 8 additions & 1 deletion cycode/cli/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,14 @@
DEFAULT_PRE_RECEIVE_MAX_COMMITS_TO_SCAN_COUNT = 50
PRE_RECEIVE_COMMAND_TIMEOUT_ENV_VAR_NAME = 'PRE_RECEIVE_COMMAND_TIMEOUT'
DEFAULT_PRE_RECEIVE_COMMAND_TIMEOUT_IN_SECONDS = 60
PRE_RECEIVE_REMEDIATION_MESSAGE = """
# pre push scan
PRE_PUSH_MAX_COMMITS_TO_SCAN_COUNT_ENV_VAR_NAME = 'PRE_PUSH_MAX_COMMITS_TO_SCAN_COUNT'
DEFAULT_PRE_PUSH_MAX_COMMITS_TO_SCAN_COUNT = 50
PRE_PUSH_COMMAND_TIMEOUT_ENV_VAR_NAME = 'PRE_PUSH_COMMAND_TIMEOUT'
DEFAULT_PRE_PUSH_COMMAND_TIMEOUT_IN_SECONDS = 60
CYCODE_DEFAULT_BRANCH_ENV_VAR_NAME = 'CYCODE_DEFAULT_BRANCH'
# pre push and pre receive common
PRE_RECEIVE_AND_PUSH_REMEDIATION_MESSAGE = """
Cycode Secrets Push Protection
------------------------------------------------------------------------------
Resolve the following secrets by rewriting your local commit history before pushing again.
Expand Down
Loading
Loading