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
25 changes: 22 additions & 3 deletions cycode/cli/commands/auth/auth_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
from cycode.cli.exceptions.custom_exceptions import AuthProcessError, HttpUnauthorizedError, NetworkError
from cycode.cli.models import CliError, CliErrors, CliResult
from cycode.cli.printers import ConsolePrinter
from cycode.cli.sentry import add_breadcrumb, capture_exception
from cycode.cli.user_settings.credentials_manager import CredentialsManager
from cycode.cli.utils.jwt_utils import get_user_and_tenant_ids_from_access_token
from cycode.cyclient import logger
from cycode.cyclient.cycode_token_based_client import CycodeTokenBasedClient

Expand All @@ -15,6 +17,8 @@
@click.pass_context
def auth_command(context: click.Context) -> None:
"""Authenticates your machine."""
add_breadcrumb('auth')

if context.invoked_subcommand is not None:
# if it is a subcommand, do nothing
return
Expand All @@ -37,9 +41,10 @@ def auth_command(context: click.Context) -> None:
@click.pass_context
def authorization_check(context: click.Context) -> None:
"""Validates that your Cycode account has permission to work with the CLI."""
add_breadcrumb('check')

printer = ConsolePrinter(context)

passed_auth_check_res = CliResult(success=True, message='Cycode authentication verified')
failed_auth_check_res = CliResult(success=False, message='Cycode authentication failed')

client_id, client_secret = CredentialsManager().get_credentials()
Expand All @@ -48,9 +53,21 @@ def authorization_check(context: click.Context) -> None:
return

try:
if CycodeTokenBasedClient(client_id, client_secret).get_access_token():
printer.print_result(passed_auth_check_res)
access_token = CycodeTokenBasedClient(client_id, client_secret).get_access_token()
if not access_token:
printer.print_result(failed_auth_check_res)
return

user_id, tenant_id = get_user_and_tenant_ids_from_access_token(access_token)
printer.print_result(
CliResult(
success=True,
message='Cycode authentication verified',
data={'user_id': user_id, 'tenant_id': tenant_id},
)
)

return
except (NetworkError, HttpUnauthorizedError):
ConsolePrinter(context).print_exception()

Expand Down Expand Up @@ -78,4 +95,6 @@ def _handle_exception(context: click.Context, e: Exception) -> None:
if isinstance(e, click.ClickException):
raise e

capture_exception(e)

raise click.ClickException(str(e))
3 changes: 3 additions & 0 deletions cycode/cli/commands/configure/configure_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import click

from cycode.cli import config, consts
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.user_settings.configuration_manager import ConfigurationManager
from cycode.cli.user_settings.credentials_manager import CredentialsManager
from cycode.cli.utils.string_utils import obfuscate_text
Expand All @@ -26,6 +27,8 @@
@click.command(short_help='Initial command to configure your CLI client authentication.')
def configure_command() -> None:
"""Configure your CLI client authentication manually."""
add_breadcrumb('configure')

global_config_manager = _CONFIGURATION_MANAGER.global_config_file_manager

current_api_url = global_config_manager.get_api_url()
Expand Down
3 changes: 3 additions & 0 deletions cycode/cli/commands/ignore/ignore_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from cycode.cli import consts
from cycode.cli.config import config, configuration_manager
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.utils.path_utils import get_absolute_path
from cycode.cli.utils.string_utils import hash_string_to_sha256
from cycode.cyclient import logger
Expand Down Expand Up @@ -67,6 +68,8 @@ def ignore_command(
by_value: str, by_sha: str, by_path: str, by_rule: str, by_package: str, scan_type: str, is_global: bool
) -> None:
"""Ignores a specific value, path or rule ID."""
add_breadcrumb('ignore')

if not by_value and not by_sha and not by_path and not by_rule and not by_package:
raise click.ClickException('ignore by type is missing')

Expand Down
2 changes: 2 additions & 0 deletions cycode/cli/commands/report/report_command.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import click

