Skip to content

Commit

Permalink
feat(text-output): only display introduced secrets unless verbose is …
Browse files Browse the repository at this point in the history
…enabled
  • Loading branch information
Walz committed Nov 15, 2024
1 parent 4a68be2 commit ee9c949
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 12 deletions.
53 changes: 42 additions & 11 deletions ggshield/verticals/secret/output/secret_text_output_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Dict, List, Optional, Tuple

from pygitguardian.client import VERSIONS
from pygitguardian.models import PolicyBreak
from pygitguardian.models import DiffKind, PolicyBreak

from ggshield.core.filter import group_policy_breaks_by_ignore_sha
from ggshield.core.lines import Line, get_offset, get_padding
Expand Down Expand Up @@ -31,17 +31,29 @@
class SecretTextOutputHandler(SecretOutputHandler):
def _process_scan_impl(self, scan: SecretScanCollection) -> str:
"""Output Secret Scan Collection in text format"""
processed_scan_results = self.process_scan_results(scan)
diff_kinds = [DiffKind.ADDITION]
if self.verbose:
diff_kinds += [DiffKind.DELETION, DiffKind.CONTEXT]

processed_scan_results = self.process_scan_results(scan, diff_kinds=diff_kinds)
scan_buf = StringIO()
if self.verbose:
scan_buf.write(secrets_engine_version())
scan_buf.write(processed_scan_results)
if not processed_scan_results:
if self.ignore_known_secrets and scan.known_secrets_count:
scan_buf.write(no_new_leak_message())
elif scan.deletion_or_context_secrets_count:
scan_buf.write(no_introduced_leak_message())

Check warning on line 47 in ggshield/verticals/secret/output/secret_text_output_handler.py

View check run for this annotation

Codecov / codecov/patch

ggshield/verticals/secret/output/secret_text_output_handler.py#L47

Added line #L47 was not covered by tests
else:
scan_buf.write(no_leak_message())

if not self.verbose and scan.deletion_or_context_secrets_count:
scan_buf.write(
no_new_leak_message()
if (self.ignore_known_secrets and scan.known_secrets_count)
else no_leak_message()
f"\nWarning: {scan.deletion_or_context_secrets_count} "
f"{pluralize('secret', scan.deletion_or_context_secrets_count)} ignored "
f"because {pluralize('it is', scan.deletion_or_context_secrets_count, 'they are')} removed or in the "
f"context of a commit.\nUse `--verbose` for more details.\n"
)

if self.ignore_known_secrets and scan.known_secrets_count > 0:
Expand All @@ -52,22 +64,29 @@ def _process_scan_impl(self, scan: SecretScanCollection) -> str:
)

if self.verbose:
scan_buf.write(self.process_scan_results(scan, True))
scan_buf.write(
self.process_scan_results(
scan, diff_kinds=diff_kinds, show_only_known_secrets=True
)
)
else:
scan_buf.write("Use `--verbose` for more details.\n")

return scan_buf.getvalue()

def process_scan_results(
self, scan: SecretScanCollection, show_only_known_secrets: bool = False
self,
scan: SecretScanCollection,
diff_kinds: List[DiffKind],
show_only_known_secrets: bool = False,
) -> str:
"""Iterate through the scans and sub-scan results to prepare the display."""
results_buf = StringIO()
if scan.results:
current_result_buf = StringIO()
for result in scan.results.results:
current_result_buf.write(
self.process_result(result, show_only_known_secrets)
self.process_result(result, diff_kinds, show_only_known_secrets)
)
current_result_string = current_result_buf.getvalue()

