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
13 changes: 8 additions & 5 deletions cycode/cli/auth/auth_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@


@click.group(
invoke_without_command=True, short_help='Authenticates your machine to associate CLI with your cycode account'
invoke_without_command=True, short_help='Authenticate your machine to associate the CLI with your Cycode account.'
)
@click.pass_context
def authenticate(context: click.Context) -> None:
"""Authenticates your machine."""
if context.invoked_subcommand is not None:
# if it is a subcommand, do nothing
return
Expand All @@ -32,14 +33,16 @@ def authenticate(context: click.Context) -> None:
_handle_exception(context, e)


@authenticate.command(name='check')
@authenticate.command(
name='check', short_help='Checks that your machine is associating the CLI with your Cycode account.'
)
@click.pass_context
def authorization_check(context: click.Context) -> None:
"""Check your machine associating CLI with your cycode account"""
"""Validates that your Cycode account has permission to work with the CLI."""
printer = ConsolePrinter(context)

passed_auth_check_res = CliResult(success=True, message='You are authorized')
failed_auth_check_res = CliResult(success=False, message='You are not authorized')
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()
if not client_id or not client_secret:
Expand Down
22 changes: 14 additions & 8 deletions cycode/cli/code_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
start_scan_time = time.time()


@click.command(short_help='Scan git repository including its history')
@click.command(short_help='Scan the git repository including its history.')
@click.argument('path', nargs=1, type=click.STRING, required=True)
@click.option(
'--branch',
Expand All @@ -72,6 +72,7 @@ def scan_repository(context: click.Context, path: str, branch: str) -> None:
raise click.ClickException('Monitor flag is currently supported for SCA scan type only')

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

file_entries = list(get_git_repository_tree_file_entries(path, branch))
progress_bar.set_section_length(ProgressBarSection.PREPARE_LOCAL_FILES, len(file_entries))
Expand All @@ -96,7 +97,7 @@ def scan_repository(context: click.Context, path: str, branch: str) -> None:
_handle_exception(context, e)


@click.command(short_help='Scan all the commits history in this git repository')
@click.command(short_help='Scan all the commits history in this git repository.')
@click.argument('path', nargs=1, type=click.STRING, required=True)
@click.option(
'--commit_range',
Expand All @@ -119,7 +120,9 @@ def scan_commit_range(
context: click.Context, path: str, commit_range: str, max_commits_count: Optional[int] = None
) -> None:
scan_type = context.obj['scan_type']

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

if scan_type not in consts.COMMIT_RANGE_SCAN_SUPPORTED_SCAN_TYPES:
raise click.ClickException(f'Commit range scanning for {str.upper(scan_type)} is not supported')
Expand Down Expand Up @@ -185,13 +188,14 @@ def scan_ci(context: click.Context) -> None:
scan_commit_range(context, path=os.getcwd(), commit_range=get_commit_range())


@click.command(short_help='Scan the files in the path supplied in the command')
@click.command(short_help='Scan the files in the path provided in the command.')
@click.argument('path', nargs=1, type=click.STRING, required=True)
@click.pass_context
def scan_path(context: click.Context, path: str) -> None:
logger.debug('Starting path scan process, %s', {'path': path})

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

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

all_files_to_scan = get_relevant_files_in_path(path=path, exclude_patterns=['**/.git/**', '**/.cycode/**'])

Expand All @@ -218,12 +222,14 @@ def scan_path(context: click.Context, path: str) -> None:
scan_disk_files(context, path, relevant_files_to_scan)


@click.command(short_help='Use this command to scan the content that was not committed yet')
@click.command(short_help='Use this command to scan any content that was not committed yet.')
@click.argument('ignored_args', nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def pre_commit_scan(context: click.Context, ignored_args: List[str]) -> None:
scan_type = context.obj['scan_type']

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

if scan_type == consts.SCA_SCAN_TYPE:
scan_sca_pre_commit(context)
Expand All @@ -242,7 +248,7 @@ def pre_commit_scan(context: click.Context, ignored_args: List[str]) -> None:
scan_documents(context, documents_to_scan, is_git_diff=True)


@click.command(short_help='Use this command to scan commits on the server side before pushing them to the repository')
@click.command(short_help='Use this command to scan commits on the server side before pushing them to the repository.')
@click.argument('ignored_args', nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def pre_receive_scan(context: click.Context, ignored_args: List[str]) -> None:
Expand Down Expand Up @@ -1160,7 +1166,7 @@ def _handle_exception(context: click.Context, e: Exception, *, return_exception:
soft_fail=False,
code='invalid_git_error',
message='The path you supplied does not correlate to a git repository. '
'Should you still wish to scan this path, use: `cycode scan path <path>`',
'If you still wish to scan this path, use: `cycode scan path <path>`',
),
}

Expand Down
4 changes: 4 additions & 0 deletions cycode/cli/consts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
PROGRAM_NAME = 'cycode'
CLI_CONTEXT_SETTINGS = {
'terminal_width': 10**9,
'max_content_width': 10**9,
}

PRE_COMMIT_COMMAND_SCAN_TYPE = 'pre_commit'
PRE_RECEIVE_COMMAND_SCAN_TYPE = 'pre_receive'
Expand Down
51 changes: 20 additions & 31 deletions cycode/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from cycode.cli import code_scanner
from cycode.cli.auth.auth_command import authenticate
from cycode.cli.config import config
from cycode.cli.consts import ISSUE_DETECTED_STATUS_CODE, NO_ISSUES_STATUS_CODE, PROGRAM_NAME
from cycode.cli.consts import CLI_CONTEXT_SETTINGS, ISSUE_DETECTED_STATUS_CODE, NO_ISSUES_STATUS_CODE, PROGRAM_NAME
from cycode.cli.models import Severity
from cycode.cli.user_settings.configuration_manager import ConfigurationManager
from cycode.cli.user_settings.credentials_manager import CredentialsManager
Expand All @@ -25,8 +25,6 @@
if TYPE_CHECKING:
from cycode.cyclient.scan_client import ScanClient

CONTEXT = {}


@click.group(
commands={
Expand All @@ -36,75 +34,68 @@
'pre_commit': code_scanner.pre_commit_scan,
'pre_receive': code_scanner.pre_receive_scan,
},
short_help='Scan content for secrets/IaC/sca/SAST violations. '
'You need to specify which scan type: ci/commit_history/path/repository/etc',
short_help='Scan the content for Secrets/IaC/SCA/SAST violations. '
'You`ll need to specify which scan type to perform: ci/commit_history/path/repository/etc.',
)
@click.option(
'--scan-type',
'-t',
default='secret',
help="""
\b
Specify the scan you wish to execute (secret/iac/sca),
the default is secret
""",
help='Specify the type of scan you wish to execute (the default is Secrets)',
type=click.Choice(config['scans']['supported_scans']),
)
@click.option(
'--secret',
default=None,
help='Specify a Cycode client secret for this specific scan execution',
help='Specify a Cycode client secret for this specific scan execution.',
type=str,
required=False,
)
@click.option(
'--client-id',
default=None,
help='Specify a Cycode client ID for this specific scan execution',
help='Specify a Cycode client ID for this specific scan execution.',
type=str,
required=False,
)
@click.option(
'--show-secret', is_flag=True, default=False, help='Show secrets in plain text', type=bool, required=False
'--show-secret', is_flag=True, default=False, help='Show Secrets in plain text.', type=bool, required=False
)
@click.option(
'--soft-fail',
is_flag=True,
default=False,
help='Run scan without failing, always return a non-error status code',
help='Run the scan without failing; always return a non-error status code.',
type=bool,
required=False,
)
@click.option(
'--severity-threshold',
default=None,
help='Show only violations at the specified level or higher (supported for SCA scan type only).',
help='Show violations only for the specified level or higher (supported for SCA scan types only).',
type=click.Choice([e.name for e in Severity]),
required=False,
)
@click.option(
'--sca-scan',
default=None,
help='Specify the sca scan you wish to execute (package-vulnerabilities/license-compliance), the default is both',
help='Specify the type of SCA scan you wish to execute (the default is both).',
multiple=True,
type=click.Choice(config['scans']['supported_sca_scans']),
)
@click.option(
'--monitor',
is_flag=True,
default=False,
help="When specified, the scan results will be recorded in the knowledge graph. "
"Please note that when working in 'monitor' mode, the knowledge graph "
"will not be updated as a result of SCM events (Push, Repo creation).(supported for SCA scan type only).",
help='Used for SCA scan types only; when specified, the scan results are recorded in the Discovery module.',
type=bool,
required=False,
)
@click.option(
'--report',
is_flag=True,
default=False,
help='When specified, a violations report will be generated. '
'A URL link to the report will be printed as an output to the command execution',
help='When specified, generates a violations report. A link to the report will be displayed in the console output.',
type=bool,
required=False,
)
Expand All @@ -121,6 +112,7 @@ def code_scan(
monitor: bool,
report: bool,
) -> int:
"""Scans for Secrets, IaC, SCA or SAST violations."""
if show_secret:
context.obj['show_secret'] = show_secret
else:
Expand All @@ -139,9 +131,6 @@ def code_scan(

_sca_scan_to_context(context, sca_scan)

context.obj['progress_bar'] = get_progress_bar(hidden=context.obj['no_progress_meter'])
context.obj['progress_bar'].start()

return 1


Expand All @@ -162,7 +151,7 @@ def finalize(context: click.Context, *_, **__) -> None:
sys.exit(exit_code)


@click.command(short_help='Show the version and exit')
@click.command(short_help='Show the CLI version and exit.')
@click.pass_context
def version(context: click.Context) -> None:
output = context.obj['output']
Expand All @@ -186,32 +175,32 @@ def version(context: click.Context) -> None:
'auth': authenticate,
'version': version,
},
context_settings=CONTEXT,
context_settings=CLI_CONTEXT_SETTINGS,
)
@click.option(
'--verbose',
'-v',
is_flag=True,
default=False,
help='Show detailed logs',
help='Show detailed logs.',
)
@click.option(
'--no-progress-meter',
is_flag=True,
default=False,
help='Do not show the progress meter',
help='Do not show the progress meter.',
)
@click.option(
'--output',
'-o',
default='text',
help='Specify the output (text/json/table), the default is text',
help='Specify the output type (the default is text).',
type=click.Choice(['text', 'json', 'table']),
)
@click.option(
'--user-agent',
default=None,
help='Characteristic JSON object that lets servers identify the application',
help='Characteristic JSON object that lets servers identify the application.',
type=str,
)
@click.pass_context
Expand All @@ -232,7 +221,7 @@ def main_cli(
if output == 'json':
no_progress_meter = True

context.obj['no_progress_meter'] = no_progress_meter
context.obj['progress_bar'] = get_progress_bar(hidden=no_progress_meter)

if user_agent:
user_agent_option = UserAgentOptionScheme().loads(user_agent)
Expand Down
2 changes: 1 addition & 1 deletion cycode/cli/printers/tables/sca_table_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def _print_results(self, local_scan_results: List['LocalScanResult']) -> None:
@staticmethod
def _get_title(policy_id: str) -> str:
if policy_id == PACKAGE_VULNERABILITY_POLICY_ID:
return 'Dependencies Vulnerabilities'
return 'Dependency Vulnerabilities'
if policy_id == LICENSE_COMPLIANCE_POLICY_ID:
return 'License Compliance'

Expand Down
28 changes: 14 additions & 14 deletions cycode/cli/user_settings/user_settings_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@


@click.command(
short_help='Initial command to authenticate your CLI client with Cycode using client ID and client secret'
short_help='Initial command to authenticate your CLI client with Cycode using a client ID and client secret.'
)
def set_credentials() -> None:
"""Authenticates your CLI client with Cycode manually by using a client ID and client secret."""
click.echo(f'Update credentials in file ({credentials_manager.get_filename()})')
current_client_id, current_client_secret = credentials_manager.get_credentials_from_file()
client_id = _get_client_id_input(current_client_id)
Expand All @@ -37,40 +38,39 @@ def set_credentials() -> None:
click.echo(_get_credentials_update_result_message())


@click.command()
@click.command(short_help='Ignores a specific value, path or rule ID.')
@click.option(
'--by-value', type=click.STRING, required=False, help='Ignore a specific value while scanning for secrets'
'--by-value', type=click.STRING, required=False, help='Ignore a specific value while scanning for Secrets.'
)
@click.option(
'--by-sha',
type=click.STRING,
required=False,
help='Ignore a specific SHA512 representation of a string while scanning for secrets',
help='Ignore a specific SHA512 representation of a string while scanning for Secrets.',
)
@click.option(
'--by-path', type=click.STRING, required=False, help='Avoid scanning a specific path. Need to specify scan type '
'--by-path',
type=click.STRING,
required=False,
help='Avoid scanning a specific path. You`ll need to specify the scan type.',
)
@click.option(
'--by-rule',
type=click.STRING,
required=False,
help='Ignore scanning a specific secret rule ID/IaC rule ID. Need to specify scan type.',
help='Ignore scanning a specific secret rule ID or IaC rule ID. You`ll to specify the scan type.',
)
@click.option(
'--by-package',
type=click.STRING,
required=False,
help='Ignore scanning a specific package version while running SCA scan. expected pattern - name@version',
help='Ignore scanning a specific package version while running an SCA scan. Expected pattern: name@version.',
)
@click.option(
'--scan-type',
'-t',
default='secret',
help="""
\b
Specify the scan you wish to execute (secrets/iac),
the default is secrets
""",
help='Specify the type of scan you wish to execute (the default is Secrets).',
type=click.Choice(config['scans']['supported_scans']),
required=False,
)
Expand All @@ -81,12 +81,12 @@ def set_credentials() -> None:
is_flag=True,
default=False,
required=False,
help='Add an ignore rule and update it in the global .cycode config file',
help='Add an ignore rule to the global CLI config.',
)
def add_exclusions(
by_value: str, by_sha: str, by_path: str, by_rule: str, by_package: str, scan_type: str, is_global: bool
) -> None:
"""Ignore a specific value, path or rule ID"""
"""Ignores a specific value, path or rule ID."""
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