diff --git a/ggshield/verticals/secret/secret_scan_collection.py b/ggshield/verticals/secret/secret_scan_collection.py index 0371f598fb..0736087518 100644 --- a/ggshield/verticals/secret/secret_scan_collection.py +++ b/ggshield/verticals/secret/secret_scan_collection.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field from enum import Enum from pathlib import Path -from typing import Any, Dict, Iterable, List, NamedTuple, Optional, Tuple, Union, cast +from typing import Dict, Iterable, List, NamedTuple, Optional, Tuple, Union, cast from pygitguardian import GGClient from pygitguardian.models import Detail, Match, PolicyBreak, ScanResult, SecretIncident @@ -22,7 +22,10 @@ class IgnoreReason(str, Enum): KNOWN_SECRET = "known_secret" BACKEND_EXCLUDED = "backend_excluded" -def compute_ignore_reason(policy_break: PolicyBreak, secret_config: SecretConfig) -> str | None: + +def compute_ignore_reason( + policy_break: PolicyBreak, secret_config: SecretConfig +) -> str | None: """Computes the possible ignore reason associated with a PolicyBreak""" ignore_reason = None if policy_break.is_excluded: @@ -37,6 +40,7 @@ def compute_ignore_reason(policy_break: PolicyBreak, secret_config: SecretConfig return ignore_reason +@dataclass class Result: """ Return model for a scan which zips the information @@ -50,27 +54,6 @@ class Result: policy_breaks: List[PolicyBreak] ignored_policy_breaks_count_by_reason: Dict[str, int] - def __init__(self, file: Scannable, scan: ScanResult): - self.filename = file.filename - self.filemode = file.filemode - self.path = file.path - self.url = file.url - self.policy_breaks = scan.policy_breaks - lines = get_lines_from_content(file.content, self.filemode) - self.enrich_matches(lines) - self.ignored_policy_breaks_count_by_reason = {} - - def __eq__(self, other: Any) -> bool: - if not isinstance(other, Result): - return False - return ( - self.filename == other.filename - and self.filemode == other.filemode - and self.path == other.path - and self.url == other.url - and self.policy_breaks == other.policy_breaks - ) - @property def is_on_patch(self) -> bool: return self.filemode != Filemode.FILE @@ -97,10 +80,13 @@ def has_policy_breaks(self) -> bool: return len(self.policy_breaks) > 0 @classmethod - def from_scan_result(cls, file: Scannable, scan_result: ScanResult, secret_config: SecretConfig): + def from_scan_result( + cls, file: Scannable, scan_result: ScanResult, secret_config: SecretConfig + ): """Creates a Result from a Scannable and a ScanResult. Removes ignored policy breaks """ + to_keep = [] ignored_policy_breaks_count_by_reason = defaultdict(lambda: 0) for policy_break in scan_result.policy_breaks: @@ -115,10 +101,17 @@ def from_scan_result(cls, file: Scannable, scan_result: ScanResult, secret_confi else: to_keep.append(policy_break) - result = Result(file, to_keep) - result.ignored_policy_breaks_count_by_reason = ( - ignored_policy_breaks_count_by_reason + result = Result( + filename=file.filename, + filemode=file.filemode, + path=file.path, + url=file.url, + policy_breaks=to_keep, + ignored_policy_breaks_count_by_reason=ignored_policy_breaks_count_by_reason, ) + + lines = get_lines_from_content(file.content, file.filemode) + result.enrich_matches(lines) return result diff --git a/tests/unit/cmd/scan/test_prereceive.py b/tests/unit/cmd/scan/test_prereceive.py index 9e423919de..0b63c83a7d 100644 --- a/tests/unit/cmd/scan/test_prereceive.py +++ b/tests/unit/cmd/scan/test_prereceive.py @@ -4,6 +4,7 @@ from click.testing import CliRunner from ggshield.__main__ import cli +from ggshield.core.config.user_config import SecretConfig from ggshield.core.errors import ExitCode from ggshield.core.scan import StringScannable from ggshield.utils.git_shell import EMPTY_SHA, Filemode @@ -195,13 +196,14 @@ def test_stdin_supports_gitlab_web_ui( type="commit", results=Results( results=[ - Result( + Result.from_scan_result( file=StringScannable( content=_SIMPLE_SECRET_PATCH, url="server.conf", filemode=Filemode.MODIFY, ), - scan=_SIMPLE_SECRET_PATCH_SCAN_RESULT, + scan_result=_SIMPLE_SECRET_PATCH_SCAN_RESULT, + secret_config=SecretConfig(), ) ], errors=[], diff --git a/tests/unit/verticals/secret/output/test_json_output.py b/tests/unit/verticals/secret/output/test_json_output.py index 26a1041f75..748987dabe 100644 --- a/tests/unit/verticals/secret/output/test_json_output.py +++ b/tests/unit/verticals/secret/output/test_json_output.py @@ -438,13 +438,14 @@ def test_ignore_known_secrets(verbose, ignore_known_secrets, secrets_types): verbose=verbose, secret_config=secret_config ) - result: Result = Result( + result: Result = Result.from_scan_result( StringScannable( content=_ONE_LINE_AND_MULTILINE_PATCH_CONTENT, url="leak.txt", filemode=Filemode.NEW, ), - scan=deepcopy(TWO_POLICY_BREAKS), # 2 policy breaks + scan_result=deepcopy(TWO_POLICY_BREAKS), + secret_config=SecretConfig(), # 2 policy breaks ) all_policy_breaks = result.policy_breaks @@ -531,13 +532,14 @@ def test_with_incident_details( verbose=True, secret_config=secret_config, client=client_mock ) - result: Result = Result( + result: Result = Result.from_scan_result( StringScannable( content=_ONE_LINE_AND_MULTILINE_PATCH_CONTENT, url="leak.txt", filemode=Filemode.NEW, ), - scan=deepcopy(TWO_POLICY_BREAKS), # 2 policy breaks + scan_result=deepcopy(TWO_POLICY_BREAKS), + secret_config=SecretConfig(), # 2 policy breaks ) all_policy_breaks = result.policy_breaks diff --git a/tests/unit/verticals/secret/output/test_sarif_output.py b/tests/unit/verticals/secret/output/test_sarif_output.py index fb7e404e93..e1dc8ea2b5 100644 --- a/tests/unit/verticals/secret/output/test_sarif_output.py +++ b/tests/unit/verticals/secret/output/test_sarif_output.py @@ -192,7 +192,9 @@ def test_sarif_output_for_flat_scan_with_secrets( policy_break.known_secret = False policy_break.incident_url = None - result = Result(file=scannable, scan=scan_result) + result = Result.from_scan_result( + file=scannable, scan_result=scan_result, secret_config=SecretConfig() + ) results = Results(results=[result]) scan = SecretScanCollection(id="path", type="test", results=results) @@ -248,7 +250,9 @@ def test_sarif_output_for_nested_scan(init_secrets_engine_version): scannable = next(commit.get_files()) contents.append(scannable.content) - result = Result(file=scannable, scan=scan_result) + result = Result.from_scan_result( + file=scannable, scan_result=scan_result, secret_config=SecretConfig() + ) results = Results(results=[result]) scan = SecretScanCollection(id=f"nested{idx}", type="test", results=results) nested_scans.append(scan) diff --git a/tests/unit/verticals/secret/output/test_text_output.py b/tests/unit/verticals/secret/output/test_text_output.py index 07b322ac58..bbf9d1825f 100644 --- a/tests/unit/verticals/secret/output/test_text_output.py +++ b/tests/unit/verticals/secret/output/test_text_output.py @@ -41,68 +41,74 @@ "result_input", [ pytest.param( - Result( + Result.from_scan_result( StringScannable( content=_SIMPLE_SECRET_PATCH, url="leak.txt", filemode=Filemode.NEW, ), - scan=_SIMPLE_SECRET_PATCH_SCAN_RESULT, + scan_result=_SIMPLE_SECRET_PATCH_SCAN_RESULT, + secret_config=SecretConfig(), ), id="_SIMPLE_SECRET_PATCH_SCAN_RESULT", ), pytest.param( - Result( + Result.from_scan_result( StringScannable( content=_MULTI_SECRET_ONE_LINE_PATCH, url="leak.txt", filemode=Filemode.NEW, ), - scan=_MULTI_SECRET_ONE_LINE_PATCH_SCAN_RESULT, + scan_result=_MULTI_SECRET_ONE_LINE_PATCH_SCAN_RESULT, + secret_config=SecretConfig(), ), id="_MULTI_SECRET_ONE_LINE_PATCH_SCAN_RESULT", ), pytest.param( - Result( + Result.from_scan_result( StringScannable( content=_MULTI_SECRET_ONE_LINE_PATCH_OVERLAY, url="leak.txt", filemode=Filemode.NEW, ), - scan=_MULTI_SECRET_ONE_LINE_PATCH_OVERLAY_SCAN_RESULT, + scan_result=_MULTI_SECRET_ONE_LINE_PATCH_OVERLAY_SCAN_RESULT, + secret_config=SecretConfig(), ), id="_MULTI_SECRET_ONE_LINE_PATCH_OVERLAY_SCAN_RESULT", ), pytest.param( - Result( + Result.from_scan_result( StringScannable( content=_MULTI_SECRET_TWO_LINES_PATCH, url="leak.txt", filemode=Filemode.NEW, ), - scan=_MULTI_SECRET_TWO_LINES_PATCH_SCAN_RESULT, + scan_result=_MULTI_SECRET_TWO_LINES_PATCH_SCAN_RESULT, + secret_config=SecretConfig(), ), id="_MULTI_SECRET_TWO_LINES_PATCH_SCAN_RESULT", ), pytest.param( - Result( + Result.from_scan_result( StringScannable( content=_SIMPLE_SECRET_MULTILINE_PATCH, url="leak.txt", filemode=Filemode.NEW, ), - scan=_SIMPLE_SECRET_MULTILINE_PATCH_SCAN_RESULT, + scan_result=_SIMPLE_SECRET_MULTILINE_PATCH_SCAN_RESULT, + secret_config=SecretConfig(), ), id="_SIMPLE_SECRET_MULTILINE_PATCH_SCAN_RESULT", ), pytest.param( - Result( + Result.from_scan_result( StringScannable( content=_ONE_LINE_AND_MULTILINE_PATCH_CONTENT, url="leak.txt", filemode=Filemode.NEW, ), - scan=_ONE_LINE_AND_MULTILINE_PATCH_SCAN_RESULT, + scan_result=_ONE_LINE_AND_MULTILINE_PATCH_SCAN_RESULT, + secret_config=SecretConfig(), ), id="_ONE_LINE_AND_MULTILINE_PATCH_CONTENT", ), diff --git a/tests/unit/verticals/secret/test_scan_repo.py b/tests/unit/verticals/secret/test_scan_repo.py index 9f94be9a11..862d37653d 100644 --- a/tests/unit/verticals/secret/test_scan_repo.py +++ b/tests/unit/verticals/secret/test_scan_repo.py @@ -121,13 +121,15 @@ def test_scan_2_commits_same_content(secret_scanner_mock): secret_scanner_mock.return_value.scan.return_value = Results( results=[ - Result( + Result.from_scan_result( commit_1_files[0], - scan=deepcopy(TWO_POLICY_BREAKS), + scan_result=deepcopy(TWO_POLICY_BREAKS), + secret_config=SecretConfig(), ), - Result( + Result.from_scan_result( commit_2_files[0], - scan=deepcopy(TWO_POLICY_BREAKS), + scan_result=deepcopy(TWO_POLICY_BREAKS), + secret_config=SecretConfig(), ), ], errors=[], @@ -206,17 +208,20 @@ def test_scan_2_commits_file_association(secret_scanner_mock): policy_breaks_file_2_1 = deepcopy(TWO_POLICY_BREAKS) secret_scanner_mock.return_value.scan.return_value = Results( results=[ - Result( + Result.from_scan_result( file1_3, - scan=policy_breaks_file_1_3, + scan_result=policy_breaks_file_1_3, + secret_config=SecretConfig(), ), - Result( + Result.from_scan_result( file2_1, - scan=policy_breaks_file_2_1, + scan_result=policy_breaks_file_2_1, + secret_config=SecretConfig(), ), - Result( + Result.from_scan_result( file1_1, - scan=policy_breaks_file_1_1, + scan_result=policy_breaks_file_1_1, + secret_config=SecretConfig(), ), ], errors=[], @@ -239,13 +244,19 @@ def test_scan_2_commits_file_association(secret_scanner_mock): scan_collection.scans[0].results.results, key=lambda f: f.filename ) == sorted( [ - Result(file1_3, policy_breaks_file_1_3), - Result(file1_1, policy_breaks_file_1_1), + Result.from_scan_result( + file1_3, policy_breaks_file_1_3, secret_config=SecretConfig() + ), + Result.from_scan_result( + file1_1, policy_breaks_file_1_1, secret_config=SecretConfig() + ), ], key=lambda f: f.filename, ) # out of 2 files, only 1 result was returned assert scan_collection.scans[1].results.results == [ - Result(file2_1, policy_breaks_file_2_1), + Result.from_scan_result( + file2_1, policy_breaks_file_2_1, secret_config=SecretConfig() + ), ]