Expand All @@ -80,14 +99,17 @@ def process_scan_results(
if scan.scans:
for sub_scan in scan.scans:
inner_scan_str = self.process_scan_results(
sub_scan, show_only_known_secrets
sub_scan, diff_kinds, show_only_known_secrets
)
results_buf.write(inner_scan_str)

return results_buf.getvalue()

def process_result(
self, result: Result, show_only_known_secrets: bool = False
self,
result: Result,
diff_kinds: List[DiffKind],
show_only_known_secrets: bool = False,
) -> str:
"""
Build readable message on the found incidents.
Expand All @@ -99,7 +121,9 @@ def process_result(
"""
result_buf = StringIO()

sha_dict = group_policy_breaks_by_ignore_sha(result.scan.policy_breaks)
sha_dict = group_policy_breaks_by_ignore_sha(
result.policy_breaks(diff_kinds=diff_kinds)
)

if not self.show_secrets:
result.censor()
Expand Down Expand Up @@ -306,6 +330,13 @@ def no_new_leak_message() -> str:
return format_text("\nNo new secrets have been found\n", STYLE["no_secret"])


def no_introduced_leak_message() -> str:
"""
Build a message if no secrets are introduced.
"""
return format_text("\nNo introduced secrets have been found\n", STYLE["no_secret"])

Check warning on line 337 in ggshield/verticals/secret/output/secret_text_output_handler.py

View check run for this annotation

Codecov / codecov/patch

ggshield/verticals/secret/output/secret_text_output_handler.py#L337

Added line #L337 was not covered by tests


def format_line_with_secret(
line_content: str,
secret_index_start: int,
Expand Down
34 changes: 33 additions & 1 deletion ggshield/verticals/secret/secret_scan_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
from typing import Any, Dict, Iterable, List, NamedTuple, Optional, Tuple, Union, cast

from pygitguardian import GGClient
from pygitguardian.models import Detail, DiffKind, Match, ScanResult, SecretIncident
from pygitguardian.models import (
Detail,
DiffKind,
Match,
PolicyBreak,
ScanResult,
SecretIncident,
)

from ggshield.core.errors import UnexpectedError, handle_api_error
from ggshield.core.filter import group_policy_breaks_by_ignore_sha
Expand Down Expand Up @@ -70,6 +77,18 @@ def censor(self) -> None:
def has_policy_breaks(self) -> bool:
return self.scan.has_policy_breaks

def policy_breaks(
self, *, diff_kinds: Optional[List[DiffKind]] = None
) -> List[PolicyBreak]:
policy_breaks = self.scan.policy_breaks
if diff_kinds is not None and self.scan.is_diff:
policy_breaks = [

Check warning on line 85 in ggshield/verticals/secret/secret_scan_collection.py

View check run for this annotation

Codecov / codecov/patch

ggshield/verticals/secret/secret_scan_collection.py#L85

Added line #L85 was not covered by tests
policy_break
for policy_break in self.scan.policy_breaks
if policy_break.diff_kind in diff_kinds
]
return policy_breaks


class Error(NamedTuple):
files: List[Tuple[str, Filemode]]
Expand Down Expand Up @@ -133,6 +152,9 @@ def __init__(
self.known_secrets_count,
self.new_secrets_count,
) = self._get_known_new_secrets_count()
self.deletion_or_context_secrets_count = (
self._get_deletion_or_context_secrets_count()
)

@property
def has_new_secrets(self) -> bool:
Expand Down Expand Up @@ -163,6 +185,16 @@ def introduces_secret(self) -> bool:
return True

Check warning on line 185 in ggshield/verticals/secret/secret_scan_collection.py

View check run for this annotation

Codecov / codecov/patch

ggshield/verticals/secret/secret_scan_collection.py#L183-L185

Added lines #L183 - L185 were not covered by tests
return False

def _get_deletion_or_context_secrets_count(self) -> int:
deletion_or_context_secrets = 0
for result in self.get_all_results():
if not result.scan.is_diff:
continue
for policy_break in result.scan.policy_breaks:
if policy_break.diff_kind in [DiffKind.DELETION, DiffKind.CONTEXT]:
deletion_or_context_secrets += 1

Check warning on line 195 in ggshield/verticals/secret/secret_scan_collection.py

View check run for this annotation

Codecov / codecov/patch

ggshield/verticals/secret/secret_scan_collection.py#L193-L195

Added lines #L193 - L195 were not covered by tests
return deletion_or_context_secrets

def _get_known_new_secrets_count(self) -> Tuple[int, int]:
policy_breaks = []
for result in self.get_all_results():
Expand Down

0 comments on commit ee9c949

Please sign in to comment.