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
6 changes: 2 additions & 4 deletions cycode/cli/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,13 @@
FILE_MAX_SIZE_LIMIT_IN_BYTES = 5000000

PRESIGNED_LINK_UPLOADED_ZIP_MAX_SIZE_LIMIT_IN_BYTES = 5 * 1024 * 1024 * 1024 # 5 GB (S3 presigned POST limit)
PRESIGNED_UPLOAD_SCAN_TYPES = {SAST_SCAN_TYPE}
# Secret scans use the previous (batched / API-upload) flow by default. The presigned S3 async flow is
# opt-in via the SECRET_SCAN_ASYNC_ENV_VAR_NAME env var; see should_use_presigned_upload.
SECRET_SCAN_ASYNC_ENV_VAR_NAME = 'CYCODE_SECRET_SCAN_ASYNC'
PRESIGNED_UPLOAD_SCAN_TYPES = {SAST_SCAN_TYPE, SECRET_SCAN_TYPE}

DEFAULT_ZIP_MAX_SIZE_LIMIT_IN_BYTES = 20 * 1024 * 1024
ZIP_MAX_SIZE_LIMIT_IN_BYTES = {
SCA_SCAN_TYPE: 200 * 1024 * 1024,
SAST_SCAN_TYPE: PRESIGNED_LINK_UPLOADED_ZIP_MAX_SIZE_LIMIT_IN_BYTES,
SECRET_SCAN_TYPE: PRESIGNED_LINK_UPLOADED_ZIP_MAX_SIZE_LIMIT_IN_BYTES,
}

# scan in batches
Expand Down
6 changes: 1 addition & 5 deletions cycode/cli/files_collector/zip_documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@
from cycode.cli.exceptions import custom_exceptions
from cycode.cli.files_collector.models.in_memory_zip import InMemoryZip
from cycode.cli.models import Document
from cycode.cli.utils.scan_utils import should_use_presigned_upload
from cycode.logger import get_logger

logger = get_logger('ZIP')


def _validate_zip_file_size(scan_type: str, zip_file_size: int) -> None:
if should_use_presigned_upload(scan_type):
max_size_limit = consts.PRESIGNED_LINK_UPLOADED_ZIP_MAX_SIZE_LIMIT_IN_BYTES
else:
max_size_limit = consts.ZIP_MAX_SIZE_LIMIT_IN_BYTES.get(scan_type, consts.DEFAULT_ZIP_MAX_SIZE_LIMIT_IN_BYTES)
max_size_limit = consts.ZIP_MAX_SIZE_LIMIT_IN_BYTES.get(scan_type, consts.DEFAULT_ZIP_MAX_SIZE_LIMIT_IN_BYTES)
if zip_file_size > max_size_limit:
raise custom_exceptions.ZipTooLargeError(max_size_limit)

Expand Down
7 changes: 1 addition & 6 deletions cycode/cli/utils/scan_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from cycode.cli import consts
from cycode.cli.cli_types import SeverityOption
from cycode.config import parse_bool

if TYPE_CHECKING:
from cycode.cli.models import LocalScanResult
Expand All @@ -34,11 +33,7 @@ def is_cycodeignore_allowed_by_scan_config(ctx: typer.Context) -> bool:


def should_use_presigned_upload(scan_type: str) -> bool:
if scan_type in consts.PRESIGNED_UPLOAD_SCAN_TYPES:
return True
if scan_type == consts.SECRET_SCAN_TYPE:
return parse_bool(os.getenv(consts.SECRET_SCAN_ASYNC_ENV_VAR_NAME))
return False
return scan_type in consts.PRESIGNED_UPLOAD_SCAN_TYPES


def generate_unique_scan_id() -> UUID:
Expand Down
9 changes: 1 addition & 8 deletions cycode/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,11 @@ def get_val_as_string(key: str) -> str:
return configuration.get(key)


_TRUTHY_STRING_VALUES = {'true', '1', 'yes', 'y', 'on', 'enabled'}


def parse_bool(value: Optional[str]) -> bool:
return value is not None and value.lower() in _TRUTHY_STRING_VALUES


def get_val_as_bool(key: str, default: bool = False) -> bool:
if key not in configuration:
return default

return parse_bool(configuration[key])
return configuration[key].lower() in {'true', '1', 'yes', 'y', 'on', 'enabled'}


def get_val_as_int(key: str) -> Optional[int]:
Expand Down
19 changes: 6 additions & 13 deletions tests/cli/commands/scan/test_code_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,14 @@ def test_entrypoint_cycode_not_added_for_single_file(


@pytest.mark.parametrize(
('scan_type', 'command_scan_type', 'sync_option', 'secret_async_env', 'expect_presigned'),
('scan_type', 'command_scan_type', 'sync_option', 'expect_presigned'),
[
# SAST keeps uploading directly to S3 via a presigned URL (regression guard for the new sync gate).
(consts.SAST_SCAN_TYPE, 'path', False, False, True),
# Secret scans use the previous batched flow by default (presigned async is opt-in).
(consts.SECRET_SCAN_TYPE, 'path', False, False, False),
# With CYCODE_SECRET_SCAN_ASYNC enabled, secret scans upload as a single file directly to S3.
(consts.SECRET_SCAN_TYPE, 'path', False, True, True),
# A --sync secret scan must stay on the batched inline path even when async is enabled.
(consts.SECRET_SCAN_TYPE, 'path', True, True, False),
(consts.SAST_SCAN_TYPE, 'path', False, True),
# Async secret scans now upload as a single file directly to S3 via a presigned URL.
(consts.SECRET_SCAN_TYPE, 'path', False, True),
# A --sync secret scan must stay on the batched inline path and never build one giant zip.
(consts.SECRET_SCAN_TYPE, 'path', True, False),
],
)
@patch('cycode.cli.apps.scan.code_scanner.print_local_scan_results')
Expand All @@ -194,13 +192,8 @@ def test_scan_documents_routes_upload_by_scan_type_and_sync(
scan_type: str,
command_scan_type: str,
sync_option: bool,
secret_async_env: bool,
expect_presigned: bool,
monkeypatch: pytest.MonkeyPatch,
) -> None:
if secret_async_env:
monkeypatch.setenv(consts.SECRET_SCAN_ASYNC_ENV_VAR_NAME, 'true')

mock_presigned_upload.return_value = ([], [])
mock_batched_scan.return_value = ([], [])

Expand Down
5 changes: 0 additions & 5 deletions tests/cli/commands/scan/test_commit_range_scanner.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from unittest.mock import MagicMock, Mock, patch

import pytest

from cycode.cli import consts
from cycode.cli.apps.scan.commit_range_scanner import _scan_commit_range_documents
from cycode.cli.exceptions import custom_exceptions
Expand All @@ -27,10 +25,7 @@ def test_commit_range_scan_falls_back_to_api_when_presigned_upload_raises_wrappe
mock_print: Mock,
mock_handle_exception: Mock,
mock_report_status: Mock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Secret uses the presigned flow only when async is opted in.
monkeypatch.setenv(consts.SECRET_SCAN_ASYNC_ENV_VAR_NAME, 'true')
# SlowUploadConnectionError is a CycodeError, not a requests.RequestException — the presigned
# commit-range fallback must still catch it and retry via the Cycode API.
mock_v4_async.side_effect = custom_exceptions.SlowUploadConnectionError
Expand Down
Empty file removed tests/cli/utils/__init__.py
Empty file.
46 changes: 0 additions & 46 deletions tests/cli/utils/test_scan_utils.py

This file was deleted.