cycode 0.2.5.dev6__py3-none-any.whl → 0.2.5.dev7__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 CHANGED
@@ -1 +1 @@
1
- __version__ = '0.2.5.dev6' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
1
+ __version__ = '0.2.5.dev7' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
cycode/cli/main.py CHANGED
@@ -65,10 +65,10 @@ NO_ISSUES_STATUS_CODE = 0
65
65
  @click.option('--output', default=None,
66
66
  help="""
67
67
  \b
68
- Specify the results output (text/json),
68
+ Specify the results output (text/json/table),
69
69
  the default is text
70
70
  """,
71
- type=click.Choice(['text', 'json']))
71
+ type=click.Choice(['text', 'json', 'table']))
72
72
  @click.option('--severity-threshold',
73
73
  default=None,
74
74
  help='Show only violations at the specified level or higher (supported for SCA scan type only).',
@@ -142,8 +142,8 @@ def finalize(context: click.Context, *args, **kwargs):
142
142
  @click.option(
143
143
  '--output',
144
144
  default='text',
145
- help='Specify the output (text/json), the default is text',
146
- type=click.Choice(['text', 'json'])
145
+ help='Specify the output (text/json/table), the default is text',
146
+ type=click.Choice(['text', 'json', 'table'])
147
147
  )
148
148
  @click.option(
149
149
  '--user-agent',
@@ -7,7 +7,9 @@ from cycode.cli.models import DocumentDetections, CliResult, CliError
7
7
 
8
8
 
9
9
  class BasePrinter(ABC):
10
- context: click.Context
10
+ RED_COLOR_NAME = 'red'
11
+ WHITE_COLOR_NAME = 'white'
12
+ GREEN_COLOR_NAME = 'green'
11
13
 
12
14
  def __init__(self, context: click.Context):
13
15
  self.context = context
@@ -0,0 +1,43 @@
1
+ import abc
2
+ from typing import List
3
+
4
+ import click
5
+
6
+ from cycode.cli.printers.text_printer import TextPrinter
7
+ from cycode.cli.models import DocumentDetections, CliError, CliResult
8
+ from cycode.cli.printers.base_printer import BasePrinter
9
+
10
+
11
+ class BaseTablePrinter(BasePrinter, abc.ABC):
12
+ def __init__(self, context: click.Context):
13
+ super().__init__(context)
14
+ self.context = context
15
+ self.scan_id: str = context.obj.get('scan_id')
16
+ self.scan_type: str = context.obj.get('scan_type')
17
+ self.show_secret: bool = context.obj.get('show_secret', False)
18
+
19
+ def print_result(self, result: CliResult) -> None:
20
+ TextPrinter(self.context).print_result(result)
21
+
22
+ def print_error(self, error: CliError) -> None:
23
+ TextPrinter(self.context).print_error(error)
24
+
25
+ def print_scan_results(self, results: List[DocumentDetections]):
26
+ click.secho(f'Scan Results: (scan_id: {self.scan_id})')
27
+
28
+ if not results:
29
+ click.secho('Good job! No issues were found!!! 👏👏👏', fg=self.GREEN_COLOR_NAME)
30
+ return
31
+
32
+ self._print_results(results)
33
+
34
+ report_url = self.context.obj.get('report_url')
35
+ if report_url:
36
+ click.secho(f'Report URL: {report_url}')
37
+
38
+ def _is_git_repository(self) -> bool:
39
+ return self.context.obj.get('remote_url') is not None
40
+
41
+ @abc.abstractmethod
42
+ def _print_results(self, results: List[DocumentDetections]) -> None:
43
+ raise NotImplementedError
@@ -1,10 +1,10 @@
1
1
  import click
2
2
  from typing import List, TYPE_CHECKING
3
3
 
4
- from cycode.cli.consts import SCA_SCAN_TYPE
5
4
  from cycode.cli.exceptions.custom_exceptions import CycodeError
6
5
  from cycode.cli.models import DocumentDetections, CliResult, CliError
7
6
  from cycode.cli.printers.table_printer import TablePrinter
7
+ from cycode.cli.printers.sca_table_printer import SCATablePrinter
8
8
  from cycode.cli.printers.json_printer import JsonPrinter
9
9
  from cycode.cli.printers.text_printer import TextPrinter
10
10
 
@@ -16,11 +16,15 @@ class ConsolePrinter:
16
16
  _AVAILABLE_PRINTERS = {
17
17
  'text': TextPrinter,
18
18
  'json': JsonPrinter,
19
- 'text_sca': TablePrinter
19
+ 'table': TablePrinter,
20
+ # overrides
21
+ 'table_sca': SCATablePrinter,
22
+ 'text_sca': SCATablePrinter,
20
23
  }
21
24
 
22
25
  def __init__(self, context: click.Context):
23
26
  self.context = context
27
+ self.scan_type = self.context.obj.get('scan_type')
24
28
  self.output_type = self.context.obj.get('output')
25
29
 
26
30
  self._printer_class = self._AVAILABLE_PRINTERS.get(self.output_type)
@@ -32,11 +36,11 @@ class ConsolePrinter:
32
36
  printer.print_scan_results(detections_results_list)
33
37
 
34
38
  def _get_scan_printer(self) -> 'BasePrinter':
35
- scan_type = self.context.obj.get('scan_type')
36
-
37
39
  printer_class = self._printer_class
38
- if scan_type == SCA_SCAN_TYPE and self.output_type == 'text':
39
- printer_class = TablePrinter
40
+
41
+ composite_printer = self._AVAILABLE_PRINTERS.get(f'{self.output_type}_{self.scan_type}')
42
+ if composite_printer:
43
+ printer_class = composite_printer
40
44
 
41
45
  return printer_class(self.context)
42
46
 
@@ -0,0 +1,142 @@
1
+ from collections import defaultdict
2
+ from typing import List, Dict
3
+
4
+ import click
5
+ from texttable import Texttable
6
+
7
+ from cycode.cli.consts import LICENSE_COMPLIANCE_POLICY_ID, PACKAGE_VULNERABILITY_POLICY_ID
8
+ from cycode.cli.models import DocumentDetections, Detection
9
+ from cycode.cli.printers.base_table_printer import BaseTablePrinter
10
+
11
+ SEVERITY_COLUMN = 'Severity'
12
+ LICENSE_COLUMN = 'License'
13
+ UPGRADE_COLUMN = 'Upgrade'
14
+ REPOSITORY_COLUMN = 'Repository'
15
+ CVE_COLUMN = 'CVE'
16
+
17
+ PREVIEW_DETECTIONS_COMMON_HEADERS = [
18
+ 'File Path',
19
+ 'Ecosystem',
20
+ 'Dependency Name',
21
+ 'Direct Dependency',
22
+ 'Development Dependency'
23
+ ]
24
+
25
+
26
+ class SCATablePrinter(BaseTablePrinter):
27
+ def _print_results(self, results: List[DocumentDetections]) -> None:
28
+ detections_per_detection_type_id = self._extract_detections_per_detection_type_id(results)
29
+ self._print_detection_per_detection_type_id(detections_per_detection_type_id)
30
+
31
+ @staticmethod
32
+ def _extract_detections_per_detection_type_id(results: List[DocumentDetections]) -> Dict[str, List[Detection]]:
33
+ detections_per_detection_type_id = defaultdict(list)
34
+
35
+ for document_detection in results:
36
+ for detection in document_detection.detections:
37
+ detections_per_detection_type_id[detection.detection_type_id].append(detection)
38
+
39
+ return detections_per_detection_type_id
40
+
41
+ def _print_detection_per_detection_type_id(
42
+ self, detections_per_detection_type_id: Dict[str, List[Detection]]
43
+ ) -> None:
44
+ for detection_type_id in detections_per_detection_type_id:
45
+ detections = detections_per_detection_type_id[detection_type_id]
46
+ headers = self._get_table_headers()
47
+
48
+ title = None
49
+ rows = []
50
+
51
+ if detection_type_id == PACKAGE_VULNERABILITY_POLICY_ID:
52
+ title = "Dependencies Vulnerabilities"
53
+
54
+ headers = [SEVERITY_COLUMN] + headers
55
+ headers.extend(PREVIEW_DETECTIONS_COMMON_HEADERS)
56
+ headers.append(CVE_COLUMN)
57
+ headers.append(UPGRADE_COLUMN)
58
+
59
+ for detection in detections:
60
+ rows.append(self._get_upgrade_package_vulnerability(detection))
61
+ elif detection_type_id == LICENSE_COMPLIANCE_POLICY_ID:
62
+ title = "License Compliance"
63
+
64
+ headers.extend(PREVIEW_DETECTIONS_COMMON_HEADERS)
65
+ headers.append(LICENSE_COLUMN)
66
+
67
+ for detection in detections:
68
+ rows.append(self._get_license(detection))
69
+
70
+ if rows:
71
+ self._print_table_detections(detections, headers, rows, title)
72
+
73
+ def _get_table_headers(self) -> list:
74
+ if self._is_git_repository():
75
+ return [REPOSITORY_COLUMN]
76
+
77
+ return []
78
+
79
+ def _print_table_detections(
80
+ self, detections: List[Detection], headers: List[str], rows, title: str
81
+ ) -> None:
82
+ self._print_summary_issues(detections, title)
83
+ text_table = Texttable()
84
+ text_table.header(headers)
85
+
86
+ self.set_table_width(headers, text_table)
87
+
88
+ for row in rows:
89
+ text_table.add_row(row)
90
+
91
+ click.echo(text_table.draw())
92
+
93
+ @staticmethod
94
+ def set_table_width(headers: List[str], text_table: Texttable) -> None:
95
+ header_width_size_cols = []
96
+ for header in headers:
97
+ header_len = len(header)
98
+ if header == CVE_COLUMN:
99
+ header_width_size_cols.append(header_len * 5)
100
+ elif header == UPGRADE_COLUMN:
101
+ header_width_size_cols.append(header_len * 2)
102
+ else:
103
+ header_width_size_cols.append(header_len)
104
+ text_table.set_cols_width(header_width_size_cols)
105
+
106
+ @staticmethod
107
+ def _print_summary_issues(detections: List, title: str) -> None:
108
+ click.echo(f'⛔ Found {len(detections)} issues of type: {click.style(title, bold=True)}')
109
+
110
+ def _get_common_detection_fields(self, detection: Detection) -> List[str]:
111
+ row = [
112
+ detection.detection_details.get('file_name'),
113
+ detection.detection_details.get('ecosystem'),
114
+ detection.detection_details.get('package_name'),
115
+ detection.detection_details.get('is_direct_dependency_str'),
116
+ detection.detection_details.get('is_dev_dependency_str')
117
+ ]
118
+
119
+ if self._is_git_repository():
120
+ row = [detection.detection_details.get('repository_name')] + row
121
+
122
+ return row
123
+
124
+ def _get_upgrade_package_vulnerability(self, detection: Detection) -> List[str]:
125
+ alert = detection.detection_details.get('alert')
126
+ row = [
127
+ detection.detection_details.get('advisory_severity'),
128
+ *self._get_common_detection_fields(detection),
129
+ detection.detection_details.get('vulnerability_id')
130
+ ]
131
+
132
+ upgrade = ''
133
+ if alert.get("first_patched_version"):
134
+ upgrade = f'{alert.get("vulnerable_requirements")} -> {alert.get("first_patched_version")}'
135
+ row.append(upgrade)
136
+
137
+ return row
138
+
139
+ def _get_license(self, detection: Detection) -> List[str]:
140
+ row = self._get_common_detection_fields(detection)
141
+ row.append(f'{detection.detection_details.get("license")}')
142
+ return row
@@ -0,0 +1,61 @@
1
+ from typing import List, Dict, Optional, TYPE_CHECKING
2
+ from texttable import Texttable
3
+
4
+ if TYPE_CHECKING:
5
+ from cycode.cli.printers.table_models import ColumnInfo, ColumnWidths
6
+
7
+
8
+ class Table:
9
+ """Helper class to manage columns and their values in the right order and only if the column should be presented."""
10
+
11
+ def __init__(self, column_infos: Optional[List['ColumnInfo']] = None):
12
+ self._column_widths = None
13
+
14
+ self._columns: Dict['ColumnInfo', List[str]] = dict()
15
+ if column_infos:
16
+ self._columns: Dict['ColumnInfo', List[str]] = {columns: list() for columns in column_infos}
17
+
18
+ def add(self, column: 'ColumnInfo') -> None:
19
+ self._columns[column] = list()
20
+
21
+ def set(self, column: 'ColumnInfo', value: str) -> None:
22
+ # we push values only for existing columns what were added before
23
+ if column in self._columns:
24
+ self._columns[column].append(value)
25
+
26
+ def _get_ordered_columns(self) -> List['ColumnInfo']:
27
+ # we are sorting columns by index to make sure that columns will be printed in the right order
28
+ return sorted(self._columns, key=lambda column_info: column_info.index)
29
+
30
+ def get_columns_info(self) -> List['ColumnInfo']:
31
+ return self._get_ordered_columns()
32
+
33
+ def get_headers(self) -> List[str]:
34
+ return [header.name for header in self._get_ordered_columns()]
35
+
36
+ def get_rows(self) -> List[str]:
37
+ column_values = [self._columns[column_info] for column_info in self._get_ordered_columns()]
38
+ return list(zip(*column_values))
39
+
40
+ def set_cols_width(self, column_widths: 'ColumnWidths') -> None:
41
+ header_width_size = []
42
+ for header in self.get_columns_info():
43
+ width_multiplier = 1
44
+ if header in column_widths:
45
+ width_multiplier = column_widths[header]
46
+
47
+ header_width_size.append(len(header.name) * width_multiplier)
48
+
49
+ self._column_widths = header_width_size
50
+
51
+ def get_table(self, max_width: int = 80) -> Texttable:
52
+ table = Texttable(max_width)
53
+ table.header(self.get_headers())
54
+
55
+ for row in self.get_rows():
56
+ table.add_row(row)
57
+
58
+ if self._column_widths:
59
+ table.set_cols_width(self._column_widths)
60
+
61
+ return table
@@ -0,0 +1,20 @@
1
+ from typing import NamedTuple, Dict
2
+
3
+
4
+ class ColumnInfoBuilder:
5
+ _index = 0
6
+
7
+ @staticmethod
8
+ def build(name: str) -> 'ColumnInfo':
9
+ column_info = ColumnInfo(name, ColumnInfoBuilder._index)
10
+ ColumnInfoBuilder._index += 1
11
+ return column_info
12
+
13
+
14
+ class ColumnInfo(NamedTuple):
15
+ name: str
16
+ index: int # Represents the order of the columns, starting from the left
17
+
18
+
19
+ ColumnWidths = Dict[ColumnInfo, int]
20
+ ColumnWidthsConfig = Dict[str, ColumnWidths]
@@ -1,170 +1,116 @@
1
- from collections import defaultdict
2
- from typing import List, Dict
1
+ from typing import List
3
2
 
4
3
  import click
5
- from texttable import Texttable
6
4
 
7
- from cycode.cli.consts import LICENSE_COMPLIANCE_POLICY_ID, PACKAGE_VULNERABILITY_POLICY_ID
8
- from cycode.cli.models import DocumentDetections, Detection, CliError, CliResult
9
- from cycode.cli.printers.base_printer import BasePrinter
5
+ from cycode.cli.printers.base_table_printer import BaseTablePrinter
6
+ from cycode.cli.printers.table_models import ColumnInfoBuilder, ColumnWidthsConfig
7
+ from cycode.cli.printers.table import Table
8
+ from cycode.cli.utils.string_utils import obfuscate_text, get_position_in_line
9
+ from cycode.cli.consts import SECRET_SCAN_TYPE, INFRA_CONFIGURATION_SCAN_TYPE, SAST_SCAN_TYPE
10
+ from cycode.cli.models import DocumentDetections, Detection, Document
11
+
12
+ # Creation must have strict order. Represents the order of the columns in the table (from left to right)
13
+ ISSUE_TYPE_COLUMN = ColumnInfoBuilder.build(name='Issue Type')
14
+ RULE_ID_COLUMN = ColumnInfoBuilder.build(name='Rule ID')
15
+ FILE_PATH_COLUMN = ColumnInfoBuilder.build(name='File Path')
16
+ SECRET_SHA_COLUMN = ColumnInfoBuilder.build(name='Secret SHA')
17
+ COMMIT_SHA_COLUMN = ColumnInfoBuilder.build(name='Commit SHA')
18
+ LINE_NUMBER_COLUMN = ColumnInfoBuilder.build(name='Line Number')
19
+ COLUMN_NUMBER_COLUMN = ColumnInfoBuilder.build(name='Column Number')
20
+ VIOLATION_LENGTH_COLUMN = ColumnInfoBuilder.build(name='Violation Length')
21
+ VIOLATION_COLUMN = ColumnInfoBuilder.build(name='Violation')
22
+
23
+ COLUMN_WIDTHS_CONFIG: ColumnWidthsConfig = {
24
+ SECRET_SCAN_TYPE: {
25
+ ISSUE_TYPE_COLUMN: 2,
26
+ RULE_ID_COLUMN: 2,
27
+ FILE_PATH_COLUMN: 2,
28
+ SECRET_SHA_COLUMN: 2,
29
+ VIOLATION_COLUMN: 2,
30
+ },
31
+ INFRA_CONFIGURATION_SCAN_TYPE: {
32
+ ISSUE_TYPE_COLUMN: 4,
33
+ RULE_ID_COLUMN: 3,
34
+ FILE_PATH_COLUMN: 3,
35
+ },
36
+ SAST_SCAN_TYPE: {
37
+ ISSUE_TYPE_COLUMN: 7,
38
+ RULE_ID_COLUMN: 2,
39
+ FILE_PATH_COLUMN: 3,
40
+ },
41
+ }
42
+
43
+
44
+ class TablePrinter(BaseTablePrinter):
45
+ def _print_results(self, results: List[DocumentDetections]) -> None:
46
+ table = self._get_table()
47
+ if self.scan_type in COLUMN_WIDTHS_CONFIG:
48
+ table.set_cols_width(COLUMN_WIDTHS_CONFIG[self.scan_type])
49
+
50
+ for result in results:
51
+ for detection in result.detections:
52
+ self._enrich_table_with_values(table, detection, result.document)
53
+
54
+ click.echo(table.get_table().draw())
55
+
56
+ def _get_table(self) -> Table:
57
+ table = Table()
58
+
59
+ table.add(ISSUE_TYPE_COLUMN)
60
+ table.add(RULE_ID_COLUMN)
61
+ table.add(FILE_PATH_COLUMN)
62
+ table.add(LINE_NUMBER_COLUMN)
63
+ table.add(COLUMN_NUMBER_COLUMN)
10
64
 
11
- SEVERITY_COLUMN = 'Severity'
12
- LICENSE_COLUMN = 'License'
13
- UPGRADE_COLUMN = 'Upgrade'
14
- REPOSITORY_COLUMN = 'Repository'
15
- CVE_COLUMN = 'CVE'
16
-
17
- PREVIEW_DETECTIONS_COMMON_HEADERS = [
18
- 'File Path',
19
- 'Ecosystem',
20
- 'Dependency Name',
21
- 'Direct Dependency',
22
- 'Development Dependency'
23
- ]
24
-
25
-
26
- class TablePrinter(BasePrinter):
27
- RED_COLOR_NAME = 'red'
28
- WHITE_COLOR_NAME = 'white'
29
- GREEN_COLOR_NAME = 'green'
30
-
31
- def __init__(self, context: click.Context):
32
- super().__init__(context)
33
- self.scan_id = context.obj.get('scan_id')
34
-
35
- def print_result(self, result: CliResult) -> None:
36
- raise NotImplemented
37
-
38
- def print_error(self, error: CliError) -> None:
39
- raise NotImplemented
40
-
41
- def print_scan_results(self, results: List[DocumentDetections]):
42
- click.secho(f"Scan Results: (scan_id: {self.scan_id})")
43
-
44
- if not results:
45
- click.secho("Good job! No issues were found!!! 👏👏👏", fg=self.GREEN_COLOR_NAME)
46
- return
47
-
48
- detections_per_detection_type_id = self._extract_detections_per_detection_type_id(results)
49
-
50
- self._print_detection_per_detection_type_id(detections_per_detection_type_id)
51
-
52
- report_url = self.context.obj.get('report_url')
53
- if report_url:
54
- click.secho(f'Report URL: {report_url}')
65
+ if self._is_git_repository():
66
+ table.add(COMMIT_SHA_COLUMN)
55
67
 
56
- @staticmethod
57
- def _extract_detections_per_detection_type_id(results: List[DocumentDetections]) -> Dict[str, List[Detection]]:
58
- detections_per_detection_type_id = defaultdict(list)
68
+ if self.scan_type == SECRET_SCAN_TYPE:
69
+ table.add(SECRET_SHA_COLUMN)
70
+ table.add(VIOLATION_LENGTH_COLUMN)
71
+ table.add(VIOLATION_COLUMN)
59
72
 
60
- for document_detection in results:
61
- for detection in document_detection.detections:
62
- detections_per_detection_type_id[detection.detection_type_id].append(detection)
73
+ return table
63
74
 
64
- return detections_per_detection_type_id
75
+ def _enrich_table_with_values(self, table: Table, detection: Detection, document: Document) -> None:
76
+ self._enrich_table_with_detection_summary_values(table, detection, document)
77
+ self._enrich_table_with_detection_code_segment_values(table, detection, document)
65
78
 
66
- def _print_detection_per_detection_type_id(
67
- self, detections_per_detection_type_id: Dict[str, List[Detection]]
79
+ def _enrich_table_with_detection_summary_values(
80
+ self, table: Table, detection: Detection, document: Document
68
81
  ) -> None:
69
- for detection_type_id in detections_per_detection_type_id:
70
- detections = detections_per_detection_type_id[detection_type_id]
71
- headers = self._get_table_headers()
72
-
73
- title = None
74
- rows = []
75
-
76
- if detection_type_id == PACKAGE_VULNERABILITY_POLICY_ID:
77
- title = "Dependencies Vulnerabilities"
78
-
79
- headers = [SEVERITY_COLUMN] + headers
80
- headers.extend(PREVIEW_DETECTIONS_COMMON_HEADERS)
81
- headers.append(CVE_COLUMN)
82
- headers.append(UPGRADE_COLUMN)
83
-
84
- for detection in detections:
85
- rows.append(self._get_upgrade_package_vulnerability(detection))
86
- elif detection_type_id == LICENSE_COMPLIANCE_POLICY_ID:
87
- title = "License Compliance"
88
-
89
- headers.extend(PREVIEW_DETECTIONS_COMMON_HEADERS)
90
- headers.append(LICENSE_COLUMN)
91
-
92
- for detection in detections:
93
- rows.append(self._get_license(detection))
94
-
95
- if rows:
96
- self._print_table_detections(detections, headers, rows, title)
97
-
98
- def _get_table_headers(self) -> list:
99
- if self._is_git_repository():
100
- return [REPOSITORY_COLUMN]
101
-
102
- return []
103
-
104
- def _print_table_detections(
105
- self, detections: List[Detection], headers: List[str], rows, title: str
82
+ issue_type = detection.message
83
+ if self.scan_type == SECRET_SCAN_TYPE:
84
+ issue_type = detection.type
85
+
86
+ table.set(ISSUE_TYPE_COLUMN, issue_type)
87
+ table.set(RULE_ID_COLUMN, detection.detection_rule_id)
88
+ table.set(FILE_PATH_COLUMN, click.format_filename(document.path))
89
+ table.set(SECRET_SHA_COLUMN, detection.detection_details.get('sha512', ''))
90
+ table.set(COMMIT_SHA_COLUMN, detection.detection_details.get('commit_id', ''))
91
+
92
+ def _enrich_table_with_detection_code_segment_values(
93
+ self, table: Table, detection: Detection, document: Document
106
94
  ) -> None:
107
- self._print_summary_issues(detections, title)
108
- text_table = Texttable()
109
- text_table.header(headers)
110
-
111
- self.set_table_width(headers, text_table)
112
-
113
- for row in rows:
114
- text_table.add_row(row)
115
-
116
- click.echo(text_table.draw())
117
-
118
- @staticmethod
119
- def set_table_width(headers: List[str], text_table: Texttable) -> None:
120
- header_width_size_cols = []
121
- for header in headers:
122
- header_len = len(header)
123
- if header == CVE_COLUMN:
124
- header_width_size_cols.append(header_len * 5)
125
- elif header == UPGRADE_COLUMN:
126
- header_width_size_cols.append(header_len * 2)
127
- else:
128
- header_width_size_cols.append(header_len)
129
- text_table.set_cols_width(header_width_size_cols)
130
-
131
- @staticmethod
132
- def _print_summary_issues(detections: List, title: str) -> None:
133
- click.echo(f'⛔ Found {len(detections)} issues of type: {click.style(title, bold=True)}')
134
-
135
- def _get_common_detection_fields(self, detection: Detection) -> List[str]:
136
- row = [
137
- detection.detection_details.get('file_name'),
138
- detection.detection_details.get('ecosystem'),
139
- detection.detection_details.get('package_name'),
140
- detection.detection_details.get('is_direct_dependency_str'),
141
- detection.detection_details.get('is_dev_dependency_str')
142
- ]
143
-
144
- if self._is_git_repository():
145
- row = [detection.detection_details.get('repository_name')] + row
146
-
147
- return row
95
+ detection_details = detection.detection_details
148
96
 
149
- def _is_git_repository(self) -> bool:
150
- return self.context.obj.get("remote_url") is not None
97
+ detection_line = detection_details.get('line_in_file', -1)
98
+ if self.scan_type == SECRET_SCAN_TYPE:
99
+ detection_line = detection_details.get('line', -1)
151
100
 
152
- def _get_upgrade_package_vulnerability(self, detection: Detection) -> List[str]:
153
- alert = detection.detection_details.get('alert')
154
- row = [
155
- detection.detection_details.get('advisory_severity'),
156
- *self._get_common_detection_fields(detection),
157
- detection.detection_details.get('vulnerability_id')
158
- ]
101
+ detection_column = get_position_in_line(document.content, detection_details.get('start_position', -1))
102
+ violation_length = detection_details.get('length', -1)
159
103
 
160
- upgrade = ''
161
- if alert.get("first_patched_version"):
162
- upgrade = f'{alert.get("vulnerable_requirements")} -> {alert.get("first_patched_version")}'
163
- row.append(upgrade)
104
+ violation = ''
105
+ file_content_lines = document.content.splitlines()
106
+ if detection_line < len(file_content_lines):
107
+ line = file_content_lines[detection_line]
108
+ violation = line[detection_column: detection_column + violation_length]
164
109
 
165
- return row
110
+ if not self.show_secret:
111
+ violation = obfuscate_text(violation)
166
112
 
167
- def _get_license(self, detection: Detection) -> List[str]:
168
- row = self._get_common_detection_fields(detection)
169
- row.append(f'{detection.detection_details.get("license")}')
170
- return row
113
+ table.set(LINE_NUMBER_COLUMN, str(detection_line))
114
+ table.set(COLUMN_NUMBER_COLUMN, str(detection_column))
115
+ table.set(VIOLATION_LENGTH_COLUMN, f'{violation_length} chars')
116
+ table.set(VIOLATION_COLUMN, violation)
@@ -7,14 +7,10 @@ from cycode.cli.printers.base_printer import BasePrinter
7
7
  from cycode.cli.models import DocumentDetections, Detection, Document, CliResult, CliError
8
8
  from cycode.cli.config import config
9
9
  from cycode.cli.consts import SECRET_SCAN_TYPE, COMMIT_RANGE_BASED_COMMAND_SCAN_TYPES
10
- from cycode.cli.utils.string_utils import obfuscate_text
10
+ from cycode.cli.utils.string_utils import obfuscate_text, get_position_in_line
11
11
 
12
12
 
13
13
  class TextPrinter(BasePrinter):
14
- RED_COLOR_NAME = 'red'
15
- WHITE_COLOR_NAME = 'white'
16
- GREEN_COLOR_NAME = 'green'
17
-
18
14
  def __init__(self, context: click.Context):
19
15
  super().__init__(context)
20
16
  self.scan_id: str = context.obj.get('scan_id')
@@ -132,9 +128,6 @@ class TextPrinter(BasePrinter):
132
128
 
133
129
  return self.WHITE_COLOR_NAME
134
130
 
135
- def _get_position_in_line(self, text: str, position: int) -> int:
136
- return position - text.rfind('\n', 0, position) - 1
137
-
138
131
  def _get_line_number_style(self, line_number: int):
139
132
  return f'{click.style(str(line_number), fg=self.WHITE_COLOR_NAME, bold=False)} ' \
140
133
  f'{click.style("|", fg=self.RED_COLOR_NAME, bold=False)}'
@@ -158,7 +151,7 @@ class TextPrinter(BasePrinter):
158
151
  file_content = document.content
159
152
  file_lines = file_content.splitlines()
160
153
  start_line = self._get_code_segment_start_line(detection_line, code_segment_size)
161
- detection_position_in_line = self._get_position_in_line(file_content, detection_position)
154
+ detection_position_in_line = get_position_in_line(file_content, detection_position)
162
155
 
163
156
  click.echo()
164
157
  for i in range(code_segment_size):
@@ -182,7 +175,7 @@ class TextPrinter(BasePrinter):
182
175
  git_diff_content = document.content
183
176
  git_diff_lines = git_diff_content.splitlines()
184
177
  detection_line = git_diff_lines[detection_line_number]
185
- detection_position_in_line = self._get_position_in_line(git_diff_content, detection_position)
178
+ detection_position_in_line = get_position_in_line(git_diff_content, detection_position)
186
179
 
187
180
  click.echo()
188
181
  self._print_detection_line(document, detection_line, detection_line_number_in_original_file,
@@ -43,3 +43,7 @@ def generate_random_string(string_len: int):
43
43
  # letters, digits, and symbols
44
44
  characters = string.ascii_letters + string.digits + string.punctuation
45
45
  return ''.join(random.choice(characters) for _ in range(string_len))
46
+
47
+
48
+ def get_position_in_line(text: str, position: int) -> int:
49
+ return position - text.rfind('\n', 0, position) - 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cycode
3
- Version: 0.2.5.dev6
3
+ Version: 0.2.5.dev7
4
4
  Summary: Perform secrets/iac scans for your sources using Cycode's engine
5
5
  Home-page: https://github.com/cycodehq-public/cycode-cli
6
6
  License: MIT
@@ -248,12 +248,12 @@ repos:
248
248
 
249
249
  The following are the options and commands available with the Cycode CLI application:
250
250
 
251
- | Option | Description |
252
- |-------------------------|-----------------------------------------------------------|
253
- | `--output [text\|json]` | Specify the output (`text`/`json`). The default is `text` |
254
- | `-v`, `--verbose` | Show detailed logs |
255
- | `--version` | Show the version and exit. |
256
- | `--help` | Show options for given command. |
251
+ | Option | Description |
252
+ |--------------------------------|-------------------------------------------------------------------|
253
+ | `--output [text\|json\|table]` | Specify the output (`text`/`json`/`table`). The default is `text` |
254
+ | `-v`, `--verbose` | Show detailed logs |
255
+ | `--version` | Show the version and exit. |
256
+ | `--help` | Show options for given command. |
257
257
 
258
258
  | Command | Description |
259
259
  |-------------------------------------|-------------|
@@ -1,4 +1,4 @@
1
- cycode/__init__.py,sha256=eB9HbCdO5PSyPNYbBJq4dF8ZSWLq96DzFhK8ouKSMuQ,115
1
+ cycode/__init__.py,sha256=1G_PZu1pEa6ExgHfDJrd83gP_4TQvL4xyZUMOLQuQ2E,115
2
2
  cycode/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  cycode/cli/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  cycode/cli/auth/auth_command.py,sha256=YG3Y0gjIXcyq9k3PNIqPHoMYnMuxG_07kCKVtaXE0p8,2813
@@ -16,14 +16,18 @@ cycode/cli/helpers/maven/base_restore_maven_dependencies.py,sha256=kcj6HmwmFbG6I
16
16
  cycode/cli/helpers/maven/restore_gradle_dependencies.py,sha256=BnJcFdkd642iBL48MJ7n7r8LmSCBWCHLeaReOt-yzhc,992
17
17
  cycode/cli/helpers/maven/restore_maven_dependencies.py,sha256=vPVb2sfPRTL3GevAe3ibqR-eHwQOP34mslhg_fSUxTg,2993
18
18
  cycode/cli/helpers/sca_code_scanner.py,sha256=G7NSNEIeyjFCYPynbae4WOtf7PB4OAiis-kjufNYd8c,5835
19
- cycode/cli/main.py,sha256=JCdEE5eBdjysK63vnWb5Ki0WRRfPVAh1kAvmj8k0xjA,7213
19
+ cycode/cli/main.py,sha256=jMhKv6PdZYXMFY1GOiwCAar6VKIBPNfQ-J7jCaBjRfs,7243
20
20
  cycode/cli/models.py,sha256=-4tVdQXmT4XqIbrXPkHs5FI6FsRzeDsFasFkU_YfZLY,1332
21
21
  cycode/cli/printers/__init__.py,sha256=ALwAXSZy2lNXWC3NfCIxf8K0F6eFrbZa9PLZwPINi5E,93
22
- cycode/cli/printers/base_printer.py,sha256=Kix0v9OI_JKVH-3PNwUWNX7BXVLUxRDnOy7MsdWXAMw,564
23
- cycode/cli/printers/console_printer.py,sha256=sQZ5Rppoj05MgHhDe9pzgykmyYuH9UjcR3Q3einPj9Y,1709
22
+ cycode/cli/printers/base_printer.py,sha256=YUNT6uJsPLqcjJmCrCzOELSm1ONgIAHbHIdjfTvgBwY,626
23
+ cycode/cli/printers/base_table_printer.py,sha256=TX2zDL4cRxNUoGRsc-DsZRM3MzBQc8pBKKn2IPx6lCk,1483
24
+ cycode/cli/printers/console_printer.py,sha256=A_T8VQTqwzCMKbwDQmerI8y9W9itAmH5ERDJ8KloVwg,1891
24
25
  cycode/cli/printers/json_printer.py,sha256=Gx-aCEGAnStZbRhMZD6nCYfAaRrWImoEpu8XzgYardY,1537
25
- cycode/cli/printers/table_printer.py,sha256=FbHSMwRwr8unPNuRXtmT7g0qBq5NSb2WTmJm0LtMQSE,6108
26
- cycode/cli/printers/text_printer.py,sha256=20pRxATAo9VVf-qpsntsSN7YIO3OVtoCnKwGN6TA5v0,9166
26
+ cycode/cli/printers/sca_table_printer.py,sha256=mP0GGAKUolaXYCyx63R-_rV2guv7PxPAATZBAkNlfns,5272
27
+ cycode/cli/printers/table.py,sha256=KJnDYLUH6PJJdolH8ayky6oVbV1OMFcGOe2RSXJSEQY,2277
28
+ cycode/cli/printers/table_models.py,sha256=DiGwXKZMcgRRyD27jWZjr0nrJ1O9-1gVjnQ_u6NUazY,477
29
+ cycode/cli/printers/table_printer.py,sha256=job1W61U1UIvJ6dAME1idXfJ9oB8g87e5L3EW9WCgFU,4701
30
+ cycode/cli/printers/text_printer.py,sha256=SrcBrgUag93ByHJ9lwpM4QhRwXuzqm4TiNronS4E8NY,8955
27
31
  cycode/cli/user_settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
32
  cycode/cli/user_settings/base_file_manager.py,sha256=4oFLLv7CwaqaMpz6XRA-cz5aG3DES6Mq_7RUWY_b3aY,533
29
33
  cycode/cli/user_settings/config_file_manager.py,sha256=1YlWBk30Ep5f3ysQjLYdzWuKuLyiXPC4xbw4ndQoGvw,4821
@@ -34,7 +38,7 @@ cycode/cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
34
38
  cycode/cli/utils/path_utils.py,sha256=wzUxLKLfjI8vnmDTN-VOjWpvREkMhgDRCOXZu_HbNpc,2162
35
39
  cycode/cli/utils/scan_utils.py,sha256=Q-XqLXxvyC0Z2kwZjjw6riRRnovQaYFyhlAhRyr_NDA,195
36
40
  cycode/cli/utils/shell_executor.py,sha256=vRA7YqFLFcdWgGToZ1ek2zgQX8p-29oxWpAJt0wqWC4,941
37
- cycode/cli/utils/string_utils.py,sha256=O4OKzo1_c3oM5783h33F27UufV-R_Oc3BcdVkm5Z8-s,1229
41
+ cycode/cli/utils/string_utils.py,sha256=wZh0SEiYTtiqJeYqjKnAmCV_TYglsSWkdLbCrO4PtEs,1346
38
42
  cycode/cli/utils/task_timer.py,sha256=r1SLglU3RXHZn2AJuTKY5sFIZIWQPyXwAzjHmrpxxHQ,2700
39
43
  cycode/cli/utils/yaml_utils.py,sha256=-mEFULELw-GpR2g0AdafLshHdmM76p4k0FlT7t6-bAI,815
40
44
  cycode/cli/zip_file.py,sha256=Uao-iTJMDb039jU04F7tl1lS5_PmWl1Ama9IySRzFNI,929
@@ -53,7 +57,7 @@ cycode/cyclient/scan_config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
53
57
  cycode/cyclient/scan_config/scan_config_base.py,sha256=iX7S-XSSZfkyFfUzGZPc48PkmNNr_D-9rzwlT9B64DM,1090
54
58
  cycode/cyclient/scan_config/scan_config_creator.py,sha256=xwVVPCaWVYUNArGAvQ9nwWMD65Hoqi4A5ZMncgiZ1JY,1021
55
59
  cycode/cyclient/utils.py,sha256=gYVYaEwXKH01uQqXPmrBhqmCv0sVVvjvGXGbdLxk1jE,197
56
- cycode-0.2.5.dev6.dist-info/METADATA,sha256=3idSwnadQO_DlvK11WOO1YaB0MtZzOkFMyUjAFvdM0k,33844
57
- cycode-0.2.5.dev6.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
58
- cycode-0.2.5.dev6.dist-info/entry_points.txt,sha256=GKZlS6LtUdABDPd7-o9bwNSI5gYQnyA3qGrFFQKt3Vc,51
59
- cycode-0.2.5.dev6.dist-info/RECORD,,
60
+ cycode-0.2.5.dev7.dist-info/METADATA,sha256=n5NZzsZpWW4Pn0WglYTV3sOzpHzUzK_-OsNchmk99iU,33934
61
+ cycode-0.2.5.dev7.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
62
+ cycode-0.2.5.dev7.dist-info/entry_points.txt,sha256=GKZlS6LtUdABDPd7-o9bwNSI5gYQnyA3qGrFFQKt3Vc,51
63
+ cycode-0.2.5.dev7.dist-info/RECORD,,