forked from cycodehq/cycode-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprinter_base.py
More file actions
115 lines (88 loc) · 3.65 KB
/
Copy pathprinter_base.py
File metadata and controls
115 lines (88 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import sys
from abc import ABC, abstractmethod
from collections import defaultdict
from typing import TYPE_CHECKING, Optional
import typer
from cycode.cli.cli_types import SeverityOption
from cycode.cli.models import CliError, CliResult
from cycode.cyclient.headers import get_correlation_id
if TYPE_CHECKING:
from rich.console import Console
from cycode.cli.models import LocalScanResult
from rich.traceback import Traceback as RichTraceback
class PrinterBase(ABC):
NO_DETECTIONS_MESSAGE = (
'[b green]Good job! No issues were found!!! :clapping_hands::clapping_hands::clapping_hands:[/]'
)
FAILED_SCAN_MESSAGE = (
'[b red]Unfortunately, Cycode was unable to complete the full scan. '
'Please note that not all results may be available:[/]'
)
def __init__(
self,
ctx: typer.Context,
console: 'Console',
console_err: 'Console',
) -> None:
self.ctx = ctx
self.console = console
self.console_err = console_err
@property
def scan_type(self) -> str:
return self.ctx.obj.get('scan_type')
@property
def command_scan_type(self) -> str:
return self.ctx.info_name
@property
def show_secret(self) -> bool:
return self.ctx.obj.get('show_secret', False)
@abstractmethod
def print_scan_results(
self, local_scan_results: list['LocalScanResult'], errors: Optional[dict[str, 'CliError']] = None
) -> None:
pass
@abstractmethod
def print_result(self, result: CliResult) -> None:
pass
@abstractmethod
def print_error(self, error: CliError) -> None:
pass
def print_exception(self, e: Optional[BaseException] = None) -> None:
"""We are printing it in stderr so, we don't care about supporting JSON and TABLE outputs.
Note:
Called only when the verbose flag is set.
"""
rich_traceback = (
RichTraceback.from_exception(type(e), e, e.__traceback__)
if e
else RichTraceback.from_exception(*sys.exc_info())
)
rich_traceback.show_locals = False
self.console_err.print(rich_traceback)
self.console_err.print(f'[red]Correlation ID:[/] {get_correlation_id()}')
def print_scan_results_summary(self, local_scan_results: list['LocalScanResult']) -> None:
"""Print a summary of scan results based on severity levels.
Args:
local_scan_results (List['LocalScanResult']): A list of local scan results containing detections.
The summary includes the count of detections for each severity level
and is displayed in the console in a formatted string.
"""
detections_count = 0
severity_counts = defaultdict(int)
for local_scan_result in local_scan_results:
for document_detections in local_scan_result.document_detections:
for detection in document_detections.detections:
if detection.severity:
detections_count += 1
severity_counts[SeverityOption(detection.severity)] += 1
self.console.line()
self.console.print(f'[bold]Cycode found {detections_count} violations[/]', end=': ')
# Example of string: CRITICAL - 6 | HIGH - 0 | MEDIUM - 14 | LOW - 0 | INFO - 0
for index, severity in enumerate(reversed(SeverityOption), start=1):
end = ' | '
if index == len(SeverityOption):
end = '\n'
self.console.print(
SeverityOption.get_member_emoji(severity), severity, '-', severity_counts[severity], end=end
)
self.console.line()