cycode 3.25.1.dev6__py3-none-any.whl → 3.25.2.dev2__py3-none-any.whl
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/__init__.py +1 -1
- cycode/cli/app.py +9 -10
- cycode/cli/apps/activation_manager.py +6 -6
- cycode/cli/apps/ai_guardrails/command_utils.py +1 -2
- cycode/cli/apps/ai_guardrails/hooks_manager.py +6 -7
- cycode/cli/apps/ai_guardrails/ides/__init__.py +8 -9
- cycode/cli/apps/ai_guardrails/ides/_plugin_utils.py +11 -10
- cycode/cli/apps/ai_guardrails/ides/_skill_utils.py +1 -2
- cycode/cli/apps/ai_guardrails/ides/base.py +19 -21
- cycode/cli/apps/ai_guardrails/ides/claude_code.py +54 -16
- cycode/cli/apps/ai_guardrails/ides/codex.py +54 -20
- cycode/cli/apps/ai_guardrails/ides/copilot.py +23 -25
- cycode/cli/apps/ai_guardrails/ides/cursor.py +48 -10
- cycode/cli/apps/ai_guardrails/install_command.py +3 -3
- cycode/cli/apps/ai_guardrails/scan/guardrail_config.py +44 -22
- cycode/cli/apps/ai_guardrails/scan/handlers.py +79 -14
- cycode/cli/apps/ai_guardrails/scan/mcp_server_status.py +103 -0
- cycode/cli/apps/ai_guardrails/scan/payload.py +13 -14
- cycode/cli/apps/ai_guardrails/scan/policy.py +3 -3
- cycode/cli/apps/ai_guardrails/scan/scan_command.py +4 -4
- cycode/cli/apps/ai_guardrails/scan/types.py +1 -0
- cycode/cli/apps/ai_guardrails/session_start_command.py +46 -21
- cycode/cli/apps/ai_guardrails/status_command.py +2 -2
- cycode/cli/apps/ai_guardrails/uninstall_command.py +2 -2
- cycode/cli/apps/api/__init__.py +3 -3
- cycode/cli/apps/api/api_command.py +6 -6
- cycode/cli/apps/api/openapi_spec.py +4 -5
- cycode/cli/apps/auth/auth_common.py +4 -4
- cycode/cli/apps/configure/configure_command.py +2 -4
- cycode/cli/apps/configure/messages.py +2 -4
- cycode/cli/apps/configure/prompts.py +5 -7
- cycode/cli/apps/ignore/ignore_command.py +7 -7
- cycode/cli/apps/mcp/mcp_command.py +11 -11
- cycode/cli/apps/report/sbom/common.py +5 -5
- cycode/cli/apps/report/sbom/sbom_command.py +2 -2
- cycode/cli/apps/report/sbom/sbom_report_file.py +1 -2
- cycode/cli/apps/report_import/sbom/sbom_command.py +3 -3
- cycode/cli/apps/sca_options.py +3 -3
- cycode/cli/apps/scan/aggregation_report.py +4 -4
- cycode/cli/apps/scan/code_scanner.py +8 -7
- cycode/cli/apps/scan/commit_range_scanner.py +10 -10
- cycode/cli/apps/scan/detection_excluder.py +2 -4
- cycode/cli/apps/scan/pre_commit/pre_commit_command.py +3 -3
- cycode/cli/apps/scan/pre_push/pre_push_command.py +3 -3
- cycode/cli/apps/scan/pre_receive/pre_receive_command.py +2 -2
- cycode/cli/apps/scan/remote_url_resolver.py +6 -8
- cycode/cli/apps/scan/repository/repository_command.py +2 -2
- cycode/cli/apps/scan/scan_command.py +2 -2
- cycode/cli/apps/scan/scan_parameters.py +1 -3
- cycode/cli/apps/scan/scan_result.py +5 -5
- cycode/cli/console.py +2 -2
- cycode/cli/exceptions/custom_exceptions.py +1 -1
- cycode/cli/exceptions/handle_errors.py +1 -3
- cycode/cli/exceptions/handle_scan_errors.py +1 -2
- cycode/cli/files_collector/commit_range_documents.py +14 -14
- cycode/cli/files_collector/models/in_memory_zip.py +3 -3
- cycode/cli/files_collector/repository_documents.py +3 -3
- cycode/cli/files_collector/sca/base_restore_dependencies.py +7 -8
- cycode/cli/files_collector/sca/go/restore_go_dependencies.py +1 -2
- cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +2 -3
- cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +3 -4
- cycode/cli/files_collector/sca/npm/restore_bun_dependencies.py +3 -4
- cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +1 -2
- cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +2 -3
- cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +2 -3
- cycode/cli/files_collector/sca/npm/workspace/coverage.py +5 -5
- cycode/cli/files_collector/sca/npm/workspace/files.py +2 -3
- cycode/cli/files_collector/sca/npm/workspace/globs.py +1 -2
- cycode/cli/files_collector/sca/npm/workspace/resolvers.py +14 -16
- cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +1 -2
- cycode/cli/files_collector/sca/python/restore_pip_dependencies.py +2 -3
- cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +1 -2
- cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +2 -3
- cycode/cli/files_collector/sca/python/restore_uv_dependencies.py +2 -3
- cycode/cli/files_collector/sca/sca_file_collector.py +5 -5
- cycode/cli/files_collector/zip_documents.py +1 -2
- cycode/cli/models.py +8 -8
- cycode/cli/printers/console_printer.py +9 -9
- cycode/cli/printers/json_printer.py +2 -2
- cycode/cli/printers/printer_base.py +3 -3
- cycode/cli/printers/rich_printer.py +2 -2
- cycode/cli/printers/tables/table.py +4 -4
- cycode/cli/printers/tables/table_models.py +2 -2
- cycode/cli/printers/tables/table_printer_base.py +2 -2
- cycode/cli/printers/text_printer.py +4 -4
- cycode/cli/printers/utils/code_snippet_syntax.py +1 -1
- cycode/cli/printers/utils/detection_data.py +3 -3
- cycode/cli/printers/utils/sca_ossf.py +4 -4
- cycode/cli/printers/utils/sca_policy_details.py +2 -1
- cycode/cli/user_settings/config_file_manager.py +10 -10
- cycode/cli/user_settings/configuration_manager.py +6 -6
- cycode/cli/user_settings/credentials_manager.py +5 -6
- cycode/cli/utils/get_api_client.py +5 -5
- cycode/cli/utils/git_proxy.py +8 -8
- cycode/cli/utils/host_info.py +21 -38
- cycode/cli/utils/ignore_utils.py +13 -15
- cycode/cli/utils/jwt_utils.py +2 -4
- cycode/cli/utils/path_utils.py +6 -6
- cycode/cli/utils/progress_bar.py +4 -4
- cycode/cli/utils/scan_batch.py +2 -1
- cycode/cli/utils/scan_utils.py +2 -2
- cycode/cli/utils/shell_executor.py +3 -4
- cycode/cli/utils/task_timer.py +4 -4
- cycode/cli/utils/trust_store.py +0 -17
- cycode/cli/utils/url_utils.py +1 -2
- cycode/cli/utils/version_checker.py +4 -5
- cycode/cli/utils/yaml_utils.py +1 -1
- cycode/config.py +1 -2
- cycode/cyclient/ai_security_manager_client.py +27 -21
- cycode/cyclient/auth_client.py +2 -4
- cycode/cyclient/base_token_auth_client.py +2 -2
- cycode/cyclient/client_creator.py +4 -6
- cycode/cyclient/cycode_client_base.py +10 -9
- cycode/cyclient/cycode_dev_based_client.py +1 -3
- cycode/cyclient/headers.py +1 -2
- cycode/cyclient/import_sbom_client.py +3 -4
- cycode/cyclient/models.py +88 -30
- cycode/cyclient/report_client.py +1 -2
- cycode/cyclient/scan_client.py +8 -7
- cycode/logger.py +4 -4
- {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/METADATA +12 -19
- {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/RECORD +125 -124
- {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/WHEEL +1 -1
- {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/entry_points.txt +0 -0
- {cycode-3.25.1.dev6.dist-info → cycode-3.25.2.dev2.dist-info}/licenses/LICENCE +0 -0
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
1
|
from cycode.cli import consts
|
|
4
2
|
from cycode.cli.utils.git_proxy import git_proxy
|
|
5
3
|
from cycode.cli.utils.shell_executor import shell
|
|
@@ -9,7 +7,7 @@ from cycode.logger import get_logger
|
|
|
9
7
|
logger = get_logger('Remote URL Resolver')
|
|
10
8
|
|
|
11
9
|
|
|
12
|
-
def _get_plastic_repository_name(path: str) ->
|
|
10
|
+
def _get_plastic_repository_name(path: str) -> str | None:
|
|
13
11
|
"""Get the name of the Plastic repository from the current working directory.
|
|
14
12
|
|
|
15
13
|
The command to execute is:
|
|
@@ -45,7 +43,7 @@ def _get_plastic_repository_name(path: str) -> Optional[str]:
|
|
|
45
43
|
return None
|
|
46
44
|
|
|
47
45
|
|
|
48
|
-
def _get_plastic_repository_list(working_dir:
|
|
46
|
+
def _get_plastic_repository_list(working_dir: str | None = None) -> dict[str, str]:
|
|
49
47
|
"""Get the list of Plastic repositories and their GUIDs.
|
|
50
48
|
|
|
51
49
|
The command to execute is:
|
|
@@ -84,7 +82,7 @@ def _get_plastic_repository_list(working_dir: Optional[str] = None) -> dict[str,
|
|
|
84
82
|
return repo_name_to_guid
|
|
85
83
|
|
|
86
84
|
|
|
87
|
-
def _try_to_get_plastic_remote_url(path: str) ->
|
|
85
|
+
def _try_to_get_plastic_remote_url(path: str) -> str | None:
|
|
88
86
|
repository_name = _get_plastic_repository_name(path)
|
|
89
87
|
if not repository_name:
|
|
90
88
|
return None
|
|
@@ -98,7 +96,7 @@ def _try_to_get_plastic_remote_url(path: str) -> Optional[str]:
|
|
|
98
96
|
return f'{consts.PLASTIC_VCS_REMOTE_URI_PREFIX}{repository_guid}'
|
|
99
97
|
|
|
100
98
|
|
|
101
|
-
def _try_get_git_remote_url(path: str) ->
|
|
99
|
+
def _try_get_git_remote_url(path: str) -> str | None:
|
|
102
100
|
try:
|
|
103
101
|
repo = git_proxy.get_repo(path, search_parent_directories=True)
|
|
104
102
|
remote_url = repo.remotes[0].config_reader.get('url')
|
|
@@ -113,7 +111,7 @@ def _try_get_git_remote_url(path: str) -> Optional[str]:
|
|
|
113
111
|
return None
|
|
114
112
|
|
|
115
113
|
|
|
116
|
-
def _try_get_any_remote_url(path: str) ->
|
|
114
|
+
def _try_get_any_remote_url(path: str) -> str | None:
|
|
117
115
|
remote_url = _try_get_git_remote_url(path)
|
|
118
116
|
if not remote_url:
|
|
119
117
|
remote_url = _try_to_get_plastic_remote_url(path)
|
|
@@ -121,7 +119,7 @@ def _try_get_any_remote_url(path: str) -> Optional[str]:
|
|
|
121
119
|
return remote_url
|
|
122
120
|
|
|
123
121
|
|
|
124
|
-
def get_remote_url_scan_parameter(paths: tuple[str, ...]) ->
|
|
122
|
+
def get_remote_url_scan_parameter(paths: tuple[str, ...]) -> str | None:
|
|
125
123
|
remote_urls = set()
|
|
126
124
|
for path in paths:
|
|
127
125
|
# FIXME(MarshalX): perf issue. This looping will produce:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Annotated
|
|
2
|
+
from typing import Annotated
|
|
3
3
|
|
|
4
4
|
import click
|
|
5
5
|
import typer
|
|
@@ -25,7 +25,7 @@ def repository_command(
|
|
|
25
25
|
Path, typer.Argument(exists=True, resolve_path=True, help='Path to Git repository to scan.', show_default=False)
|
|
26
26
|
],
|
|
27
27
|
branch: Annotated[
|
|
28
|
-
|
|
28
|
+
str | None, typer.Option('--branch', '-b', help='Branch to scan.', show_default='default branch')
|
|
29
29
|
] = None,
|
|
30
30
|
) -> None:
|
|
31
31
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Annotated
|
|
3
|
+
from typing import Annotated
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
6
|
import typer
|
|
@@ -121,7 +121,7 @@ def scan_command(
|
|
|
121
121
|
),
|
|
122
122
|
] = None,
|
|
123
123
|
export_file: Annotated[
|
|
124
|
-
|
|
124
|
+
Path | None,
|
|
125
125
|
typer.Option(
|
|
126
126
|
'--export-file',
|
|
127
127
|
help='Export file. Path to the file where the export will be saved.',
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
1
|
import typer
|
|
4
2
|
|
|
5
3
|
from cycode import _BOOT_WALL
|
|
@@ -23,7 +21,7 @@ def _get_default_scan_parameters(ctx: typer.Context) -> dict:
|
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
|
|
26
|
-
def get_scan_parameters(ctx: typer.Context, paths:
|
|
24
|
+
def get_scan_parameters(ctx: typer.Context, paths: tuple[str, ...] | None = None) -> dict:
|
|
27
25
|
scan_parameters = _get_default_scan_parameters(ctx)
|
|
28
26
|
|
|
29
27
|
if not paths:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
import typer
|
|
5
5
|
|
|
@@ -26,8 +26,8 @@ logger = get_logger('Scan Results')
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def _get_document_by_file_name(
|
|
29
|
-
documents: list[Document], file_name: str, unique_id:
|
|
30
|
-
) ->
|
|
29
|
+
documents: list[Document], file_name: str, unique_id: str | None = None
|
|
30
|
+
) -> Document | None:
|
|
31
31
|
normalized_file_name = normalize_file_path(file_name)
|
|
32
32
|
for document in documents:
|
|
33
33
|
if normalize_file_path(document.path) == normalized_file_name and document.unique_id == unique_id:
|
|
@@ -132,7 +132,7 @@ def _map_detections_per_file_and_commit_id(scan_type: str, raw_detections: list[
|
|
|
132
132
|
|
|
133
133
|
file_name = _get_file_name_from_detection(scan_type, raw_detection)
|
|
134
134
|
detection: Detection = DetectionSchema().load(raw_detection)
|
|
135
|
-
commit_id:
|
|
135
|
+
commit_id: str | None = detection.detection_details.get('commit_id') # could be None
|
|
136
136
|
group_by_key = (file_name, commit_id)
|
|
137
137
|
|
|
138
138
|
if group_by_key in detections_per_files:
|
|
@@ -187,7 +187,7 @@ def get_sync_scan_result(scan_type: str, scan_results: 'ScanResultsSyncFlow') ->
|
|
|
187
187
|
|
|
188
188
|
|
|
189
189
|
def print_local_scan_results(
|
|
190
|
-
ctx: typer.Context, local_scan_results: list[LocalScanResult], errors:
|
|
190
|
+
ctx: typer.Context, local_scan_results: list[LocalScanResult], errors: dict[str, 'CliError'] | None = None
|
|
191
191
|
) -> None:
|
|
192
192
|
printer = ctx.obj.get('console_printer')
|
|
193
193
|
printer.update_ctx(ctx)
|
cycode/cli/console.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from rich.console import Console, RenderResult
|
|
5
5
|
from rich.markdown import Heading, Markdown
|
|
@@ -14,7 +14,7 @@ console_err = Console(stderr=True)
|
|
|
14
14
|
console = console_out # alias
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def is_dark_console() ->
|
|
17
|
+
def is_dark_console() -> bool | None:
|
|
18
18
|
"""Detect if the console is dark or light.
|
|
19
19
|
|
|
20
20
|
This function checks the environment variables and terminal type to determine if the console is dark or light.
|
|
@@ -129,7 +129,7 @@ _SSL_ERROR_TRUST_HINT = (
|
|
|
129
129
|
'If you use an on-premises installation or a proxy that intercepts SSL traffic, '
|
|
130
130
|
f'set {consts.ENABLE_TRUSTSTORE_ENV_VAR_NAME}=1 to trust the CA certificates installed in your '
|
|
131
131
|
f'machine certificate store, or {_SSL_ERROR_CA_BUNDLE_HINT}'
|
|
132
|
-
if
|
|
132
|
+
if not trust_store.is_enabled()
|
|
133
133
|
else f'If you use an on-premises installation or a proxy that intercepts SSL traffic, {_SSL_ERROR_CA_BUNDLE_HINT}'
|
|
134
134
|
)
|
|
135
135
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
1
|
import click
|
|
4
2
|
import typer
|
|
5
3
|
|
|
@@ -8,7 +6,7 @@ from cycode.cli.models import CliError, CliErrors
|
|
|
8
6
|
|
|
9
7
|
def handle_errors(
|
|
10
8
|
ctx: typer.Context, err: BaseException, cli_errors: CliErrors, *, return_exception: bool = False
|
|
11
|
-
) ->
|
|
9
|
+
) -> CliError | None:
|
|
12
10
|
printer = ctx.obj.get('console_printer')
|
|
13
11
|
printer.print_exception(err)
|
|
14
12
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import zipfile
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
import typer
|
|
5
4
|
|
|
@@ -10,7 +9,7 @@ from cycode.cli.models import CliError, CliErrors
|
|
|
10
9
|
from cycode.cli.utils.git_proxy import git_proxy
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
def handle_scan_exception(ctx: typer.Context, err: Exception, *, return_exception: bool = False) ->
|
|
12
|
+
def handle_scan_exception(ctx: typer.Context, err: Exception, *, return_exception: bool = False) -> CliError | None:
|
|
14
13
|
ctx.obj['did_fail'] = True
|
|
15
14
|
|
|
16
15
|
errors: CliErrors = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
import typer
|
|
6
6
|
|
|
@@ -47,7 +47,7 @@ def get_safe_head_reference_for_diff(repo: 'Repo') -> str:
|
|
|
47
47
|
return consts.GIT_EMPTY_TREE_OBJECT
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def get_staged_diff_index(repo: 'Repo', paths:
|
|
50
|
+
def get_staged_diff_index(repo: 'Repo', paths: list[str] | None = None) -> tuple[str, 'DiffIndex']:
|
|
51
51
|
"""Diff the index against HEAD, or against the empty tree in repositories with no commits.
|
|
52
52
|
|
|
53
53
|
GitPython only inverts the `R` flag for HEAD, so `R` must be off for the empty tree to keep
|
|
@@ -65,7 +65,7 @@ def get_staged_diff_index(repo: 'Repo', paths: Optional[list[str]] = None) -> tu
|
|
|
65
65
|
return head_reference, repo.index.diff(head_reference, create_patch=True, R=reverse, paths=paths or None)
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def _does_reach_to_max_commits_to_scan_limit(commit_ids: list[str], max_commits_count:
|
|
68
|
+
def _does_reach_to_max_commits_to_scan_limit(commit_ids: list[str], max_commits_count: int | None) -> bool:
|
|
69
69
|
if max_commits_count is None:
|
|
70
70
|
return False
|
|
71
71
|
|
|
@@ -73,7 +73,7 @@ def _does_reach_to_max_commits_to_scan_limit(commit_ids: list[str], max_commits_
|
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
def collect_commit_range_diff_documents(
|
|
76
|
-
ctx: typer.Context, path: str, commit_range: str, max_commits_count:
|
|
76
|
+
ctx: typer.Context, path: str, commit_range: str, max_commits_count: int | None = None
|
|
77
77
|
) -> list[Document]:
|
|
78
78
|
"""Collects documents from a specified commit range in a Git repository.
|
|
79
79
|
|
|
@@ -138,7 +138,7 @@ def collect_commit_range_diff_documents(
|
|
|
138
138
|
return commit_documents_to_scan
|
|
139
139
|
|
|
140
140
|
|
|
141
|
-
def calculate_pre_receive_commit_range(repo_path: str, branch_update_details: str) ->
|
|
141
|
+
def calculate_pre_receive_commit_range(repo_path: str, branch_update_details: str) -> str | None:
|
|
142
142
|
end_commit = _get_end_commit_from_branch_update_details(branch_update_details)
|
|
143
143
|
|
|
144
144
|
# branch is deleted, no need to perform scan
|
|
@@ -168,7 +168,7 @@ def _get_end_commit_from_branch_update_details(update_details: str) -> str:
|
|
|
168
168
|
return end_commit
|
|
169
169
|
|
|
170
170
|
|
|
171
|
-
def _get_oldest_unupdated_commit_for_branch(repo: 'Repo', commit: str) ->
|
|
171
|
+
def _get_oldest_unupdated_commit_for_branch(repo: 'Repo', commit: str) -> str | None:
|
|
172
172
|
# get a list of commits by chronological order that are not in the remote repository yet
|
|
173
173
|
# more info about rev-list command: https://git-scm.com/docs/git-rev-list
|
|
174
174
|
|
|
@@ -181,12 +181,12 @@ def _get_oldest_unupdated_commit_for_branch(repo: 'Repo', commit: str) -> Option
|
|
|
181
181
|
return commits[0]
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
def _get_file_content_from_commit_diff(repo: 'Repo', commit: str, diff: 'Diff') ->
|
|
184
|
+
def _get_file_content_from_commit_diff(repo: 'Repo', commit: str, diff: 'Diff') -> str | None:
|
|
185
185
|
file_path = get_diff_file_path(diff, relative=True)
|
|
186
186
|
return get_file_content_from_commit_path(repo, commit, file_path)
|
|
187
187
|
|
|
188
188
|
|
|
189
|
-
def _get_blob_content(blob:
|
|
189
|
+
def _get_blob_content(blob: 'Blob | None') -> str | None:
|
|
190
190
|
"""Read a diff-side blob's content in-process, with no subprocess spawn.
|
|
191
191
|
|
|
192
192
|
`diff.a_blob`/`diff.b_blob` already hold whatever git loaded to compute the patch, so this is
|
|
@@ -266,7 +266,7 @@ def parse_pre_receive_input() -> str:
|
|
|
266
266
|
return pre_receive_input.splitlines()[0]
|
|
267
267
|
|
|
268
268
|
|
|
269
|
-
def parse_pre_push_input() ->
|
|
269
|
+
def parse_pre_push_input() -> str | None:
|
|
270
270
|
"""Parse input to pre-push hook details.
|
|
271
271
|
|
|
272
272
|
Example input:
|
|
@@ -289,7 +289,7 @@ def is_invoked_by_pre_commit_framework() -> bool:
|
|
|
289
289
|
return os.getenv(consts.PRE_COMMIT_FRAMEWORK_ENV_VAR_NAME) == '1'
|
|
290
290
|
|
|
291
291
|
|
|
292
|
-
def get_pre_commit_framework_push_range() ->
|
|
292
|
+
def get_pre_commit_framework_push_range() -> str | None:
|
|
293
293
|
"""Get the commit range to scan when invoked as a pre-push hook by the pre-commit framework.
|
|
294
294
|
|
|
295
295
|
The pre-commit framework reads git's pre-push stdin itself and never forwards it to hooks.
|
|
@@ -379,7 +379,7 @@ def _get_default_branches_for_merge_base(repo: 'Repo') -> list[str]:
|
|
|
379
379
|
return default_branches
|
|
380
380
|
|
|
381
381
|
|
|
382
|
-
def calculate_pre_push_commit_range(push_update_details: str) ->
|
|
382
|
+
def calculate_pre_push_commit_range(push_update_details: str) -> str | None:
|
|
383
383
|
"""Calculate the commit range for pre-push hook scanning.
|
|
384
384
|
|
|
385
385
|
Args:
|
|
@@ -430,7 +430,7 @@ def calculate_pre_push_commit_range(push_update_details: str) -> Optional[str]:
|
|
|
430
430
|
return f'{remote_object_name}..{local_object_name}'
|
|
431
431
|
|
|
432
432
|
|
|
433
|
-
def get_diff_file_path(diff: 'Diff', relative: bool = False, repo:
|
|
433
|
+
def get_diff_file_path(diff: 'Diff', relative: bool = False, repo: 'Repo | None' = None) -> str | None:
|
|
434
434
|
"""Get the file path from a git Diff object.
|
|
435
435
|
|
|
436
436
|
Args:
|
|
@@ -470,7 +470,7 @@ def get_pre_commit_modified_documents(
|
|
|
470
470
|
repo_path: str,
|
|
471
471
|
base_ref: str = consts.GIT_HEAD_COMMIT_REV,
|
|
472
472
|
include_unstaged: bool = False,
|
|
473
|
-
paths:
|
|
473
|
+
paths: list[str] | None = None,
|
|
474
474
|
collect_file_contents: bool = True,
|
|
475
475
|
) -> tuple[list[Document], list[Document], list[Document]]:
|
|
476
476
|
"""Diffs `base_ref` against the staged index (default) or the full working tree.
|
|
@@ -562,7 +562,7 @@ def get_pre_commit_modified_documents(
|
|
|
562
562
|
return from_ref_documents, working_copy_documents, diff_documents
|
|
563
563
|
|
|
564
564
|
|
|
565
|
-
def parse_commit_range(commit_range: str, path: str) -> tuple[
|
|
565
|
+
def parse_commit_range(commit_range: str, path: str) -> tuple[str | None, str | None, str | None]:
|
|
566
566
|
"""Parses a git commit range string and returns the full SHAs for the 'from' and 'to' commits.
|
|
567
567
|
Also, it returns the separator in the commit range.
|
|
568
568
|
|
|
@@ -3,7 +3,7 @@ import tempfile
|
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
from os import SEEK_END
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import IO
|
|
6
|
+
from typing import IO
|
|
7
7
|
from zipfile import ZIP_DEFLATED, ZipFile
|
|
8
8
|
|
|
9
9
|
from cycode.cli import consts
|
|
@@ -17,7 +17,7 @@ logger = get_logger('ZIP')
|
|
|
17
17
|
_SPOOL_DIRECTORY_NAME = 'tmp'
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def _get_spool_directory(configuration_manager: ConfigurationManager) ->
|
|
20
|
+
def _get_spool_directory(configuration_manager: ConfigurationManager) -> str | None:
|
|
21
21
|
"""Directory to spill big ZIPs into. None falls back to the system temp directory."""
|
|
22
22
|
try:
|
|
23
23
|
directory = Path(configuration_manager.global_config_file_manager.get_config_directory_path())
|
|
@@ -47,7 +47,7 @@ class InMemoryZip:
|
|
|
47
47
|
self._files_count = 0
|
|
48
48
|
self._extension_statistics = defaultdict(int)
|
|
49
49
|
|
|
50
|
-
def append(self, filename: str, unique_id:
|
|
50
|
+
def append(self, filename: str, unique_id: str | None, content: str) -> None:
|
|
51
51
|
self._files_count += 1
|
|
52
52
|
self._extension_statistics[Path(filename).suffix] += 1
|
|
53
53
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from collections.abc import Iterator
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from cycode.cli.utils.git_proxy import git_proxy
|
|
5
5
|
|
|
@@ -15,11 +15,11 @@ def _should_process_git_object(obj: 'Blob', _: int) -> bool:
|
|
|
15
15
|
|
|
16
16
|
def get_git_repository_tree_file_entries(
|
|
17
17
|
path: str, branch: str
|
|
18
|
-
) ->
|
|
18
|
+
) -> Iterator['IndexObjUnion'] | Iterator['TraversedTreeTup']:
|
|
19
19
|
return git_proxy.get_repo(path).tree(branch).traverse(predicate=_should_process_git_object)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def get_file_content_from_commit_path(repo: 'Repo', commit: str, file_path: str) ->
|
|
22
|
+
def get_file_content_from_commit_path(repo: 'Repo', commit: str, file_path: str) -> str | None:
|
|
23
23
|
try:
|
|
24
24
|
return repo.git.show(f'{commit}:{file_path}')
|
|
25
25
|
except git_proxy.get_git_command_error():
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
4
3
|
|
|
5
4
|
import typer
|
|
6
5
|
|
|
@@ -19,9 +18,9 @@ def build_dep_tree_path(path: str, generated_file_name: str) -> str:
|
|
|
19
18
|
def execute_commands(
|
|
20
19
|
commands: list[list[str]],
|
|
21
20
|
timeout: int,
|
|
22
|
-
output_file_path:
|
|
23
|
-
working_directory:
|
|
24
|
-
) ->
|
|
21
|
+
output_file_path: str | None = None,
|
|
22
|
+
working_directory: str | None = None,
|
|
23
|
+
) -> str | None:
|
|
25
24
|
logger.debug(
|
|
26
25
|
'Executing restore commands, %s',
|
|
27
26
|
{
|
|
@@ -67,7 +66,7 @@ class BaseRestoreDependencies(ABC):
|
|
|
67
66
|
self.command_timeout = command_timeout
|
|
68
67
|
self.create_output_file_manually = create_output_file_manually
|
|
69
68
|
|
|
70
|
-
def restore(self, document: Document) ->
|
|
69
|
+
def restore(self, document: Document) -> Document | None:
|
|
71
70
|
return self.try_restore_dependencies(document)
|
|
72
71
|
|
|
73
72
|
def get_manifest_file_path(self, document: Document) -> str:
|
|
@@ -75,7 +74,7 @@ class BaseRestoreDependencies(ABC):
|
|
|
75
74
|
join_paths(get_path_from_context(self.ctx), document.path) if self.ctx.obj.get('monitor') else document.path
|
|
76
75
|
)
|
|
77
76
|
|
|
78
|
-
def try_restore_dependencies(self, document: Document) ->
|
|
77
|
+
def try_restore_dependencies(self, document: Document) -> Document | None:
|
|
79
78
|
manifest_file_path = self.get_manifest_file_path(document)
|
|
80
79
|
restore_file_paths = [
|
|
81
80
|
build_dep_tree_path(document.absolute_path, restore_file_path_item)
|
|
@@ -122,7 +121,7 @@ class BaseRestoreDependencies(ABC):
|
|
|
122
121
|
|
|
123
122
|
return Document(relative_restore_file_path, restore_file_content, self.is_git_diff)
|
|
124
123
|
|
|
125
|
-
def get_manifest_dir(self, document: Document) ->
|
|
124
|
+
def get_manifest_dir(self, document: Document) -> str | None:
|
|
126
125
|
"""Return the directory containing the manifest file, resolving monitor-mode paths.
|
|
127
126
|
|
|
128
127
|
Uses the same path resolution as get_manifest_file_path() to ensure consistency.
|
|
@@ -143,7 +142,7 @@ class BaseRestoreDependencies(ABC):
|
|
|
143
142
|
|
|
144
143
|
return None
|
|
145
144
|
|
|
146
|
-
def get_working_directory(self, document: Document) ->
|
|
145
|
+
def get_working_directory(self, document: Document) -> str | None:
|
|
147
146
|
return str(Path(document.absolute_path).parent)
|
|
148
147
|
|
|
149
148
|
def get_restored_lock_file_name(self, restore_file_path: str) -> str:
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
import typer
|
|
5
4
|
|
|
@@ -19,7 +18,7 @@ class RestoreGoDependencies(BaseRestoreDependencies):
|
|
|
19
18
|
def __init__(self, ctx: typer.Context, is_git_diff: bool, command_timeout: int) -> None:
|
|
20
19
|
super().__init__(ctx, is_git_diff, command_timeout, create_output_file_manually=True)
|
|
21
20
|
|
|
22
|
-
def try_restore_dependencies(self, document: Document) ->
|
|
21
|
+
def try_restore_dependencies(self, document: Document) -> Document | None:
|
|
23
22
|
manifest_exists = (Path(self.get_working_directory(document)) / BUILD_GO_FILE_NAME).is_file()
|
|
24
23
|
lock_exists = (Path(self.get_working_directory(document)) / BUILD_GO_LOCK_FILE_NAME).is_file()
|
|
25
24
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import platform
|
|
3
3
|
import re
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
import typer
|
|
7
6
|
|
|
@@ -22,7 +21,7 @@ GRADLEW_BAT_FILE_NAME = 'gradlew.bat'
|
|
|
22
21
|
|
|
23
22
|
class RestoreGradleDependencies(BaseRestoreDependencies):
|
|
24
23
|
def __init__(
|
|
25
|
-
self, ctx: typer.Context, is_git_diff: bool, command_timeout: int, projects:
|
|
24
|
+
self, ctx: typer.Context, is_git_diff: bool, command_timeout: int, projects: set[str] | None = None
|
|
26
25
|
) -> None:
|
|
27
26
|
super().__init__(ctx, is_git_diff, command_timeout, create_output_file_manually=True)
|
|
28
27
|
self.gradle_executable = self._resolve_gradle_executable()
|
|
@@ -58,7 +57,7 @@ class RestoreGradleDependencies(BaseRestoreDependencies):
|
|
|
58
57
|
def get_lock_file_names(self) -> list[str]:
|
|
59
58
|
return [self.get_lock_file_name()]
|
|
60
59
|
|
|
61
|
-
def get_working_directory(self, document: Document) ->
|
|
60
|
+
def get_working_directory(self, document: Document) -> str | None:
|
|
62
61
|
return get_path_from_context(self.ctx) if self.is_gradle_sub_projects() else None
|
|
63
62
|
|
|
64
63
|
def get_all_projects(self) -> set[str]:
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from os import path
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
import typer
|
|
7
6
|
|
|
@@ -21,7 +20,7 @@ MAVEN_CYCLONE_DEP_TREE_FILE_NAME = 'bom.json'
|
|
|
21
20
|
MAVEN_DEP_TREE_FILE_NAME = 'bcde.mvndeps'
|
|
22
21
|
|
|
23
22
|
|
|
24
|
-
def _has_dependency_graph(bom_content:
|
|
23
|
+
def _has_dependency_graph(bom_content: str | None) -> bool:
|
|
25
24
|
try:
|
|
26
25
|
if not bom_content:
|
|
27
26
|
return False
|
|
@@ -52,7 +51,7 @@ class RestoreMavenDependencies(BaseRestoreDependencies):
|
|
|
52
51
|
def get_lock_file_names(self) -> list[str]:
|
|
53
52
|
return [self.get_lock_file_name()]
|
|
54
53
|
|
|
55
|
-
def try_restore_dependencies(self, document: Document) ->
|
|
54
|
+
def try_restore_dependencies(self, document: Document) -> Document | None:
|
|
56
55
|
manifest_file_path = self.get_manifest_file_path(document)
|
|
57
56
|
if document.content is None:
|
|
58
57
|
return self.restore_from_secondary_command(document, manifest_file_path)
|
|
@@ -68,7 +67,7 @@ class RestoreMavenDependencies(BaseRestoreDependencies):
|
|
|
68
67
|
|
|
69
68
|
return restore_dependencies_document
|
|
70
69
|
|
|
71
|
-
def restore_from_secondary_command(self, document: Document, manifest_file_path: str) ->
|
|
70
|
+
def restore_from_secondary_command(self, document: Document, manifest_file_path: str) -> Document | None:
|
|
72
71
|
restore_content = execute_commands(
|
|
73
72
|
commands=self.create_secondary_restore_commands(manifest_file_path),
|
|
74
73
|
timeout=self.command_timeout,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import re
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
import typer
|
|
7
6
|
|
|
@@ -26,7 +25,7 @@ MINIMUM_BUN_VERSION = (1, 2)
|
|
|
26
25
|
BUN_VERSION_COMMAND = ['bun', '--version']
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
def _indicates_bun(package_json_content:
|
|
28
|
+
def _indicates_bun(package_json_content: str | None) -> bool:
|
|
30
29
|
"""Return True if package.json content signals that this project uses Bun."""
|
|
31
30
|
if not package_json_content:
|
|
32
31
|
return False
|
|
@@ -43,7 +42,7 @@ def _indicates_bun(package_json_content: Optional[str]) -> bool:
|
|
|
43
42
|
return isinstance(engines, dict) and 'bun' in engines
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
def _parse_bun_version(raw_version:
|
|
45
|
+
def _parse_bun_version(raw_version: str | None) -> tuple[int, int] | None:
|
|
47
46
|
"""Parse the (major, minor) version from `bun --version` output (e.g. '1.2.3')."""
|
|
48
47
|
if not raw_version:
|
|
49
48
|
return None
|
|
@@ -91,7 +90,7 @@ class RestoreBunDependencies(BaseRestoreDependencies):
|
|
|
91
90
|
return False
|
|
92
91
|
return True
|
|
93
92
|
|
|
94
|
-
def try_restore_dependencies(self, document: Document) ->
|
|
93
|
+
def try_restore_dependencies(self, document: Document) -> Document | None:
|
|
95
94
|
manifest_dir = self.get_manifest_dir(document)
|
|
96
95
|
lockfile_path = Path(manifest_dir) / BUN_LOCK_FILE_NAME if manifest_dir else None
|
|
97
96
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
import typer
|
|
5
4
|
|
|
@@ -21,7 +20,7 @@ class RestoreDenoDependencies(BaseRestoreDependencies):
|
|
|
21
20
|
def is_project(self, document: Document) -> bool:
|
|
22
21
|
return Path(document.path).name in DENO_MANIFEST_FILE_NAMES
|
|
23
22
|
|
|
24
|
-
def try_restore_dependencies(self, document: Document) ->
|
|
23
|
+
def try_restore_dependencies(self, document: Document) -> Document | None:
|
|
25
24
|
manifest_dir = self.get_manifest_dir(document)
|
|
26
25
|
if not manifest_dir:
|
|
27
26
|
return None
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
4
3
|
|
|
5
4
|
import typer
|
|
6
5
|
|
|
@@ -19,7 +18,7 @@ logger = get_logger('Pnpm Restore Dependencies')
|
|
|
19
18
|
PNPM_MANIFEST_FILE_NAME = MANIFEST_FILE_NAME
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def _indicates_pnpm(package_json_content:
|
|
21
|
+
def _indicates_pnpm(package_json_content: str | None) -> bool:
|
|
23
22
|
"""Return True if package.json content signals that this project uses pnpm."""
|
|
24
23
|
if not package_json_content:
|
|
25
24
|
return False
|
|
@@ -53,7 +52,7 @@ class RestorePnpmDependencies(BaseRestoreDependencies):
|
|
|
53
52
|
|
|
54
53
|
return _indicates_pnpm(document.content)
|
|
55
54
|
|
|
56
|
-
def try_restore_dependencies(self, document: Document) ->
|
|
55
|
+
def try_restore_dependencies(self, document: Document) -> Document | None:
|
|
57
56
|
manifest_dir = self.get_manifest_dir(document)
|
|
58
57
|
lockfile_path = Path(manifest_dir) / PNPM_LOCK_FILE_NAME if manifest_dir else None
|
|
59
58
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
4
3
|
|
|
5
4
|
import typer
|
|
6
5
|
|
|
@@ -19,7 +18,7 @@ logger = get_logger('Yarn Restore Dependencies')
|
|
|
19
18
|
YARN_MANIFEST_FILE_NAME = MANIFEST_FILE_NAME
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def _indicates_yarn(package_json_content:
|
|
21
|
+
def _indicates_yarn(package_json_content: str | None) -> bool:
|
|
23
22
|
"""Return True if package.json content signals that this project uses Yarn."""
|
|
24
23
|
if not package_json_content:
|
|
25
24
|
return False
|
|
@@ -53,7 +52,7 @@ class RestoreYarnDependencies(BaseRestoreDependencies):
|
|
|
53
52
|
|
|
54
53
|
return _indicates_yarn(document.content)
|
|
55
54
|
|
|
56
|
-
def try_restore_dependencies(self, document: Document) ->
|
|
55
|
+
def try_restore_dependencies(self, document: Document) -> Document | None:
|
|
57
56
|
manifest_dir = self.get_manifest_dir(document)
|
|
58
57
|
lockfile_path = Path(manifest_dir) / YARN_LOCK_FILE_NAME if manifest_dir else None
|
|
59
58
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import TYPE_CHECKING, NamedTuple
|
|
5
|
+
from typing import TYPE_CHECKING, NamedTuple
|
|
6
6
|
|
|
7
7
|
from cycode.cli.files_collector.sca.npm.workspace.files import logger, resolved_path
|
|
8
8
|
from cycode.cli.files_collector.sca.npm.workspace.globs import declares_workspace_member
|
|
@@ -49,7 +49,7 @@ def _containing_scan_roots(manifest_dir: Path, scan_roots: tuple) -> list:
|
|
|
49
49
|
]
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def _resolve_walk_boundary(manifest_dir: Path, scan_roots: tuple) ->
|
|
52
|
+
def _resolve_walk_boundary(manifest_dir: Path, scan_roots: tuple) -> Path | None:
|
|
53
53
|
for root_dir in manifest_dir.parents:
|
|
54
54
|
if (root_dir / _GIT_DIR_NAME).exists():
|
|
55
55
|
return root_dir
|
|
@@ -76,7 +76,7 @@ def _workspace_root_candidates(manifest_dir: Path, scan_roots: tuple) -> 'Iterat
|
|
|
76
76
|
return
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def _find_covering_workspace(manifest_dir: Path, scan_roots: tuple) ->
|
|
79
|
+
def _find_covering_workspace(manifest_dir: Path, scan_roots: tuple) -> WorkspaceCoverage | None:
|
|
80
80
|
for root_dir in _workspace_root_candidates(manifest_dir, scan_roots):
|
|
81
81
|
member_path = manifest_dir.relative_to(root_dir).as_posix()
|
|
82
82
|
|
|
@@ -98,7 +98,7 @@ def _find_covering_workspace(manifest_dir: Path, scan_roots: tuple) -> Optional[
|
|
|
98
98
|
return None
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
def find_covering_workspace(manifest_dir:
|
|
101
|
+
def find_covering_workspace(manifest_dir: str | None, scan_roots: tuple = ()) -> WorkspaceCoverage | None:
|
|
102
102
|
if not manifest_dir:
|
|
103
103
|
return None
|
|
104
104
|
|
|
@@ -115,7 +115,7 @@ def _is_inside_scanned_paths(scan_roots: tuple, root_dir: Path) -> bool:
|
|
|
115
115
|
return any(is_sub_path(directory, resolved_root_dir) for directory in directories)
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
def is_covered_workspace_member(manifest_dir:
|
|
118
|
+
def is_covered_workspace_member(manifest_dir: str | None, document_path: str, scan_roots: tuple = ()) -> bool:
|
|
119
119
|
coverage = find_covering_workspace(manifest_dir, scan_roots)
|
|
120
120
|
if coverage is None:
|
|
121
121
|
return False
|