from cycode.cli.commands.report.sbom.sbom_command import sbom_command
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.utils.progress_bar import SBOM_REPORT_PROGRESS_BAR_SECTIONS, get_progress_bar


Expand All @@ -15,5 +16,6 @@ def report_command(
context: click.Context,
) -> int:
"""Generate report."""
add_breadcrumb('report')
context.obj['progress_bar'] = get_progress_bar(hidden=False, sections=SBOM_REPORT_PROGRESS_BAR_SECTIONS)
return 1
3 changes: 3 additions & 0 deletions cycode/cli/commands/report/sbom/path/path_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from cycode.cli.files_collector.path_documents import get_relevant_documents
from cycode.cli.files_collector.sca.sca_code_scanner import perform_pre_scan_documents_actions
from cycode.cli.files_collector.zip_documents import zip_documents
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.utils.get_api_client import get_report_cycode_client
from cycode.cli.utils.progress_bar import SbomReportProgressBarSection

Expand All @@ -16,6 +17,8 @@
@click.argument('path', nargs=1, type=click.Path(exists=True, resolve_path=True), required=True)
@click.pass_context
def path_command(context: click.Context, path: str) -> None:
add_breadcrumb('path')

client = get_report_cycode_client()
report_parameters = context.obj['report_parameters']
output_format = report_parameters.output_format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from cycode.cli.commands.report.sbom.common import create_sbom_report, send_report_feedback
from cycode.cli.exceptions.handle_report_sbom_errors import handle_report_exception
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.utils.get_api_client import get_report_cycode_client
from cycode.cli.utils.progress_bar import SbomReportProgressBarSection

Expand All @@ -12,6 +13,8 @@
@click.argument('uri', nargs=1, type=str, required=True)
@click.pass_context
def repository_url_command(context: click.Context, uri: str) -> None:
add_breadcrumb('repository_url')

progress_bar = context.obj['progress_bar']
progress_bar.start()
progress_bar.set_section_length(SbomReportProgressBarSection.PREPARE_LOCAL_FILES)
Expand Down
3 changes: 3 additions & 0 deletions cycode/cli/commands/report/sbom/sbom_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from cycode.cli.commands.report.sbom.path.path_command import path_command
from cycode.cli.commands.report.sbom.repository_url.repository_url_command import repository_url_command
from cycode.cli.config import config
from cycode.cli.sentry import add_breadcrumb
from cycode.cyclient.report_client import ReportParameters


Expand Down Expand Up @@ -64,6 +65,8 @@ def sbom_command(
include_dev_dependencies: bool,
) -> int:
"""Generate SBOM report."""
add_breadcrumb('sbom')

sbom_format_parts = format.split('-')
if len(sbom_format_parts) != 2:
raise click.ClickException('Invalid SBOM format.')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from cycode.cli.commands.scan.code_scanner import scan_commit_range
from cycode.cli.exceptions.handle_scan_errors import handle_scan_exception
from cycode.cli.sentry import add_breadcrumb
from cycode.cyclient import logger


Expand All @@ -18,6 +19,8 @@
@click.pass_context
def commit_history_command(context: click.Context, path: str, commit_range: str) -> None:
try:
add_breadcrumb('commit_history')

logger.debug('Starting commit history scan process, %s', {'path': path, 'commit_range': commit_range})
scan_commit_range(context, path=path, commit_range=commit_range)
except Exception as e:
Expand Down
3 changes: 3 additions & 0 deletions cycode/cli/commands/scan/path/path_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import click

from cycode.cli.commands.scan.code_scanner import scan_disk_files
from cycode.cli.sentry import add_breadcrumb
from cycode.cyclient import logger


@click.command(short_help='Scan the files in the path provided in the command.')
@click.argument('paths', nargs=-1, type=click.Path(exists=True, resolve_path=True), required=True)
@click.pass_context
def path_command(context: click.Context, paths: Tuple[str]) -> None:
add_breadcrumb('path')

progress_bar = context.obj['progress_bar']
progress_bar.start()

