|
16 | 16 | from cycode.cli.ci_integrations import get_commit_range |
17 | 17 | from cycode.cli.config import configuration_manager |
18 | 18 | from cycode.cli.exceptions import custom_exceptions |
19 | | -from cycode.cli.helpers import sca_code_scanner |
| 19 | +from cycode.cli.helpers import sca_code_scanner, tf_content_generator |
20 | 20 | from cycode.cli.models import CliError, CliErrors, Document, DocumentDetections, LocalScanResult, Severity |
21 | 21 | from cycode.cli.printers import ConsolePrinter |
22 | 22 | from cycode.cli.user_settings.config_file_manager import ConfigFileManager |
23 | 23 | from cycode.cli.utils import scan_utils |
24 | 24 | from cycode.cli.utils.path_utils import ( |
| 25 | + change_filename_extension, |
25 | 26 | get_file_content, |
26 | 27 | get_file_size, |
27 | 28 | get_path_by_os, |
28 | 29 | get_relevant_files_in_path, |
29 | 30 | is_binary_file, |
30 | 31 | is_sub_path, |
| 32 | + load_json, |
31 | 33 | ) |
32 | 34 | from cycode.cli.utils.progress_bar import ProgressBarSection |
33 | 35 | from cycode.cli.utils.progress_bar import logger as progress_bar_logger |
@@ -328,18 +330,22 @@ def scan_disk_files(context: click.Context, path: str, files_to_scan: List[str]) |
328 | 330 |
|
329 | 331 | is_git_diff = False |
330 | 332 |
|
331 | | - documents: List[Document] = [] |
332 | | - for file in files_to_scan: |
333 | | - progress_bar.update(ProgressBarSection.PREPARE_LOCAL_FILES) |
| 333 | + try: |
| 334 | + documents: List[Document] = [] |
| 335 | + for file in files_to_scan: |
| 336 | + progress_bar.update(ProgressBarSection.PREPARE_LOCAL_FILES) |
334 | 337 |
|
335 | | - content = get_file_content(file) |
336 | | - if not content: |
337 | | - continue |
| 338 | + content = get_file_content(file) |
| 339 | + if not content: |
| 340 | + continue |
| 341 | + |
| 342 | + documents.append(_generate_document(file, scan_type, content, is_git_diff)) |
338 | 343 |
|
339 | | - documents.append(Document(file, content, is_git_diff)) |
| 344 | + perform_pre_scan_documents_actions(context, scan_type, documents, is_git_diff) |
| 345 | + scan_documents(context, documents, is_git_diff=is_git_diff, scan_parameters=scan_parameters) |
340 | 346 |
|
341 | | - perform_pre_scan_documents_actions(context, scan_type, documents, is_git_diff) |
342 | | - scan_documents(context, documents, is_git_diff=is_git_diff, scan_parameters=scan_parameters) |
| 347 | + except Exception as e: |
| 348 | + _handle_exception(context, e) |
343 | 349 |
|
344 | 350 |
|
345 | 351 | def set_issue_detected_by_scan_results(context: click.Context, scan_results: List[LocalScanResult]) -> None: |
@@ -574,7 +580,7 @@ def perform_pre_scan_documents_actions( |
574 | 580 | context: click.Context, scan_type: str, documents_to_scan: List[Document], is_git_diff: bool = False |
575 | 581 | ) -> None: |
576 | 582 | if scan_type == consts.SCA_SCAN_TYPE: |
577 | | - logger.debug('Perform pre scan document actions') |
| 583 | + logger.debug('Perform pre scan document add_dependencies_tree_document action') |
578 | 584 | sca_code_scanner.add_dependencies_tree_document(context, documents_to_scan, is_git_diff) |
579 | 585 |
|
580 | 586 |
|
@@ -1099,6 +1105,37 @@ def _is_file_extension_supported(scan_type: str, filename: str) -> bool: |
1099 | 1105 | return not filename.endswith(consts.SECRET_SCAN_FILE_EXTENSIONS_TO_IGNORE) |
1100 | 1106 |
|
1101 | 1107 |
|
| 1108 | +def _generate_document(file: str, scan_type: str, content: str, is_git_diff: bool) -> Document: |
| 1109 | + if _is_iac(scan_type) and _is_tfplan_file(file, content): |
| 1110 | + return _handle_tfplan_file(file, content, is_git_diff) |
| 1111 | + return Document(file, content, is_git_diff) |
| 1112 | + |
| 1113 | + |
| 1114 | +def _handle_tfplan_file(file: str, content: str, is_git_diff: bool) -> Document: |
| 1115 | + document_name = _generate_tfplan_document_name(file) |
| 1116 | + tf_content = tf_content_generator.generate_tf_content_from_tfplan(file, content) |
| 1117 | + return Document(document_name, tf_content, is_git_diff) |
| 1118 | + |
| 1119 | + |
| 1120 | +def _generate_tfplan_document_name(path: str) -> str: |
| 1121 | + document_name = change_filename_extension(path, 'tf') |
| 1122 | + timestamp = int(time.time()) |
| 1123 | + return f'{timestamp}-{document_name}' |
| 1124 | + |
| 1125 | + |
| 1126 | +def _is_iac(scan_type: str) -> bool: |
| 1127 | + return scan_type == consts.INFRA_CONFIGURATION_SCAN_TYPE |
| 1128 | + |
| 1129 | + |
| 1130 | +def _is_tfplan_file(file: str, content: str) -> bool: |
| 1131 | + if not file.endswith('.json'): |
| 1132 | + return False |
| 1133 | + tf_plan = load_json(content) |
| 1134 | + if not isinstance(tf_plan, dict): |
| 1135 | + return False |
| 1136 | + return 'resource_changes' in tf_plan |
| 1137 | + |
| 1138 | + |
1102 | 1139 | def _does_file_exceed_max_size_limit(filename: str) -> bool: |
1103 | 1140 | return get_file_size(filename) > consts.FILE_MAX_SIZE_LIMIT_IN_BYTES |
1104 | 1141 |
|
@@ -1157,6 +1194,14 @@ def _handle_exception(context: click.Context, e: Exception, *, return_exception: |
1157 | 1194 | 'Please try ignoring irrelevant paths using the `cycode ignore --by-path` command ' |
1158 | 1195 | 'and execute the scan again', |
1159 | 1196 | ), |
| 1197 | + custom_exceptions.TfplanKeyError: CliError( |
| 1198 | + soft_fail=True, |
| 1199 | + code='key_error', |
| 1200 | + message=f'\n{e!s}\n' |
| 1201 | + 'A crucial field is missing in your terraform plan file. ' |
| 1202 | + 'Please make sure that your file is well formed ' |
| 1203 | + 'and execute the scan again', |
| 1204 | + ), |
1160 | 1205 | InvalidGitRepositoryError: CliError( |
1161 | 1206 | soft_fail=False, |
1162 | 1207 | code='invalid_git_error', |
|
0 commit comments