Skip to content

Commit 4e1f7e0

Browse files
authored
CM-48075 - Update pre-commit hook to work with compact output (#307)
1 parent 3575a21 commit 4e1f7e0

13 files changed

+172
-93
lines changed

.pre-commit-hooks.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
language: python
44
language_version: python3
55
entry: cycode
6-
args: [ '--no-progress-meter', 'scan', '--scan-type', 'secret', 'pre-commit' ]
6+
args: [ '-o', 'text', '--no-progress-meter', 'scan', '-t', 'secret', 'pre-commit' ]
77
- id: cycode-sca
88
name: Cycode SCA pre-commit defender
99
language: python
1010
language_version: python3
1111
entry: cycode
12-
args: [ '--no-progress-meter', 'scan', '--scan-type', 'sca', 'pre-commit' ]
12+
args: [ '-o', 'text', '--no-progress-meter', 'scan', '-t', 'sca', 'pre-commit' ]

README.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -221,26 +221,26 @@ Perform the following steps to install the pre-commit hook:
221221
```yaml
222222
repos:
223223
- repo: https://github.com/cycodehq/cycode-cli
224-
rev: v2.3.0
224+
rev: v3.0.0
225225
hooks:
226226
- id: cycode
227227
stages:
228-
- commit
228+
- pre-commit
229229
```
230230

231231
4. Modify the created file for your specific needs. Use hook ID `cycode` to enable scan for Secrets. Use hook ID `cycode-sca` to enable SCA scan. If you want to enable both, use this configuration:
232232

233233
```yaml
234234
repos:
235235
- repo: https://github.com/cycodehq/cycode-cli
236-
rev: v2.3.0
236+
rev: v3.0.0
237237
hooks:
238238
- id: cycode
239239
stages:
240-
- commit
240+
- pre-commit
241241
- id: cycode-sca
242242
stages:
243-
- commit
243+
- pre-commit
244244
```
245245

246246
5. Install Cycode’s hook:

cycode/cli/apps/scan/code_scanner.py

+17-12
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,17 @@
4848
logger = get_logger('Code Scanner')
4949

5050

51-
def scan_sca_pre_commit(ctx: typer.Context) -> None:
51+
def scan_sca_pre_commit(ctx: typer.Context, repo_path: str) -> None:
5252
scan_type = ctx.obj['scan_type']
5353
scan_parameters = get_scan_parameters(ctx)
5454
git_head_documents, pre_committed_documents = get_pre_commit_modified_documents(
55-
ctx.obj['progress_bar'], ScanProgressBarSection.PREPARE_LOCAL_FILES
55+
progress_bar=ctx.obj['progress_bar'],
56+
progress_bar_section=ScanProgressBarSection.PREPARE_LOCAL_FILES,
57+
repo_path=repo_path,
5658
)
5759
git_head_documents = exclude_irrelevant_documents_to_scan(scan_type, git_head_documents)
5860
pre_committed_documents = exclude_irrelevant_documents_to_scan(scan_type, pre_committed_documents)
59-
sca_code_scanner.perform_pre_hook_range_scan_actions(git_head_documents, pre_committed_documents)
61+
sca_code_scanner.perform_pre_hook_range_scan_actions(repo_path, git_head_documents, pre_committed_documents)
6062
scan_commit_range_documents(
6163
ctx,
6264
git_head_documents,
@@ -269,14 +271,13 @@ def scan_commit_range(
269271
commit_id = commit.hexsha
270272
commit_ids_to_scan.append(commit_id)
271273
parent = commit.parents[0] if commit.parents else git_proxy.get_null_tree()
272-
diff = commit.diff(parent, create_patch=True, R=True)
274+
diff_index = commit.diff(parent, create_patch=True, R=True)
273275
commit_documents_to_scan = []
274-
for blob in diff:
275-
blob_path = get_path_by_os(os.path.join(path, get_diff_file_path(blob)))
276+
for diff in diff_index:
276277
commit_documents_to_scan.append(
277278
Document(
278-
path=blob_path,
279-
content=blob.diff.decode('UTF-8', errors='replace'),
279+
path=get_path_by_os(get_diff_file_path(diff)),
280+
content=diff.diff.decode('UTF-8', errors='replace'),
280281
is_git_diff_format=True,
281282
unique_id=commit_id,
282283
)
@@ -413,10 +414,10 @@ def scan_commit_range_documents(
413414
_report_scan_status(
414415
cycode_client,
415416
scan_type,
416-
local_scan_result.scan_id,
417+
scan_id,
417418
scan_completed,
418-
local_scan_result.relevant_detections_count,
419-
local_scan_result.detections_count,
419+
relevant_detections_count,
420+
detections_count,
420421
len(to_documents_to_scan),
421422
zip_file_size,
422423
scan_command_type,
@@ -658,7 +659,11 @@ def get_scan_parameters(ctx: typer.Context, paths: Optional[tuple[str, ...]] = N
658659
scan_parameters['paths'] = paths
659660

660661
if len(paths) != 1:
661-
# ignore remote url if multiple paths are provided
662+
logger.debug('Multiple paths provided, going to ignore remote url')
663+
return scan_parameters
664+
665+
if not os.path.isdir(paths[0]):
666+
logger.debug('Path is not a directory, going to ignore remote url')
662667
return scan_parameters
663668

664669
remote_url = try_get_git_remote_url(paths[0])

cycode/cli/apps/scan/pre_commit/pre_commit_command.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@ def pre_commit_command(
2727

2828
scan_type = ctx.obj['scan_type']
2929

30+
repo_path = os.getcwd() # change locally for easy testing
31+
3032
progress_bar = ctx.obj['progress_bar']
3133
progress_bar.start()
3234

3335
if scan_type == consts.SCA_SCAN_TYPE:
34-
scan_sca_pre_commit(ctx)
36+
scan_sca_pre_commit(ctx, repo_path)
3537
return
3638

37-
diff_files = git_proxy.get_repo(os.getcwd()).index.diff('HEAD', create_patch=True, R=True)
39+
diff_files = git_proxy.get_repo(repo_path).index.diff(consts.GIT_HEAD_COMMIT_REV, create_patch=True, R=True)
3840

3941
progress_bar.set_section_length(ScanProgressBarSection.PREPARE_LOCAL_FILES, len(diff_files))
4042

cycode/cli/apps/scan/repository/repository_command.py

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import os
21
from pathlib import Path
32
from typing import Annotated, Optional
43

@@ -44,16 +43,16 @@ def repository_command(
4443
progress_bar.set_section_length(ScanProgressBarSection.PREPARE_LOCAL_FILES, len(file_entries))
4544

4645
documents_to_scan = []
47-
for file in file_entries:
46+
for blob in file_entries:
4847
# FIXME(MarshalX): probably file could be tree or submodule too. we expect blob only
4948
progress_bar.update(ScanProgressBarSection.PREPARE_LOCAL_FILES)
5049

51-
absolute_path = get_path_by_os(os.path.join(path, file.path))
52-
file_path = file.path if monitor else absolute_path
50+
absolute_path = get_path_by_os(blob.abspath)
51+
file_path = get_path_by_os(blob.path) if monitor else absolute_path
5352
documents_to_scan.append(
5453
Document(
5554
file_path,
56-
file.data_stream.read().decode('UTF-8', errors='replace'),
55+
blob.data_stream.read().decode('UTF-8', errors='replace'),
5756
absolute_path=absolute_path,
5857
)
5958
)

cycode/cli/cli_types.py

+15-7
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,50 @@
33
from cycode.cli import consts
44

55

6-
class OutputTypeOption(str, Enum):
6+
class StrEnum(str, Enum):
7+
def __str__(self) -> str:
8+
return self.value
9+
10+
11+
class OutputTypeOption(StrEnum):
712
RICH = 'rich'
813
TEXT = 'text'
914
JSON = 'json'
1015
TABLE = 'table'
1116

1217

13-
class ExportTypeOption(str, Enum):
18+
class ExportTypeOption(StrEnum):
1419
JSON = 'json'
1520
HTML = 'html'
1621
SVG = 'svg'
1722

1823

19-
class ScanTypeOption(str, Enum):
24+
class ScanTypeOption(StrEnum):
2025
SECRET = consts.SECRET_SCAN_TYPE
2126
SCA = consts.SCA_SCAN_TYPE
2227
IAC = consts.IAC_SCAN_TYPE
2328
SAST = consts.SAST_SCAN_TYPE
2429

30+
def __str__(self) -> str:
31+
return self.value
32+
2533

26-
class ScaScanTypeOption(str, Enum):
34+
class ScaScanTypeOption(StrEnum):
2735
PACKAGE_VULNERABILITIES = 'package-vulnerabilities'
2836
LICENSE_COMPLIANCE = 'license-compliance'
2937

3038

31-
class SbomFormatOption(str, Enum):
39+
class SbomFormatOption(StrEnum):
3240
SPDX_2_2 = 'spdx-2.2'
3341
SPDX_2_3 = 'spdx-2.3'
3442
CYCLONEDX_1_4 = 'cyclonedx-1.4'
3543

3644

37-
class SbomOutputFormatOption(str, Enum):
45+
class SbomOutputFormatOption(StrEnum):
3846
JSON = 'json'
3947

4048

41-
class SeverityOption(str, Enum):
49+
class SeverityOption(StrEnum):
4250
INFO = 'info'
4351
LOW = 'low'
4452
MEDIUM = 'medium'

cycode/cli/files_collector/repository_documents.py

+21-16
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import TYPE_CHECKING, Optional, Union
44

55
from cycode.cli import consts
6-
from cycode.cli.files_collector.sca import sca_code_scanner
6+
from cycode.cli.files_collector.sca.sca_code_scanner import get_file_content_from_commit_diff
77
from cycode.cli.models import Document
88
from cycode.cli.utils.git_proxy import git_proxy
99
from cycode.cli.utils.path_utils import get_file_content, get_path_by_os
@@ -38,30 +38,36 @@ def parse_commit_range(commit_range: str, path: str) -> tuple[str, str]:
3838
return from_commit_rev, to_commit_rev
3939

4040

41-
def get_diff_file_path(file: 'Diff') -> Optional[str]:
42-
return file.b_path if file.b_path else file.a_path
41+
def get_diff_file_path(file: 'Diff', relative: bool = False) -> Optional[str]:
42+
if relative:
43+
# relative to the repository root
44+
return file.b_path if file.b_path else file.a_path
45+
46+
if file.b_blob:
47+
return file.b_blob.abspath
48+
return file.a_blob.abspath
4349

4450

4551
def get_diff_file_content(file: 'Diff') -> str:
4652
return file.diff.decode('UTF-8', errors='replace')
4753

4854

4955
def get_pre_commit_modified_documents(
50-
progress_bar: 'BaseProgressBar', progress_bar_section: 'ProgressBarSection'
56+
progress_bar: 'BaseProgressBar',
57+
progress_bar_section: 'ProgressBarSection',
58+
repo_path: str,
5159
) -> tuple[list[Document], list[Document]]:
5260
git_head_documents = []
5361
pre_committed_documents = []
5462

55-
repo = git_proxy.get_repo(os.getcwd())
56-
diff_files = repo.index.diff(consts.GIT_HEAD_COMMIT_REV, create_patch=True, R=True)
57-
progress_bar.set_section_length(progress_bar_section, len(diff_files))
58-
for file in diff_files:
63+
repo = git_proxy.get_repo(repo_path)
64+
diff_index = repo.index.diff(consts.GIT_HEAD_COMMIT_REV, create_patch=True, R=True)
65+
progress_bar.set_section_length(progress_bar_section, len(diff_index))
66+
for diff in diff_index:
5967
progress_bar.update(progress_bar_section)
6068

61-
diff_file_path = get_diff_file_path(file)
62-
file_path = get_path_by_os(diff_file_path)
63-
64-
file_content = sca_code_scanner.get_file_content_from_commit(repo, consts.GIT_HEAD_COMMIT_REV, diff_file_path)
69+
file_path = get_path_by_os(get_diff_file_path(diff))
70+
file_content = get_file_content_from_commit_diff(repo, consts.GIT_HEAD_COMMIT_REV, diff)
6571
if file_content is not None:
6672
git_head_documents.append(Document(file_path, file_content))
6773

@@ -92,14 +98,13 @@ def get_commit_range_modified_documents(
9298
for blob in modified_files_diff:
9399
progress_bar.update(progress_bar_section)
94100

95-
diff_file_path = get_diff_file_path(blob)
96-
file_path = get_path_by_os(diff_file_path)
101+
file_path = get_path_by_os(get_diff_file_path(blob))
97102

98-
file_content = sca_code_scanner.get_file_content_from_commit(repo, from_commit_rev, diff_file_path)
103+
file_content = get_file_content_from_commit_diff(repo, from_commit_rev, blob)
99104
if file_content is not None:
100105
from_commit_documents.append(Document(file_path, file_content))
101106

102-
file_content = sca_code_scanner.get_file_content_from_commit(repo, to_commit_rev, diff_file_path)
107+
file_content = get_file_content_from_commit_diff(repo, to_commit_rev, blob)
103108
if file_content is not None:
104109
to_commit_documents.append(Document(file_path, file_content))
105110

cycode/cli/files_collector/sca/sca_code_scanner.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import os
21
from typing import TYPE_CHECKING, Optional
32

43
import typer
@@ -18,7 +17,7 @@
1817
from cycode.logger import get_logger
1918

2019
if TYPE_CHECKING:
21-
from git import Repo
20+
from git import Diff, Repo
2221

2322
BUILD_DEP_TREE_TIMEOUT = 180
2423

@@ -39,9 +38,9 @@ def perform_pre_commit_range_scan_actions(
3938

4039

4140
def perform_pre_hook_range_scan_actions(
42-
git_head_documents: list[Document], pre_committed_documents: list[Document]
41+
repo_path: str, git_head_documents: list[Document], pre_committed_documents: list[Document]
4342
) -> None:
44-
repo = git_proxy.get_repo(os.getcwd())
43+
repo = git_proxy.get_repo(repo_path)
4544
add_ecosystem_related_files_if_exists(git_head_documents, repo, consts.GIT_HEAD_COMMIT_REV)
4645
add_ecosystem_related_files_if_exists(pre_committed_documents)
4746

@@ -69,7 +68,7 @@ def get_doc_ecosystem_related_project_files(
6968
file_to_search = join_paths(get_file_dir(doc.path), ecosystem_project_file)
7069
if not is_project_file_exists_in_documents(documents, file_to_search):
7170
if repo:
72-
file_content = get_file_content_from_commit(repo, commit_rev, file_to_search)
71+
file_content = get_file_content_from_commit_path(repo, commit_rev, file_to_search)
7372
else:
7473
file_content = get_file_content(file_to_search)
7574

@@ -151,13 +150,20 @@ def get_manifest_file_path(document: Document, is_monitor_action: bool, project_
151150
return join_paths(project_path, document.path) if is_monitor_action else document.path
152151

153152

154-
def get_file_content_from_commit(repo: 'Repo', commit: str, file_path: str) -> Optional[str]:
153+
def get_file_content_from_commit_path(repo: 'Repo', commit: str, file_path: str) -> Optional[str]:
155154
try:
156155
return repo.git.show(f'{commit}:{file_path}')
157156
except git_proxy.get_git_command_error():
158157
return None
159158

160159

160+
def get_file_content_from_commit_diff(repo: 'Repo', commit: str, diff: 'Diff') -> Optional[str]:
161+
from cycode.cli.files_collector.repository_documents import get_diff_file_path
162+
163+
file_path = get_diff_file_path(diff, relative=True)
164+
return get_file_content_from_commit_path(repo, commit, file_path)
165+
166+
161167
def perform_pre_scan_documents_actions(
162168
ctx: typer.Context, scan_type: str, documents_to_scan: list[Document], is_git_diff: bool = False
163169
) -> None:

cycode/cli/printers/console_printer.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ class ConsolePrinter:
2828
'text': TextPrinter,
2929
'json': JsonPrinter,
3030
'table': TablePrinter,
31-
# overrides
31+
# overrides:
3232
'table_sca': ScaTablePrinter,
33-
'text_sca': ScaTablePrinter,
3433
}
3534

3635
def __init__(

0 commit comments

Comments
 (0)