Expand Down
3 changes: 3 additions & 0 deletions cycode/cli/commands/scan/pre_commit/pre_commit_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
get_diff_file_path,
)
from cycode.cli.models import Document
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.utils.git_proxy import git_proxy
from cycode.cli.utils.path_utils import (
get_path_by_os,
Expand All @@ -22,6 +23,8 @@
@click.argument('ignored_args', nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def pre_commit_command(context: click.Context, ignored_args: List[str]) -> None:
add_breadcrumb('pre_commit')

scan_type = context.obj['scan_type']

progress_bar = context.obj['progress_bar']
Expand Down
3 changes: 3 additions & 0 deletions cycode/cli/commands/scan/pre_receive/pre_receive_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from cycode.cli.files_collector.repository_documents import (
calculate_pre_receive_commit_range,
)
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.utils.task_timer import TimeoutAfter
from cycode.cyclient import logger

Expand All @@ -26,6 +27,8 @@
@click.pass_context
def pre_receive_command(context: click.Context, ignored_args: List[str]) -> None:
try:
add_breadcrumb('pre_receive')

scan_type = context.obj['scan_type']
if scan_type != consts.SECRET_SCAN_TYPE:
raise click.ClickException(f'Commit range scanning for {scan_type.upper()} is not supported')
Expand Down
3 changes: 3 additions & 0 deletions cycode/cli/commands/scan/repository/repository_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from cycode.cli.files_collector.repository_documents import get_git_repository_tree_file_entries
from cycode.cli.files_collector.sca.sca_code_scanner import perform_pre_scan_documents_actions
from cycode.cli.models import Document
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.utils.path_utils import get_path_by_os
from cycode.cli.utils.progress_bar import ScanProgressBarSection
from cycode.cyclient import logger
Expand All @@ -27,6 +28,8 @@
@click.pass_context
def repository_command(context: click.Context, path: str, branch: str) -> None:
try:
add_breadcrumb('repository')

logger.debug('Starting repository scan process, %s', {'path': path, 'branch': branch})

scan_type = context.obj['scan_type']
Expand Down
2 changes: 2 additions & 0 deletions cycode/cli/commands/scan/scan_ci/scan_ci_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from cycode.cli.commands.scan.code_scanner import scan_commit_range
from cycode.cli.commands.scan.scan_ci.ci_integrations import get_commit_range
from cycode.cli.sentry import add_breadcrumb

# This command is not finished yet. It is not used in the codebase.

Expand All @@ -14,4 +15,5 @@
)
@click.pass_context
def scan_ci_command(context: click.Context) -> None:
add_breadcrumb('ci')
scan_commit_range(context, path=os.getcwd(), commit_range=get_commit_range())
5 changes: 5 additions & 0 deletions cycode/cli/commands/scan/scan_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
SCA_SKIP_RESTORE_DEPENDENCIES_FLAG,
)
from cycode.cli.models import Severity
from cycode.cli.sentry import add_breadcrumb
from cycode.cli.utils import scan_utils
from cycode.cli.utils.get_api_client import get_scan_cycode_client

Expand Down Expand Up @@ -124,6 +125,8 @@ def scan_command(
sync: bool,
) -> int:
"""Scans for Secrets, IaC, SCA or SAST violations."""
add_breadcrumb('scan')

