From 9ad47bc87b7b618ad0c1e2044526df47b75508f3 Mon Sep 17 00:00:00 2001 From: Aaron Butler Date: Wed, 6 May 2026 14:58:10 -0400 Subject: [PATCH 1/2] CM-63882 - Added scanType validation --- cycode/cli/apps/scan/scan_command.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/cycode/cli/apps/scan/scan_command.py b/cycode/cli/apps/scan/scan_command.py index 9b2aa280..cdeb2a52 100644 --- a/cycode/cli/apps/scan/scan_command.py +++ b/cycode/cli/apps/scan/scan_command.py @@ -31,14 +31,14 @@ def scan_command( ctx: typer.Context, scan_type: Annotated[ - ScanTypeOption, + list[ScanTypeOption], typer.Option( '--scan-type', '-t', help='Specify the type of scan you wish to execute.', case_sensitive=False, ), - ] = ScanTypeOption.SECRET, + ] = (ScanTypeOption.SECRET,), soft_fail: Annotated[ bool, typer.Option('--soft-fail', help='Run the scan without failing; always return a non-error status code.') ] = False, @@ -126,6 +126,16 @@ def scan_command( * `cycode scan commit-history `: Scan the commit history of a local Git repository. """ + if len(scan_type) > 1: + raise typer.BadParameter( + f'Only one scan type can be specified per command. ' + f'Got: {", ".join(str(t) for t in scan_type)}. ' + f'Run a separate command for each scan type.', + param_hint='-t/--scan-type', + ) + + resolved_scan_type = scan_type[0] + if export_file and export_type is None: raise typer.BadParameter( 'Export type must be specified when --export-file is provided.', @@ -140,7 +150,7 @@ def scan_command( ctx.obj['show_secret'] = show_secret ctx.obj['soft_fail'] = soft_fail ctx.obj['stop_on_error'] = stop_on_error - ctx.obj['scan_type'] = scan_type + ctx.obj['scan_type'] = resolved_scan_type ctx.obj['sync'] = sync ctx.obj['severity_threshold'] = severity_threshold ctx.obj['monitor'] = monitor @@ -158,9 +168,9 @@ def scan_command( # Get remote URL from current working directory remote_url = _try_get_git_remote_url(os.getcwd()) - remote_scan_config = scan_client.get_scan_configuration_safe(scan_type, remote_url) + remote_scan_config = scan_client.get_scan_configuration_safe(resolved_scan_type, remote_url) if remote_scan_config: - excluder.apply_scan_config(str(scan_type), remote_scan_config) + excluder.apply_scan_config(str(resolved_scan_type), remote_scan_config) ctx.obj['scan_config'] = remote_scan_config From e182d4b14b4a13bdd4e4472f347af34e5854eab6 Mon Sep 17 00:00:00 2001 From: Aaron Butler Date: Wed, 6 May 2026 15:07:20 -0400 Subject: [PATCH 2/2] CM-63882 - added supporting unit tests --- tests/cli/commands/scan/test_scan_command.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/cli/commands/scan/test_scan_command.py b/tests/cli/commands/scan/test_scan_command.py index de218da5..5ef3a920 100644 --- a/tests/cli/commands/scan/test_scan_command.py +++ b/tests/cli/commands/scan/test_scan_command.py @@ -1,7 +1,9 @@ import click import pytest import typer +from typer.testing import CliRunner +from cycode.cli.app import app from cycode.cli.apps.scan.scan_command import scan_command_result_callback from cycode.cli.consts import ISSUE_DETECTED_STATUS_CODE, NO_ISSUES_STATUS_CODE, SCAN_ERROR_STATUS_CODE @@ -25,6 +27,20 @@ def _invoke_result_callback(ctx: click.Context) -> int: return exc_info.value.exit_code +class TestScanCommand: + def test_multiple_scan_types_rejected(self) -> None: + result = CliRunner().invoke(app, ['scan', '-t', 'iac', '-t', 'sast', 'path', '.']) + assert result.exit_code == 2 + assert '-t/--scan-type' in result.output + assert 'iac' in result.output + assert 'sast' in result.output + + def test_single_scan_type_accepted(self) -> None: + result = CliRunner().invoke(app, ['scan', '-t', 'iac', '--help']) + assert result.exit_code == 0 + assert 'Error' not in result.output + + class TestScanCommandResultCallback: def test_no_issues_no_errors_exits_zero(self) -> None: assert _invoke_result_callback(_make_ctx()) == NO_ISSUES_STATUS_CODE