From f352f6fc1c703943b02fea67245dc3ddce4870de Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Tue, 13 Jan 2026 14:44:29 +0100 Subject: [PATCH 1/4] CM-55749-Add scan entrypoint marker file --- cycode/cli/apps/scan/code_scanner.py | 15 +++++++++++++++ cycode/cli/consts.py | 1 + 2 files changed, 16 insertions(+) diff --git a/cycode/cli/apps/scan/code_scanner.py b/cycode/cli/apps/scan/code_scanner.py index 5b4c3e78..4b70ba3d 100644 --- a/cycode/cli/apps/scan/code_scanner.py +++ b/cycode/cli/apps/scan/code_scanner.py @@ -1,3 +1,4 @@ +import os import time from platform import platform from typing import TYPE_CHECKING, Callable, Optional @@ -30,6 +31,7 @@ ) from cycode.cyclient.models import ZippedFileScanResult from cycode.logger import get_logger +from cycode.cli.utils.path_utils import get_path_by_os, get_absolute_path if TYPE_CHECKING: from cycode.cli.files_collector.models.in_memory_zip import InMemoryZip @@ -53,6 +55,19 @@ def scan_disk_files(ctx: typer.Context, paths: tuple[str, ...]) -> None: paths, is_cycodeignore_allowed=is_cycodeignore_allowed_by_scan_config(ctx), ) + + # Add entrypoint.cycode file at each root path to mark the scan root + for root_path in paths: + absolute_root_path = get_absolute_path(root_path) + entrypoint_path = get_path_by_os(os.path.join(absolute_root_path, consts.CYCODE_ENTRYPOINT_FILENAME)) + entrypoint_document = Document( + entrypoint_path, + '', # Empty file content + is_git_diff_format=False, + absolute_path=entrypoint_path, + ) + documents.append(entrypoint_document) + add_sca_dependencies_tree_documents_if_needed(ctx, scan_type, documents) scan_documents(ctx, documents, get_scan_parameters(ctx, paths)) except Exception as e: diff --git a/cycode/cli/consts.py b/cycode/cli/consts.py index e06f9b00..0acd887e 100644 --- a/cycode/cli/consts.py +++ b/cycode/cli/consts.py @@ -18,6 +18,7 @@ IAC_SCAN_SUPPORTED_FILE_PREFIXES = ('dockerfile', 'containerfile') CYCODEIGNORE_FILENAME = '.cycodeignore' +CYCODE_ENTRYPOINT_FILENAME = 'entrypoint.cycode' SECRET_SCAN_FILE_EXTENSIONS_TO_IGNORE = ( '.DS_Store', From d2d31cb3b8a2ac9ad2593d8ac526c839c2a48197 Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Tue, 13 Jan 2026 14:50:18 +0100 Subject: [PATCH 2/4] CM-55749-Ruff --- cycode/cli/apps/scan/code_scanner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cycode/cli/apps/scan/code_scanner.py b/cycode/cli/apps/scan/code_scanner.py index 4b70ba3d..facaad2c 100644 --- a/cycode/cli/apps/scan/code_scanner.py +++ b/cycode/cli/apps/scan/code_scanner.py @@ -22,6 +22,7 @@ from cycode.cli.files_collector.sca.sca_file_collector import add_sca_dependencies_tree_documents_if_needed from cycode.cli.files_collector.zip_documents import zip_documents from cycode.cli.models import CliError, Document, LocalScanResult +from cycode.cli.utils.path_utils import get_absolute_path, get_path_by_os from cycode.cli.utils.progress_bar import ScanProgressBarSection from cycode.cli.utils.scan_batch import run_parallel_batched_scan from cycode.cli.utils.scan_utils import ( @@ -31,7 +32,6 @@ ) from cycode.cyclient.models import ZippedFileScanResult from cycode.logger import get_logger -from cycode.cli.utils.path_utils import get_path_by_os, get_absolute_path if TYPE_CHECKING: from cycode.cli.files_collector.models.in_memory_zip import InMemoryZip @@ -55,7 +55,7 @@ def scan_disk_files(ctx: typer.Context, paths: tuple[str, ...]) -> None: paths, is_cycodeignore_allowed=is_cycodeignore_allowed_by_scan_config(ctx), ) - + # Add entrypoint.cycode file at each root path to mark the scan root for root_path in paths: absolute_root_path = get_absolute_path(root_path) @@ -67,7 +67,7 @@ def scan_disk_files(ctx: typer.Context, paths: tuple[str, ...]) -> None: absolute_path=entrypoint_path, ) documents.append(entrypoint_document) - + add_sca_dependencies_tree_documents_if_needed(ctx, scan_type, documents) scan_documents(ctx, documents, get_scan_parameters(ctx, paths)) except Exception as e: From 17cbafbc7a1db870a171dd69445cdd7d369138dd Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Tue, 13 Jan 2026 17:08:59 +0100 Subject: [PATCH 3/4] CM-55749-Tests coverage --- cycode/cli/apps/scan/code_scanner.py | 5 ++- tests/cli/commands/scan/test_code_scanner.py | 46 ++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/cycode/cli/apps/scan/code_scanner.py b/cycode/cli/apps/scan/code_scanner.py index facaad2c..724009a5 100644 --- a/cycode/cli/apps/scan/code_scanner.py +++ b/cycode/cli/apps/scan/code_scanner.py @@ -56,8 +56,9 @@ def scan_disk_files(ctx: typer.Context, paths: tuple[str, ...]) -> None: is_cycodeignore_allowed=is_cycodeignore_allowed_by_scan_config(ctx), ) - # Add entrypoint.cycode file at each root path to mark the scan root - for root_path in paths: + # Add entrypoint.cycode file at root path to mark the scan root (only for single path) + if len(paths) == 1: + root_path = paths[0] absolute_root_path = get_absolute_path(root_path) entrypoint_path = get_path_by_os(os.path.join(absolute_root_path, consts.CYCODE_ENTRYPOINT_FILENAME)) entrypoint_document = Document( diff --git a/tests/cli/commands/scan/test_code_scanner.py b/tests/cli/commands/scan/test_code_scanner.py index bf4d3574..07e226fd 100644 --- a/tests/cli/commands/scan/test_code_scanner.py +++ b/tests/cli/commands/scan/test_code_scanner.py @@ -1,6 +1,8 @@ import os +from unittest.mock import MagicMock, Mock, patch from cycode.cli import consts +from cycode.cli.apps.scan.code_scanner import scan_disk_files from cycode.cli.files_collector.file_excluder import _is_file_relevant_for_sca_scan from cycode.cli.files_collector.path_documents import _generate_document from cycode.cli.models import Document @@ -72,3 +74,47 @@ def test_generate_document() -> None: assert isinstance(generated_tfplan_document, Document) assert generated_tfplan_document.path.endswith('.tf') assert generated_tfplan_document.is_git_diff_format == is_git_diff + + +@patch('cycode.cli.apps.scan.code_scanner.get_relevant_documents') +@patch('cycode.cli.apps.scan.code_scanner.scan_documents') +@patch('cycode.cli.apps.scan.code_scanner.get_scan_parameters') +def test_entrypoint_cycode_added_to_documents( + mock_get_scan_parameters: Mock, + mock_scan_documents: Mock, + mock_get_relevant_documents: Mock, +) -> None: + """Test that entrypoint.cycode file is added to documents in scan_disk_files.""" + # Arrange + mock_ctx = MagicMock() + mock_ctx.obj = { + 'scan_type': consts.SAST_SCAN_TYPE, + 'progress_bar': MagicMock(), + } + mock_get_scan_parameters.return_value = {} + + mock_documents = [ + Document('/test/path/file1.py', 'content1', is_git_diff_format=False), + Document('/test/path/file2.js', 'content2', is_git_diff_format=False), + ] + mock_get_relevant_documents.return_value = mock_documents.copy() + test_path = '/Users/test/repositories' + + # Act + scan_disk_files(mock_ctx, (test_path,)) + + # Assert + call_args = mock_scan_documents.call_args + documents_passed = call_args[0][1] + + # Verify entrypoint document was added + entrypoint_docs = [ + doc for doc in documents_passed if doc.path.endswith(consts.CYCODE_ENTRYPOINT_FILENAME) + ] + assert len(entrypoint_docs) == 1 + + entrypoint_doc = entrypoint_docs[0] + assert entrypoint_doc.path == os.path.join(test_path, consts.CYCODE_ENTRYPOINT_FILENAME) + assert entrypoint_doc.content == '' + assert entrypoint_doc.is_git_diff_format is False + assert entrypoint_doc.absolute_path == entrypoint_doc.path From 1ff1aa29984269e701c4bbaf3209467d83bc3996 Mon Sep 17 00:00:00 2001 From: Mateusz Sterczewski Date: Tue, 13 Jan 2026 17:13:48 +0100 Subject: [PATCH 4/4] CM-55749-Fix tests --- tests/cli/commands/scan/test_code_scanner.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/cli/commands/scan/test_code_scanner.py b/tests/cli/commands/scan/test_code_scanner.py index 07e226fd..0a43f1c1 100644 --- a/tests/cli/commands/scan/test_code_scanner.py +++ b/tests/cli/commands/scan/test_code_scanner.py @@ -1,4 +1,5 @@ import os +from os.path import normpath from unittest.mock import MagicMock, Mock, patch from cycode.cli import consts @@ -92,7 +93,7 @@ def test_entrypoint_cycode_added_to_documents( 'progress_bar': MagicMock(), } mock_get_scan_parameters.return_value = {} - + mock_documents = [ Document('/test/path/file1.py', 'content1', is_git_diff_format=False), Document('/test/path/file2.js', 'content2', is_git_diff_format=False), @@ -108,13 +109,13 @@ def test_entrypoint_cycode_added_to_documents( documents_passed = call_args[0][1] # Verify entrypoint document was added - entrypoint_docs = [ - doc for doc in documents_passed if doc.path.endswith(consts.CYCODE_ENTRYPOINT_FILENAME) - ] + entrypoint_docs = [doc for doc in documents_passed if doc.path.endswith(consts.CYCODE_ENTRYPOINT_FILENAME)] assert len(entrypoint_docs) == 1 entrypoint_doc = entrypoint_docs[0] - assert entrypoint_doc.path == os.path.join(test_path, consts.CYCODE_ENTRYPOINT_FILENAME) + # Normalize paths for cross-platform compatibility + expected_path = normpath(os.path.join(os.path.abspath(test_path), consts.CYCODE_ENTRYPOINT_FILENAME)) + assert normpath(entrypoint_doc.path) == expected_path assert entrypoint_doc.content == '' assert entrypoint_doc.is_git_diff_format is False - assert entrypoint_doc.absolute_path == entrypoint_doc.path + assert normpath(entrypoint_doc.absolute_path) == normpath(entrypoint_doc.path)