if show_secret:
context.obj['show_secret'] = show_secret
else:
Expand Down Expand Up @@ -155,6 +158,8 @@ def _sca_scan_to_context(context: click.Context, sca_scan_user_selected: List[st
@scan_command.result_callback()
@click.pass_context
def finalize(context: click.Context, *_, **__) -> None:
add_breadcrumb('scan_finalize')

progress_bar = context.obj.get('progress_bar')
if progress_bar:
progress_bar.stop()
Expand Down
9 changes: 9 additions & 0 deletions cycode/cli/consts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
PROGRAM_NAME = 'cycode'
APP_NAME = 'CycodeCLI'
CLI_CONTEXT_SETTINGS = {
'terminal_width': 10**9,
'max_content_width': 10**9,
Expand Down Expand Up @@ -142,6 +143,14 @@
SCAN_BATCH_MAX_PARALLEL_SCANS = 5
SCAN_BATCH_SCANS_PER_CPU = 1

# sentry
SENTRY_DSN = 'https://5e26b304b30ced3a34394b6f81f1076d@o1026942.ingest.us.sentry.io/4507543840096256'
SENTRY_DEBUG = False
SENTRY_SAMPLE_RATE = 1.0
SENTRY_SEND_DEFAULT_PII = False
SENTRY_INCLUDE_LOCAL_VARIABLES = False
SENTRY_MAX_REQUEST_BODY_SIZE = 'never'

# report with polling
REPORT_POLLING_WAIT_INTERVAL_IN_SECONDS = 5
DEFAULT_REPORT_POLLING_TIMEOUT_IN_SECONDS = 600
Expand Down
3 changes: 3 additions & 0 deletions cycode/cli/exceptions/handle_report_sbom_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from cycode.cli.exceptions import custom_exceptions
from cycode.cli.models import CliError, CliErrors
from cycode.cli.printers import ConsolePrinter
from cycode.cli.sentry import capture_exception


def handle_report_exception(context: click.Context, err: Exception) -> Optional[CliError]:
Expand Down Expand Up @@ -42,4 +43,6 @@ def handle_report_exception(context: click.Context, err: Exception) -> Optional[
if isinstance(err, click.ClickException):
raise err

capture_exception(err)

raise click.ClickException(str(err))
10 changes: 6 additions & 4 deletions cycode/cli/exceptions/handle_scan_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from cycode.cli.exceptions import custom_exceptions
from cycode.cli.models import CliError, CliErrors
from cycode.cli.printers import ConsolePrinter
from cycode.cli.sentry import capture_exception
from cycode.cli.utils.git_proxy import git_proxy


Expand Down Expand Up @@ -69,13 +70,14 @@ def handle_scan_exception(
ConsolePrinter(context).print_error(error)
return None

unknown_error = CliError(code='unknown_error', message=str(e))
if isinstance(e, click.ClickException):
raise e

capture_exception(e)

unknown_error = CliError(code='unknown_error', message=str(e))
if return_exception:
return unknown_error

if isinstance(e, click.ClickException):
raise e

ConsolePrinter(context).print_error(unknown_error)
exit(1)
4 changes: 4 additions & 0 deletions cycode/cli/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from multiprocessing import freeze_support

from cycode.cli.commands.main_cli import main_cli
from cycode.cli.sentry import add_breadcrumb, init_sentry

if __name__ == '__main__':
# DO NOT REMOVE OR MOVE THIS LINE
# this is required to support multiprocessing in executables files packaged with PyInstaller
# see https://pyinstaller.org/en/latest/common-issues-and-pitfalls.html#multi-processing
freeze_support()

init_sentry()
add_breadcrumb('cycode')

main_cli()
1 change: 1 addition & 0 deletions cycode/cli/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class CliError(NamedTuple):
class CliResult(NamedTuple):
success: bool
message: str
data: Optional[Dict[str, any]] = None


class LocalScanResult(NamedTuple):
Expand Down
2 changes: 1 addition & 1 deletion cycode/cli/printers/json_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class JsonPrinter(PrinterBase):
def print_result(self, result: CliResult) -> None:
result = {'result': result.success, 'message': result.message}
result = {'result': result.success, 'message': result.message, 'data': result.data}

click.echo(self.get_data_json(result))

Expand Down
7 changes: 7 additions & 0 deletions cycode/cli/printers/text_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ def print_result(self, result: CliResult) -> None:

click.secho(result.message, fg=color)

if not result.data:
return

click.secho('\nAdditional data:', fg=color)
for name, value in result.data.items():
click.secho(f'- {name}: {value}', fg=color)

def print_error(self, error: CliError) -> None:
click.secho(error.message, fg=self.RED_COLOR_NAME)

Expand Down
Loading