cycode 2.0.1.dev2__tar.gz → 2.0.1.dev4__tar.gz
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-2.0.1.dev2 → cycode-2.0.1.dev4}/PKG-INFO +3 -1
- cycode-2.0.1.dev4/cycode/__init__.py +1 -0
- cycode-2.0.1.dev4/cycode/cli/commands/ai_remediation/ai_remediation_command.py +67 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/main_cli.py +2 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/version/version_command.py +1 -1
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/consts.py +4 -0
- cycode-2.0.1.dev4/cycode/cli/exceptions/common.py +37 -0
- cycode-2.0.1.dev4/cycode/cli/exceptions/handle_ai_remediation_errors.py +22 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/exceptions/handle_report_sbom_errors.py +3 -20
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/exceptions/handle_scan_errors.py +4 -29
- cycode-2.0.1.dev4/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +25 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/sca_code_scanner.py +2 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/models.py +1 -1
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/console_printer.py +12 -0
- cycode-2.0.1.dev4/cycode/cli/user_settings/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/user_settings/configuration_manager.py +7 -0
- cycode-2.0.1.dev4/cycode/cli/utils/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/models.py +3 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/scan_client.py +22 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/pyproject.toml +3 -1
- cycode-2.0.1.dev2/cycode/__init__.py +0 -1
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/LICENCE +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/README.md +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/__init__.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/auth → cycode-2.0.1.dev4/cycode/cli/commands/ai_remediation}/__init__.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/configure → cycode-2.0.1.dev4/cycode/cli/commands/auth}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/auth/auth_command.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/auth/auth_manager.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/auth_common.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/ignore → cycode-2.0.1.dev4/cycode/cli/commands/configure}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/configure/configure_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/report → cycode-2.0.1.dev4/cycode/cli/commands/ignore}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/ignore/ignore_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/report/sbom → cycode-2.0.1.dev4/cycode/cli/commands/report}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/report/report_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/report/sbom/path → cycode-2.0.1.dev4/cycode/cli/commands/report/sbom}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/report/sbom/common.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/report/sbom/repository_url → cycode-2.0.1.dev4/cycode/cli/commands/report/sbom/path}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/report/sbom/path/path_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/scan → cycode-2.0.1.dev4/cycode/cli/commands/report/sbom/repository_url}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/report/sbom/repository_url/repository_url_command.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/report/sbom/sbom_command.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/report/sbom/sbom_report_file.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/scan/commit_history → cycode-2.0.1.dev4/cycode/cli/commands/scan}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/code_scanner.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/scan/path → cycode-2.0.1.dev4/cycode/cli/commands/scan/commit_history}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/commit_history/commit_history_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/scan/pre_commit → cycode-2.0.1.dev4/cycode/cli/commands/scan/path}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/path/path_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/scan/pre_receive → cycode-2.0.1.dev4/cycode/cli/commands/scan/pre_commit}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/pre_commit/pre_commit_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/scan/repository → cycode-2.0.1.dev4/cycode/cli/commands/scan/pre_receive}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/pre_receive/pre_receive_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/scan/scan_ci → cycode-2.0.1.dev4/cycode/cli/commands/scan/repository}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/repository/repository_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/status → cycode-2.0.1.dev4/cycode/cli/commands/scan/scan_ci}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/scan_ci/ci_integrations.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/scan_ci/scan_ci_command.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/scan_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/commands/version → cycode-2.0.1.dev4/cycode/cli/commands/status}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/status/status_command.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/exceptions → cycode-2.0.1.dev4/cycode/cli/commands/version}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/config.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/config.yaml +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector → cycode-2.0.1.dev4/cycode/cli/exceptions}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/exceptions/custom_exceptions.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector/iac → cycode-2.0.1.dev4/cycode/cli/files_collector}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/excluder.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector/models → cycode-2.0.1.dev4/cycode/cli/files_collector/iac}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector/sca → cycode-2.0.1.dev4/cycode/cli/files_collector/models}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/path_documents.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/repository_documents.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector/sca/go → cycode-2.0.1.dev4/cycode/cli/files_collector/sca}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector/sca/maven → cycode-2.0.1.dev4/cycode/cli/files_collector/sca/go}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector/sca/npm → cycode-2.0.1.dev4/cycode/cli/files_collector/sca/maven}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector/sca/nuget → cycode-2.0.1.dev4/cycode/cli/files_collector/sca/npm}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/files_collector/sca/sbt → cycode-2.0.1.dev4/cycode/cli/files_collector/sca/nuget}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/printers/tables → cycode-2.0.1.dev4/cycode/cli/files_collector/sca/ruby}/__init__.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/user_settings → cycode-2.0.1.dev4/cycode/cli/files_collector/sca/sbt}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/zip_documents.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/main.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/json_printer.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/printer_base.py +0 -0
- {cycode-2.0.1.dev2/cycode/cli/utils → cycode-2.0.1.dev4/cycode/cli/printers/tables}/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/tables/table.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/tables/table_models.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/tables/table_printer.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/tables/table_printer_base.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/printers/text_printer.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/sentry.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/user_settings/base_file_manager.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/user_settings/config_file_manager.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/user_settings/credentials_manager.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/user_settings/jwt_creator.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/enum_utils.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/get_api_client.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/git_proxy.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/jwt_utils.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/path_utils.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/progress_bar.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/scan_batch.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/scan_utils.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/shell_executor.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/string_utils.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/task_timer.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/utils/yaml_utils.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/__init__.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/auth_client.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/client_creator.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/config.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/config.yaml +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/config_dev.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/cycode_client.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/cycode_client_base.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/cycode_dev_based_client.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/cycode_token_based_client.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/headers.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/report_client.py +0 -0
- {cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cyclient/scan_config_base.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cycode
|
|
3
|
-
Version: 2.0.1.
|
|
3
|
+
Version: 2.0.1.dev4
|
|
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
|
|
@@ -28,10 +28,12 @@ Requires-Dist: click (>=8.1.0,<8.2.0)
|
|
|
28
28
|
Requires-Dist: colorama (>=0.4.3,<0.5.0)
|
|
29
29
|
Requires-Dist: gitpython (>=3.1.30,<3.2.0)
|
|
30
30
|
Requires-Dist: marshmallow (>=3.15.0,<3.23.0)
|
|
31
|
+
Requires-Dist: patch-ng (==1.18.1)
|
|
31
32
|
Requires-Dist: pathspec (>=0.11.1,<0.13.0)
|
|
32
33
|
Requires-Dist: pyjwt (>=2.8.0,<3.0)
|
|
33
34
|
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
34
35
|
Requires-Dist: requests (>=2.32.2,<3.0)
|
|
36
|
+
Requires-Dist: rich (>=13.9.4,<14)
|
|
35
37
|
Requires-Dist: sentry-sdk (>=2.8.0,<3.0)
|
|
36
38
|
Requires-Dist: texttable (>=1.6.7,<1.8.0)
|
|
37
39
|
Requires-Dist: urllib3 (==1.26.19)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.0.1.dev4' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from patch_ng import fromstring
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.markdown import Markdown
|
|
7
|
+
|
|
8
|
+
from cycode.cli.exceptions.handle_ai_remediation_errors import handle_ai_remediation_exception
|
|
9
|
+
from cycode.cli.models import CliResult
|
|
10
|
+
from cycode.cli.printers import ConsolePrinter
|
|
11
|
+
from cycode.cli.utils.get_api_client import get_scan_cycode_client
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _echo_remediation(context: click.Context, remediation_markdown: str, is_fix_available: bool) -> None:
|
|
15
|
+
printer = ConsolePrinter(context)
|
|
16
|
+
if printer.is_json_printer:
|
|
17
|
+
data = {'remediation': remediation_markdown, 'is_fix_available': is_fix_available}
|
|
18
|
+
printer.print_result(CliResult(success=True, message='Remediation fetched successfully', data=data))
|
|
19
|
+
else: # text or table
|
|
20
|
+
Console().print(Markdown(remediation_markdown))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _apply_fix(context: click.Context, diff: str, is_fix_available: bool) -> None:
|
|
24
|
+
printer = ConsolePrinter(context)
|
|
25
|
+
if not is_fix_available:
|
|
26
|
+
printer.print_result(CliResult(success=False, message='Fix is not available for this violation'))
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
patch = fromstring(diff.encode('UTF-8'))
|
|
30
|
+
if patch is False:
|
|
31
|
+
printer.print_result(CliResult(success=False, message='Failed to parse fix diff'))
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
is_fix_applied = patch.apply(root=os.getcwd(), strip=0)
|
|
35
|
+
if is_fix_applied:
|
|
36
|
+
printer.print_result(CliResult(success=True, message='Fix applied successfully'))
|
|
37
|
+
else:
|
|
38
|
+
printer.print_result(CliResult(success=False, message='Failed to apply fix'))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@click.command(short_help='Get AI remediation (INTERNAL).', hidden=True)
|
|
42
|
+
@click.argument('detection_id', nargs=1, type=click.UUID, required=True)
|
|
43
|
+
@click.option(
|
|
44
|
+
'--fix',
|
|
45
|
+
is_flag=True,
|
|
46
|
+
default=False,
|
|
47
|
+
help='Apply fixes to resolve violations. Fix is not available for all violations.',
|
|
48
|
+
type=click.BOOL,
|
|
49
|
+
required=False,
|
|
50
|
+
)
|
|
51
|
+
@click.pass_context
|
|
52
|
+
def ai_remediation_command(context: click.Context, detection_id: str, fix: bool) -> None:
|
|
53
|
+
client = get_scan_cycode_client()
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
remediation_markdown = client.get_ai_remediation(detection_id)
|
|
57
|
+
fix_diff = client.get_ai_remediation(detection_id, fix=True)
|
|
58
|
+
is_fix_available = bool(fix_diff) # exclude empty string, None, etc.
|
|
59
|
+
|
|
60
|
+
if fix:
|
|
61
|
+
_apply_fix(context, fix_diff, is_fix_available)
|
|
62
|
+
else:
|
|
63
|
+
_echo_remediation(context, remediation_markdown, is_fix_available)
|
|
64
|
+
except Exception as err:
|
|
65
|
+
handle_ai_remediation_exception(context, err)
|
|
66
|
+
|
|
67
|
+
context.exit()
|
|
@@ -3,6 +3,7 @@ from typing import Optional
|
|
|
3
3
|
|
|
4
4
|
import click
|
|
5
5
|
|
|
6
|
+
from cycode.cli.commands.ai_remediation.ai_remediation_command import ai_remediation_command
|
|
6
7
|
from cycode.cli.commands.auth.auth_command import auth_command
|
|
7
8
|
from cycode.cli.commands.configure.configure_command import configure_command
|
|
8
9
|
from cycode.cli.commands.ignore.ignore_command import ignore_command
|
|
@@ -30,6 +31,7 @@ from cycode.cyclient.models import UserAgentOptionScheme
|
|
|
30
31
|
'auth': auth_command,
|
|
31
32
|
'version': version_command,
|
|
32
33
|
'status': status_command,
|
|
34
|
+
'ai_remediation': ai_remediation_command,
|
|
33
35
|
},
|
|
34
36
|
context_settings=CLI_CONTEXT_SETTINGS,
|
|
35
37
|
)
|
|
@@ -6,7 +6,7 @@ from cycode import __version__
|
|
|
6
6
|
from cycode.cli.consts import PROGRAM_NAME
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
@click.command(short_help='Show the CLI version and exit.')
|
|
9
|
+
@click.command(short_help='Show the CLI version and exit. Use `cycode status` instead.', deprecated=True)
|
|
10
10
|
@click.pass_context
|
|
11
11
|
def version_command(context: click.Context) -> None:
|
|
12
12
|
output = context.obj['output']
|
|
@@ -159,6 +159,10 @@ SENTRY_MAX_REQUEST_BODY_SIZE = 'never'
|
|
|
159
159
|
SYNC_SCAN_TIMEOUT_IN_SECONDS_ENV_VAR_NAME = 'SYNC_SCAN_TIMEOUT_IN_SECONDS'
|
|
160
160
|
DEFAULT_SYNC_SCAN_TIMEOUT_IN_SECONDS = 180
|
|
161
161
|
|
|
162
|
+
# ai remediation
|
|
163
|
+
AI_REMEDIATION_TIMEOUT_IN_SECONDS_ENV_VAR_NAME = 'AI_REMEDIATION_TIMEOUT_IN_SECONDS'
|
|
164
|
+
DEFAULT_AI_REMEDIATION_TIMEOUT_IN_SECONDS = 60
|
|
165
|
+
|
|
162
166
|
# report with polling
|
|
163
167
|
REPORT_POLLING_WAIT_INTERVAL_IN_SECONDS = 5
|
|
164
168
|
DEFAULT_REPORT_POLLING_TIMEOUT_IN_SECONDS = 600
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from cycode.cli.models import CliError, CliErrors
|
|
6
|
+
from cycode.cli.printers import ConsolePrinter
|
|
7
|
+
from cycode.cli.sentry import capture_exception
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def handle_errors(
|
|
11
|
+
context: click.Context, err: BaseException, cli_errors: CliErrors, *, return_exception: bool = False
|
|
12
|
+
) -> Optional['CliError']:
|
|
13
|
+
ConsolePrinter(context).print_exception(err)
|
|
14
|
+
|
|
15
|
+
if type(err) in cli_errors:
|
|
16
|
+
error = cli_errors[type(err)]
|
|
17
|
+
|
|
18
|
+
if error.soft_fail is True:
|
|
19
|
+
context.obj['soft_fail'] = True
|
|
20
|
+
|
|
21
|
+
if return_exception:
|
|
22
|
+
return error
|
|
23
|
+
|
|
24
|
+
ConsolePrinter(context).print_error(error)
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
if isinstance(err, click.ClickException):
|
|
28
|
+
raise err
|
|
29
|
+
|
|
30
|
+
capture_exception(err)
|
|
31
|
+
|
|
32
|
+
unknown_error = CliError(code='unknown_error', message=str(err))
|
|
33
|
+
if return_exception:
|
|
34
|
+
return unknown_error
|
|
35
|
+
|
|
36
|
+
ConsolePrinter(context).print_error(unknown_error)
|
|
37
|
+
exit(1)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from cycode.cli.exceptions.common import handle_errors
|
|
4
|
+
from cycode.cli.exceptions.custom_exceptions import KNOWN_USER_FRIENDLY_REQUEST_ERRORS, RequestHttpError
|
|
5
|
+
from cycode.cli.models import CliError, CliErrors
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AiRemediationNotFoundError(Exception): ...
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def handle_ai_remediation_exception(context: click.Context, err: Exception) -> None:
|
|
12
|
+
if isinstance(err, RequestHttpError) and err.status_code == 404:
|
|
13
|
+
err = AiRemediationNotFoundError()
|
|
14
|
+
|
|
15
|
+
errors: CliErrors = {
|
|
16
|
+
**KNOWN_USER_FRIENDLY_REQUEST_ERRORS,
|
|
17
|
+
AiRemediationNotFoundError: CliError(
|
|
18
|
+
code='ai_remediation_not_found',
|
|
19
|
+
message='The AI remediation was not found. Please try different detection ID',
|
|
20
|
+
),
|
|
21
|
+
}
|
|
22
|
+
handle_errors(context, err, errors)
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
1
|
import click
|
|
4
2
|
|
|
5
3
|
from cycode.cli.exceptions import custom_exceptions
|
|
4
|
+
from cycode.cli.exceptions.common import handle_errors
|
|
6
5
|
from cycode.cli.exceptions.custom_exceptions import KNOWN_USER_FRIENDLY_REQUEST_ERRORS
|
|
7
6
|
from cycode.cli.models import CliError, CliErrors
|
|
8
|
-
from cycode.cli.printers import ConsolePrinter
|
|
9
|
-
from cycode.cli.sentry import capture_exception
|
|
10
|
-
|
|
11
7
|
|
|
12
|
-
def handle_report_exception(context: click.Context, err: Exception) -> Optional[CliError]:
|
|
13
|
-
ConsolePrinter(context).print_exception()
|
|
14
8
|
|
|
9
|
+
def handle_report_exception(context: click.Context, err: Exception) -> None:
|
|
15
10
|
errors: CliErrors = {
|
|
16
11
|
**KNOWN_USER_FRIENDLY_REQUEST_ERRORS,
|
|
17
12
|
custom_exceptions.ScanAsyncError: CliError(
|
|
@@ -25,16 +20,4 @@ def handle_report_exception(context: click.Context, err: Exception) -> Optional[
|
|
|
25
20
|
'Please try again by executing the `cycode report` command',
|
|
26
21
|
),
|
|
27
22
|
}
|
|
28
|
-
|
|
29
|
-
if type(err) in errors:
|
|
30
|
-
error = errors[type(err)]
|
|
31
|
-
|
|
32
|
-
ConsolePrinter(context).print_error(error)
|
|
33
|
-
return None
|
|
34
|
-
|
|
35
|
-
if isinstance(err, click.ClickException):
|
|
36
|
-
raise err
|
|
37
|
-
|
|
38
|
-
capture_exception(err)
|
|
39
|
-
|
|
40
|
-
raise click.ClickException(str(err))
|
|
23
|
+
handle_errors(context, err, errors)
|
|
@@ -3,20 +3,17 @@ from typing import Optional
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
5
|
from cycode.cli.exceptions import custom_exceptions
|
|
6
|
+
from cycode.cli.exceptions.common import handle_errors
|
|
6
7
|
from cycode.cli.exceptions.custom_exceptions import KNOWN_USER_FRIENDLY_REQUEST_ERRORS
|
|
7
8
|
from cycode.cli.models import CliError, CliErrors
|
|
8
|
-
from cycode.cli.printers import ConsolePrinter
|
|
9
|
-
from cycode.cli.sentry import capture_exception
|
|
10
9
|
from cycode.cli.utils.git_proxy import git_proxy
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
def handle_scan_exception(
|
|
14
|
-
context: click.Context,
|
|
13
|
+
context: click.Context, err: Exception, *, return_exception: bool = False
|
|
15
14
|
) -> Optional[CliError]:
|
|
16
15
|
context.obj['did_fail'] = True
|
|
17
16
|
|
|
18
|
-
ConsolePrinter(context).print_exception(e)
|
|
19
|
-
|
|
20
17
|
errors: CliErrors = {
|
|
21
18
|
**KNOWN_USER_FRIENDLY_REQUEST_ERRORS,
|
|
22
19
|
custom_exceptions.ScanAsyncError: CliError(
|
|
@@ -35,7 +32,7 @@ def handle_scan_exception(
|
|
|
35
32
|
custom_exceptions.TfplanKeyError: CliError(
|
|
36
33
|
soft_fail=True,
|
|
37
34
|
code='key_error',
|
|
38
|
-
message=f'\n{
|
|
35
|
+
message=f'\n{err!s}\n'
|
|
39
36
|
'A crucial field is missing in your terraform plan file. '
|
|
40
37
|
'Please make sure that your file is well formed '
|
|
41
38
|
'and execute the scan again',
|
|
@@ -48,26 +45,4 @@ def handle_scan_exception(
|
|
|
48
45
|
),
|
|
49
46
|
}
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
error = errors[type(e)]
|
|
53
|
-
|
|
54
|
-
if error.soft_fail is True:
|
|
55
|
-
context.obj['soft_fail'] = True
|
|
56
|
-
|
|
57
|
-
if return_exception:
|
|
58
|
-
return error
|
|
59
|
-
|
|
60
|
-
ConsolePrinter(context).print_error(error)
|
|
61
|
-
return None
|
|
62
|
-
|
|
63
|
-
if isinstance(e, click.ClickException):
|
|
64
|
-
raise e
|
|
65
|
-
|
|
66
|
-
capture_exception(e)
|
|
67
|
-
|
|
68
|
-
unknown_error = CliError(code='unknown_error', message=str(e))
|
|
69
|
-
if return_exception:
|
|
70
|
-
return unknown_error
|
|
71
|
-
|
|
72
|
-
ConsolePrinter(context).print_error(unknown_error)
|
|
73
|
-
exit(1)
|
|
48
|
+
return handle_errors(context, err, errors, return_exception=return_exception)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies
|
|
5
|
+
from cycode.cli.models import Document
|
|
6
|
+
|
|
7
|
+
RUBY_PROJECT_FILE_EXTENSIONS = ['Gemfile']
|
|
8
|
+
RUBY_LOCK_FILE_NAME = 'Gemfile.lock'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RestoreRubyDependencies(BaseRestoreDependencies):
|
|
12
|
+
def is_project(self, document: Document) -> bool:
|
|
13
|
+
return any(document.path.endswith(ext) for ext in RUBY_PROJECT_FILE_EXTENSIONS)
|
|
14
|
+
|
|
15
|
+
def get_commands(self, manifest_file_path: str) -> List[List[str]]:
|
|
16
|
+
return [['bundle', '--quiet']]
|
|
17
|
+
|
|
18
|
+
def get_lock_file_name(self) -> str:
|
|
19
|
+
return RUBY_LOCK_FILE_NAME
|
|
20
|
+
|
|
21
|
+
def verify_restore_file_already_exist(self, restore_file_path: str) -> bool:
|
|
22
|
+
return os.path.isfile(restore_file_path)
|
|
23
|
+
|
|
24
|
+
def get_working_directory(self, document: Document) -> Optional[str]:
|
|
25
|
+
return os.path.dirname(document.absolute_path)
|
|
@@ -10,6 +10,7 @@ from cycode.cli.files_collector.sca.maven.restore_gradle_dependencies import Res
|
|
|
10
10
|
from cycode.cli.files_collector.sca.maven.restore_maven_dependencies import RestoreMavenDependencies
|
|
11
11
|
from cycode.cli.files_collector.sca.npm.restore_npm_dependencies import RestoreNpmDependencies
|
|
12
12
|
from cycode.cli.files_collector.sca.nuget.restore_nuget_dependencies import RestoreNugetDependencies
|
|
13
|
+
from cycode.cli.files_collector.sca.ruby.restore_ruby_dependencies import RestoreRubyDependencies
|
|
13
14
|
from cycode.cli.files_collector.sca.sbt.restore_sbt_dependencies import RestoreSbtDependencies
|
|
14
15
|
from cycode.cli.models import Document
|
|
15
16
|
from cycode.cli.utils.git_proxy import git_proxy
|
|
@@ -138,6 +139,7 @@ def restore_handlers(context: click.Context, is_git_diff: bool) -> List[BaseRest
|
|
|
138
139
|
RestoreGoDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
|
|
139
140
|
RestoreNugetDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
|
|
140
141
|
RestoreNpmDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
|
|
142
|
+
RestoreRubyDependencies(context, is_git_diff, BUILD_DEP_TREE_TIMEOUT),
|
|
141
143
|
]
|
|
142
144
|
|
|
143
145
|
|
|
@@ -60,3 +60,15 @@ class ConsolePrinter:
|
|
|
60
60
|
"""Print traceback message in stderr if verbose mode is set."""
|
|
61
61
|
if force_print or self.context.obj.get('verbose', False):
|
|
62
62
|
self._printer_class(self.context).print_exception(e)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def is_json_printer(self) -> bool:
|
|
66
|
+
return self._printer_class == JsonPrinter
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def is_table_printer(self) -> bool:
|
|
70
|
+
return self._printer_class == TablePrinter
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def is_text_printer(self) -> bool:
|
|
74
|
+
return self._printer_class == TextPrinter
|
|
File without changes
|
|
@@ -113,6 +113,13 @@ class ConfigurationManager:
|
|
|
113
113
|
)
|
|
114
114
|
)
|
|
115
115
|
|
|
116
|
+
def get_ai_remediation_timeout_in_seconds(self) -> int:
|
|
117
|
+
return int(
|
|
118
|
+
self._get_value_from_environment_variables(
|
|
119
|
+
consts.AI_REMEDIATION_TIMEOUT_IN_SECONDS_ENV_VAR_NAME, consts.DEFAULT_AI_REMEDIATION_TIMEOUT_IN_SECONDS
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
116
123
|
def get_report_polling_timeout_in_seconds(self) -> int:
|
|
117
124
|
return int(
|
|
118
125
|
self._get_value_from_environment_variables(
|
|
File without changes
|
|
@@ -13,8 +13,10 @@ class Detection(Schema):
|
|
|
13
13
|
detection_details: dict,
|
|
14
14
|
detection_rule_id: str,
|
|
15
15
|
severity: Optional[str] = None,
|
|
16
|
+
id: Optional[str] = None,
|
|
16
17
|
) -> None:
|
|
17
18
|
super().__init__()
|
|
19
|
+
self.id = id
|
|
18
20
|
self.message = message
|
|
19
21
|
self.type = type
|
|
20
22
|
self.severity = severity
|
|
@@ -36,6 +38,7 @@ class DetectionSchema(Schema):
|
|
|
36
38
|
class Meta:
|
|
37
39
|
unknown = EXCLUDE
|
|
38
40
|
|
|
41
|
+
id = fields.String(missing=None)
|
|
39
42
|
message = fields.String()
|
|
40
43
|
type = fields.String()
|
|
41
44
|
severity = fields.String(missing=None)
|
|
@@ -206,6 +206,28 @@ class ScanClient:
|
|
|
206
206
|
response = self.scan_cycode_client.get(url_path='preferences/api/v1/supportedmodules')
|
|
207
207
|
return models.SupportedModulesPreferencesSchema().load(response.json())
|
|
208
208
|
|
|
209
|
+
@staticmethod
|
|
210
|
+
def get_ai_remediation_path(detection_id: str) -> str:
|
|
211
|
+
return f'scm-remediator/api/v1/ContentRemediation/preview/{detection_id}'
|
|
212
|
+
|
|
213
|
+
def get_ai_remediation(self, detection_id: str, *, fix: bool = False) -> str:
|
|
214
|
+
path = self.get_ai_remediation_path(detection_id)
|
|
215
|
+
|
|
216
|
+
data = {
|
|
217
|
+
'resolving_parameters': {
|
|
218
|
+
'get_diff': True,
|
|
219
|
+
'use_code_snippet': True,
|
|
220
|
+
'add_diff_header': True,
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if not fix:
|
|
224
|
+
data['resolving_parameters']['remediation_action'] = 'ReplyWithRemediationDetails'
|
|
225
|
+
|
|
226
|
+
response = self.scan_cycode_client.get(
|
|
227
|
+
url_path=path, json=data, timeout=configuration_manager.get_ai_remediation_timeout_in_seconds()
|
|
228
|
+
)
|
|
229
|
+
return response.text.strip()
|
|
230
|
+
|
|
209
231
|
@staticmethod
|
|
210
232
|
def _get_policy_type_by_scan_type(scan_type: str) -> str:
|
|
211
233
|
scan_type_to_policy_type = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "cycode"
|
|
3
|
-
version = "2.0.1.
|
|
3
|
+
version = "2.0.1.dev4" # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
4
4
|
description = "Boost security in your dev lifecycle via SAST, SCA, Secrets & IaC scanning."
|
|
5
5
|
keywords=["secret-scan", "cycode", "devops", "token", "secret", "security", "cycode", "code"]
|
|
6
6
|
authors = ["Cycode <support@cycode.com>"]
|
|
@@ -41,6 +41,8 @@ requests = ">=2.32.2,<3.0"
|
|
|
41
41
|
urllib3 = "1.26.19" # lock v1 to avoid issues with openssl and old Python versions (<3.9.11) on macOS
|
|
42
42
|
sentry-sdk = ">=2.8.0,<3.0"
|
|
43
43
|
pyjwt = ">=2.8.0,<3.0"
|
|
44
|
+
rich = ">=13.9.4, <14"
|
|
45
|
+
patch-ng = "1.18.1"
|
|
44
46
|
|
|
45
47
|
[tool.poetry.group.test.dependencies]
|
|
46
48
|
mock = ">=4.0.3,<4.1.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.0.1.dev2' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/report/sbom/path/path_command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/pre_commit/pre_commit_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/pre_receive/pre_receive_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/commands/scan/repository/repository_command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2/cycode/cli/files_collector → cycode-2.0.1.dev4/cycode/cli/exceptions}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/iac/tf_content_generator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/base_restore_dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2 → cycode-2.0.1.dev4}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-2.0.1.dev2/cycode/cli/utils → cycode-2.0.1.dev4/cycode/cli/printers/tables}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|