conviso-ast 3.0.0__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.
- conviso_ast-3.0.0.data/scripts/flow_bash_completer.sh +21 -0
- conviso_ast-3.0.0.data/scripts/flow_fish_completer.fish +1 -0
- conviso_ast-3.0.0.data/scripts/flow_zsh_completer.sh +32 -0
- conviso_ast-3.0.0.dist-info/METADATA +37 -0
- conviso_ast-3.0.0.dist-info/RECORD +128 -0
- conviso_ast-3.0.0.dist-info/WHEEL +5 -0
- conviso_ast-3.0.0.dist-info/entry_points.txt +3 -0
- conviso_ast-3.0.0.dist-info/top_level.txt +1 -0
- convisoappsec/__init__.py +0 -0
- convisoappsec/common/__init__.py +5 -0
- convisoappsec/common/box.py +251 -0
- convisoappsec/common/cleaner.py +78 -0
- convisoappsec/common/docker.py +399 -0
- convisoappsec/common/exceptions.py +8 -0
- convisoappsec/common/git_data_parser.py +76 -0
- convisoappsec/common/graphql/__init__.py +0 -0
- convisoappsec/common/graphql/error_handlers.py +75 -0
- convisoappsec/common/graphql/errors.py +16 -0
- convisoappsec/common/graphql/low_client.py +51 -0
- convisoappsec/common/retry_handler.py +40 -0
- convisoappsec/common/strings.py +8 -0
- convisoappsec/flow/__init__.py +3 -0
- convisoappsec/flow/api.py +104 -0
- convisoappsec/flow/cleaner.py +118 -0
- convisoappsec/flow/graphql_api/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/client.py +18 -0
- convisoappsec/flow/graphql_api/beta/models/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/models/issues/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/models/issues/container.py +72 -0
- convisoappsec/flow/graphql_api/beta/models/issues/iac.py +6 -0
- convisoappsec/flow/graphql_api/beta/models/issues/normalize.py +13 -0
- convisoappsec/flow/graphql_api/beta/models/issues/sast.py +53 -0
- convisoappsec/flow/graphql_api/beta/models/issues/sca.py +78 -0
- convisoappsec/flow/graphql_api/beta/resources_api.py +142 -0
- convisoappsec/flow/graphql_api/beta/schemas/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/schemas/mutations/__init__.py +61 -0
- convisoappsec/flow/graphql_api/beta/schemas/resolvers/__init__.py +0 -0
- convisoappsec/flow/graphql_api/v1/__init__.py +0 -0
- convisoappsec/flow/graphql_api/v1/client.py +46 -0
- convisoappsec/flow/graphql_api/v1/models/__init__.py +0 -0
- convisoappsec/flow/graphql_api/v1/models/asset.py +14 -0
- convisoappsec/flow/graphql_api/v1/models/issues.py +16 -0
- convisoappsec/flow/graphql_api/v1/models/project.py +35 -0
- convisoappsec/flow/graphql_api/v1/resources_api.py +489 -0
- convisoappsec/flow/graphql_api/v1/schemas/__init__.py +0 -0
- convisoappsec/flow/graphql_api/v1/schemas/mutations/__init__.py +212 -0
- convisoappsec/flow/graphql_api/v1/schemas/resolvers/__init__.py +180 -0
- convisoappsec/flow/source_code_scanner/__init__.py +9 -0
- convisoappsec/flow/source_code_scanner/exceptions.py +2 -0
- convisoappsec/flow/source_code_scanner/scc.py +68 -0
- convisoappsec/flow/source_code_scanner/source_code_scanner.py +177 -0
- convisoappsec/flow/util/__init__.py +7 -0
- convisoappsec/flow/util/ci_provider.py +99 -0
- convisoappsec/flow/util/metrics.py +16 -0
- convisoappsec/flow/util/source_code_compressor.py +22 -0
- convisoappsec/flow/version_control_system_adapter.py +528 -0
- convisoappsec/flow/version_searchers/__init__.py +9 -0
- convisoappsec/flow/version_searchers/sorted_by_versioning_style.py +85 -0
- convisoappsec/flow/version_searchers/timebased_version_seacher.py +39 -0
- convisoappsec/flow/version_searchers/version_searcher_result.py +33 -0
- convisoappsec/flow/versioning_style/__init__.py +0 -0
- convisoappsec/flow/versioning_style/semantic_versioning.py +44 -0
- convisoappsec/flowcli/__init__.py +3 -0
- convisoappsec/flowcli/__main__.py +4 -0
- convisoappsec/flowcli/assets/__init__.py +4 -0
- convisoappsec/flowcli/assets/create.py +88 -0
- convisoappsec/flowcli/assets/entrypoint.py +20 -0
- convisoappsec/flowcli/assets/ls.py +63 -0
- convisoappsec/flowcli/ast/__init__.py +3 -0
- convisoappsec/flowcli/ast/entrypoint.py +427 -0
- convisoappsec/flowcli/common.py +175 -0
- convisoappsec/flowcli/companies/__init__.py +0 -0
- convisoappsec/flowcli/companies/ls.py +25 -0
- convisoappsec/flowcli/container/__init__.py +3 -0
- convisoappsec/flowcli/container/entrypoint.py +17 -0
- convisoappsec/flowcli/container/run.py +306 -0
- convisoappsec/flowcli/context.py +49 -0
- convisoappsec/flowcli/deploy/__init__.py +0 -0
- convisoappsec/flowcli/deploy/create/__init__.py +4 -0
- convisoappsec/flowcli/deploy/create/context.py +12 -0
- convisoappsec/flowcli/deploy/create/entrypoint.py +31 -0
- convisoappsec/flowcli/deploy/create/with_/__init__.py +3 -0
- convisoappsec/flowcli/deploy/create/with_/entrypoint.py +20 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/__init__.py +4 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/context.py +11 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/entrypoint.py +30 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/__init__.py +4 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/entrypoint.py +21 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/time_.py +84 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/versioning_style.py +115 -0
- convisoappsec/flowcli/deploy/create/with_/values.py +133 -0
- convisoappsec/flowcli/entrypoint.py +103 -0
- convisoappsec/flowcli/environment_checker.py +45 -0
- convisoappsec/flowcli/findings/__init__.py +4 -0
- convisoappsec/flowcli/findings/create/__init__.py +4 -0
- convisoappsec/flowcli/findings/create/entrypoint.py +18 -0
- convisoappsec/flowcli/findings/create/with_/__init__.py +3 -0
- convisoappsec/flowcli/findings/create/with_/entrypoint.py +19 -0
- convisoappsec/flowcli/findings/create/with_/version_tracker.py +93 -0
- convisoappsec/flowcli/findings/entrypoint.py +19 -0
- convisoappsec/flowcli/findings/import_sarif/__init__.py +4 -0
- convisoappsec/flowcli/findings/import_sarif/entrypoint.py +430 -0
- convisoappsec/flowcli/help_option.py +18 -0
- convisoappsec/flowcli/iac/__init__.py +3 -0
- convisoappsec/flowcli/iac/entrypoint.py +17 -0
- convisoappsec/flowcli/iac/run.py +328 -0
- convisoappsec/flowcli/requirements_verifier.py +132 -0
- convisoappsec/flowcli/sast/__init__.py +3 -0
- convisoappsec/flowcli/sast/entrypoint.py +17 -0
- convisoappsec/flowcli/sast/run.py +485 -0
- convisoappsec/flowcli/sbom/__init__.py +3 -0
- convisoappsec/flowcli/sbom/entrypoint.py +17 -0
- convisoappsec/flowcli/sbom/generate.py +235 -0
- convisoappsec/flowcli/sca/__init__.py +3 -0
- convisoappsec/flowcli/sca/entrypoint.py +17 -0
- convisoappsec/flowcli/sca/run.py +479 -0
- convisoappsec/flowcli/vulnerability/__init__.py +3 -0
- convisoappsec/flowcli/vulnerability/assert_security_rules.py +201 -0
- convisoappsec/flowcli/vulnerability/container_vulnerability_manager.py +175 -0
- convisoappsec/flowcli/vulnerability/entrypoint.py +18 -0
- convisoappsec/flowcli/vulnerability/rules_schema.json +53 -0
- convisoappsec/flowcli/vulnerability/run.py +487 -0
- convisoappsec/logger.py +29 -0
- convisoappsec/sast/__init__.py +0 -0
- convisoappsec/sast/decision.py +45 -0
- convisoappsec/sast/sastbox.py +296 -0
- convisoappsec/version.py +1 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import click_log
|
|
3
|
+
import json
|
|
4
|
+
from base64 import b64decode
|
|
5
|
+
from re import search as regex_search
|
|
6
|
+
from copy import deepcopy as clone
|
|
7
|
+
from convisoappsec.flowcli import help_option
|
|
8
|
+
from convisoappsec.flowcli.context import pass_flow_context
|
|
9
|
+
from convisoappsec.logger import LOGGER
|
|
10
|
+
from convisoappsec.flowcli.requirements_verifier import RequirementsVerifier
|
|
11
|
+
from convisoappsec.flow.graphql_api.beta.models.issues.sast import (CreateSastFindingInput)
|
|
12
|
+
from convisoappsec.flow.graphql_api.beta.models.issues.sca import CreateScaFindingInput
|
|
13
|
+
from convisoappsec.common.graphql.errors import ResponseError
|
|
14
|
+
|
|
15
|
+
click_log.basic_config(LOGGER)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.command()
|
|
19
|
+
@click.option(
|
|
20
|
+
"-i",
|
|
21
|
+
"--input-file",
|
|
22
|
+
required=True,
|
|
23
|
+
type=click.Path(exists=True),
|
|
24
|
+
help='The path to SARIF file.',
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--company-id",
|
|
28
|
+
required=False,
|
|
29
|
+
envvar=("CONVISO_COMPANY_ID", "FLOW_COMPANY_ID"),
|
|
30
|
+
help="Company ID on Conviso Platform",
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"-r",
|
|
34
|
+
"--repository-dir",
|
|
35
|
+
default=".",
|
|
36
|
+
show_default=True,
|
|
37
|
+
type=click.Path(
|
|
38
|
+
exists=True,
|
|
39
|
+
resolve_path=True,
|
|
40
|
+
),
|
|
41
|
+
required=False,
|
|
42
|
+
help="The source code repository directory.",
|
|
43
|
+
)
|
|
44
|
+
@click.option(
|
|
45
|
+
'--asset-name',
|
|
46
|
+
required=False,
|
|
47
|
+
envvar=("CONVISO_ASSET_NAME", "FLOW_ASSET_NAME"),
|
|
48
|
+
help="Provides a asset name.",
|
|
49
|
+
)
|
|
50
|
+
@help_option
|
|
51
|
+
@pass_flow_context
|
|
52
|
+
@click.pass_context
|
|
53
|
+
def import_sarif(context, flow_context, input_file, company_id, repository_dir, asset_name):
|
|
54
|
+
context.params['company_id'] = company_id if company_id is not None else None
|
|
55
|
+
context.params['repository_dir'] = repository_dir
|
|
56
|
+
|
|
57
|
+
prepared_context = RequirementsVerifier.prepare_context(clone(context))
|
|
58
|
+
asset_id = prepared_context.params['asset_id']
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
conviso_api = flow_context.create_conviso_api_client_beta()
|
|
62
|
+
LOGGER.info("💬 Starting the import process for the SARIF file.")
|
|
63
|
+
parse_sarif_file(conviso_api, asset_id, input_file)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
LOGGER.error(f"❌ Error during SARIF file import: {str(e)}")
|
|
66
|
+
raise Exception("SARIF file import failed. Please contact support and provide the SARIF file for assistance.")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def parse_sarif_file(conviso_api, asset_id, sarif_file):
|
|
70
|
+
try:
|
|
71
|
+
sarif_data = _load_sarif_data(sarif_file)
|
|
72
|
+
if not sarif_data:
|
|
73
|
+
raise Exception("Failed to load SARIF file")
|
|
74
|
+
|
|
75
|
+
sarif_infos = _extract_rules_info(sarif_data)
|
|
76
|
+
|
|
77
|
+
success_count = 0
|
|
78
|
+
error_count = 0
|
|
79
|
+
|
|
80
|
+
for run in sarif_data.get('runs', []):
|
|
81
|
+
for result in run.get('results', []):
|
|
82
|
+
try:
|
|
83
|
+
if _process_vulnerability(conviso_api, asset_id, result, sarif_infos):
|
|
84
|
+
success_count += 1
|
|
85
|
+
else:
|
|
86
|
+
error_count += 1
|
|
87
|
+
except Exception as e:
|
|
88
|
+
LOGGER.error(f"❌ Error processing vulnerability: {str(e)}")
|
|
89
|
+
error_count += 1
|
|
90
|
+
|
|
91
|
+
LOGGER.info(f"✅ Successfully processed {success_count} vulnerabilities")
|
|
92
|
+
if error_count > 0:
|
|
93
|
+
LOGGER.warning(f"⚠️ {error_count} vulnerabilities with errors")
|
|
94
|
+
|
|
95
|
+
LOGGER.info("✅ SARIF file import completed successfully.")
|
|
96
|
+
|
|
97
|
+
except Exception as e:
|
|
98
|
+
LOGGER.error(f"❌ Error during SARIF file parsing: {str(e)}")
|
|
99
|
+
raise
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _load_sarif_data(sarif_file):
|
|
103
|
+
try:
|
|
104
|
+
cleaned_file = clean_file(sarif_file)
|
|
105
|
+
|
|
106
|
+
with open(cleaned_file, 'r', encoding='utf-8') as file:
|
|
107
|
+
sarif_data = json.load(file)
|
|
108
|
+
|
|
109
|
+
if not isinstance(sarif_data, dict) or 'runs' not in sarif_data:
|
|
110
|
+
LOGGER.error("❌ Invalid SARIF file: 'runs' structure not found")
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
return sarif_data
|
|
114
|
+
|
|
115
|
+
except json.JSONDecodeError as e:
|
|
116
|
+
LOGGER.error(f"❌ Error decoding JSON: {str(e)}")
|
|
117
|
+
return None
|
|
118
|
+
except Exception as e:
|
|
119
|
+
LOGGER.error(f"❌ Error loading file: {str(e)}")
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _extract_rules_info(sarif_data):
|
|
124
|
+
sarif_infos = []
|
|
125
|
+
|
|
126
|
+
for run in sarif_data.get('runs', []):
|
|
127
|
+
for rule in run.get('tool', {}).get('driver', {}).get('rules', []):
|
|
128
|
+
title = (rule.get('shortDescription', {}).get('text') or
|
|
129
|
+
rule.get('name') or
|
|
130
|
+
rule.get('fullDescription', {}).get('text') or
|
|
131
|
+
rule.get('help', {}).get('text'))
|
|
132
|
+
|
|
133
|
+
rule_info = {
|
|
134
|
+
"id": rule.get('id'),
|
|
135
|
+
"name": title,
|
|
136
|
+
"references": rule.get('helpUri'),
|
|
137
|
+
"description": _get_rule_description(rule)
|
|
138
|
+
}
|
|
139
|
+
sarif_infos.append(rule_info)
|
|
140
|
+
|
|
141
|
+
return sarif_infos
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _get_rule_description(rule):
|
|
145
|
+
return (rule.get('help', {}).get('text') or
|
|
146
|
+
rule.get('fullDescription', {}).get('text') or
|
|
147
|
+
rule.get('shortDescription', {}).get('text'))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _process_vulnerability(conviso_api, asset_id, result, sarif_infos):
|
|
151
|
+
try:
|
|
152
|
+
if not result.get('ruleId'):
|
|
153
|
+
LOGGER.warning("⚠️ Result without ruleId, skipping...")
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
vuln_data = _extract_vulnerability_data(result, sarif_infos)
|
|
157
|
+
if not vuln_data:
|
|
158
|
+
LOGGER.warning(f"⚠️ Could not extract data for {result.get('ruleId')}")
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
rule_id = result.get('ruleId', '')
|
|
162
|
+
|
|
163
|
+
if "(sca)" in rule_id.lower():
|
|
164
|
+
return _process_sca_vulnerability(conviso_api, asset_id, result, vuln_data)
|
|
165
|
+
elif "(sast)" in rule_id.lower():
|
|
166
|
+
return _process_sast_vulnerability(conviso_api, asset_id, result, vuln_data)
|
|
167
|
+
else:
|
|
168
|
+
return _process_sast_vulnerability(conviso_api, asset_id, result, vuln_data)
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
LOGGER.error(f"❌ Error processing vulnerability: {str(e)}")
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _extract_vulnerability_data(result, sarif_infos):
|
|
176
|
+
try:
|
|
177
|
+
rule_id = result.get('ruleId')
|
|
178
|
+
matching_info = next((info for info in sarif_infos if info['id'] == rule_id), None)
|
|
179
|
+
title = None
|
|
180
|
+
references = None
|
|
181
|
+
description = None
|
|
182
|
+
|
|
183
|
+
if matching_info:
|
|
184
|
+
title = matching_info['name']
|
|
185
|
+
references = matching_info['references']
|
|
186
|
+
description = matching_info['description']
|
|
187
|
+
|
|
188
|
+
if not title:
|
|
189
|
+
title = result.get('message', {}).get('text', 'No title provided')
|
|
190
|
+
if not description:
|
|
191
|
+
description = result.get('message', {}).get('text', 'No description provided')
|
|
192
|
+
|
|
193
|
+
locations = result.get('locations', [])
|
|
194
|
+
if not locations:
|
|
195
|
+
LOGGER.warning(f"⚠️ No location found for {rule_id}")
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
first_location = locations[0]
|
|
199
|
+
physical_location = first_location.get('physicalLocation', {})
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
'title': title,
|
|
203
|
+
'references': references,
|
|
204
|
+
'description': description,
|
|
205
|
+
'severity': result.get('level', 'info'),
|
|
206
|
+
'file_name': physical_location.get('artifactLocation', {}).get('uri'),
|
|
207
|
+
'vulnerable_line': physical_location.get('region', {}).get('startLine'),
|
|
208
|
+
'first_line': physical_location.get('region', {}).get('startLine', 1),
|
|
209
|
+
'code_snippet': physical_location.get('contextRegion', {}).get('snippet', {}).get('text', ''),
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
LOGGER.error(f"❌ Error extracting vulnerability data: {str(e)}")
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _process_sca_vulnerability(conviso_api, asset_id, result, vuln_data):
|
|
218
|
+
try:
|
|
219
|
+
message_text = result.get('message', {}).get('text', '')
|
|
220
|
+
sca_data = _parse_sca_data(message_text)
|
|
221
|
+
|
|
222
|
+
if not sca_data:
|
|
223
|
+
LOGGER.warning(f"⚠️ Could not extract SCA data from: {message_text}")
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
create_sca_vulnerabilities(
|
|
227
|
+
conviso_api, asset_id, message_text,
|
|
228
|
+
vuln_data['references'], vuln_data['description'],
|
|
229
|
+
vuln_data['severity'], vuln_data['file_name'],
|
|
230
|
+
vuln_data['first_line'], sca_data['package'],
|
|
231
|
+
sca_data['version'], sca_data['cve']
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
except Exception as e:
|
|
237
|
+
LOGGER.error(f"❌ Error processing SCA vulnerability: {str(e)}")
|
|
238
|
+
return False
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _parse_sca_data(message_text):
|
|
242
|
+
try:
|
|
243
|
+
if ':' not in message_text:
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
package = message_text.split(':')[1].split(' ')[0]
|
|
247
|
+
version = package.split('-')[-1] if '-' in package else 'Unknown'
|
|
248
|
+
cve = 'Unknown'
|
|
249
|
+
|
|
250
|
+
if '(' in message_text and ')' in message_text:
|
|
251
|
+
parts = message_text.split(' ')
|
|
252
|
+
for part in parts:
|
|
253
|
+
if part.startswith('(') and part.endswith(')'):
|
|
254
|
+
cve = part.strip('()')
|
|
255
|
+
break
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
'package': package,
|
|
259
|
+
'version': version,
|
|
260
|
+
'cve': cve
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
LOGGER.error(f"❌ Error parsing SCA data: {str(e)}")
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _process_sast_vulnerability(conviso_api, asset_id, result, vuln_data):
|
|
269
|
+
try:
|
|
270
|
+
create_sast_vulnerabilities(
|
|
271
|
+
conviso_api, asset_id, vuln_data['title'],
|
|
272
|
+
vuln_data['references'], vuln_data['description'],
|
|
273
|
+
vuln_data['vulnerable_line'], vuln_data['severity'],
|
|
274
|
+
vuln_data['file_name'], vuln_data['code_snippet'],
|
|
275
|
+
vuln_data['first_line'], None
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
LOGGER.error(f"❌ Error processing SAST vulnerability: {str(e)}")
|
|
282
|
+
return False
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def create_sast_vulnerabilities(conviso_api, asset_id, *args):
|
|
286
|
+
title, references, description, vulnerable_line, severity, file_name, code_snippet, first_line, cve = args
|
|
287
|
+
|
|
288
|
+
issue_model = CreateSastFindingInput(
|
|
289
|
+
asset_id=asset_id,
|
|
290
|
+
file_name=file_name,
|
|
291
|
+
vulnerable_line=vulnerable_line or 0,
|
|
292
|
+
title=title or 'No title provided',
|
|
293
|
+
description=description or 'No description provided',
|
|
294
|
+
severity=severity,
|
|
295
|
+
commit_ref=None,
|
|
296
|
+
deploy_id=None,
|
|
297
|
+
code_snippet=parse_code_snippet(code_snippet),
|
|
298
|
+
reference=parse_conviso_references(references),
|
|
299
|
+
first_line=first_line,
|
|
300
|
+
category=None,
|
|
301
|
+
original_issue_id_from_tool=None,
|
|
302
|
+
solution=None
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
try:
|
|
306
|
+
conviso_api.issues.create_sast(issue_model)
|
|
307
|
+
LOGGER.debug(f"✅ SAST vulnerability created: {title}")
|
|
308
|
+
except ResponseError as error:
|
|
309
|
+
if error.code == 'RECORD_NOT_UNIQUE':
|
|
310
|
+
LOGGER.debug(f"ℹ️ SAST vulnerability already exists: {title}")
|
|
311
|
+
else:
|
|
312
|
+
LOGGER.error(f"❌ Error creating SAST vulnerability: {error}")
|
|
313
|
+
raise
|
|
314
|
+
except Exception as e:
|
|
315
|
+
LOGGER.error(f"❌ Unexpected error creating SAST vulnerability: {str(e)}")
|
|
316
|
+
raise
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def create_sca_vulnerabilities(conviso_api, asset_id, *args):
|
|
320
|
+
title, references, description, severity, file_name, first_line, package, version, cve = args
|
|
321
|
+
|
|
322
|
+
issue_model = CreateScaFindingInput(
|
|
323
|
+
asset_id=asset_id,
|
|
324
|
+
title=title,
|
|
325
|
+
description=description,
|
|
326
|
+
severity=severity,
|
|
327
|
+
solution="Update to the last package version.",
|
|
328
|
+
reference=references,
|
|
329
|
+
file_name=file_name,
|
|
330
|
+
affected_version=version,
|
|
331
|
+
package=package,
|
|
332
|
+
cve=cve,
|
|
333
|
+
patched_version='',
|
|
334
|
+
category='',
|
|
335
|
+
original_issue_id_from_tool=''
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
try:
|
|
339
|
+
conviso_api.issues.create_sca(issue_model)
|
|
340
|
+
LOGGER.debug(f"✅ SCA vulnerability created: {title}")
|
|
341
|
+
except ResponseError as error:
|
|
342
|
+
if error.code == 'RECORD_NOT_UNIQUE':
|
|
343
|
+
LOGGER.debug(f"ℹ️ SCA vulnerability already exists: {title}")
|
|
344
|
+
else:
|
|
345
|
+
LOGGER.error(f"❌ Error creating SCA vulnerability: {error}")
|
|
346
|
+
raise
|
|
347
|
+
except Exception as e:
|
|
348
|
+
LOGGER.error(f"❌ Unexpected error creating SCA vulnerability: {str(e)}")
|
|
349
|
+
raise
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def parse_code_snippet(code_snippet):
|
|
353
|
+
try:
|
|
354
|
+
decoded_text = b64decode(code_snippet).decode("utf-8")
|
|
355
|
+
lines = decoded_text.split("\n")
|
|
356
|
+
cleaned_lines = []
|
|
357
|
+
|
|
358
|
+
for line in lines:
|
|
359
|
+
cleaned_line = line.split(": ", 1)[-1]
|
|
360
|
+
cleaned_lines.append(cleaned_line)
|
|
361
|
+
|
|
362
|
+
code_snippet = "\n".join(cleaned_lines)
|
|
363
|
+
|
|
364
|
+
return code_snippet
|
|
365
|
+
except Exception:
|
|
366
|
+
return code_snippet
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def parse_conviso_references(references=[]):
|
|
370
|
+
if not references:
|
|
371
|
+
return ""
|
|
372
|
+
|
|
373
|
+
DIVIDER = "\n"
|
|
374
|
+
|
|
375
|
+
references_to_join = []
|
|
376
|
+
|
|
377
|
+
for reference in references:
|
|
378
|
+
if reference:
|
|
379
|
+
references_to_join.append(reference)
|
|
380
|
+
|
|
381
|
+
return DIVIDER.join(references_to_join)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def parse_first_line_number(encoded_base64):
|
|
385
|
+
decoded_text = b64decode(encoded_base64).decode("utf-8")
|
|
386
|
+
|
|
387
|
+
regex = r"^(\d+):"
|
|
388
|
+
|
|
389
|
+
result = regex_search(regex, decoded_text)
|
|
390
|
+
|
|
391
|
+
if result and result.group(1):
|
|
392
|
+
return result.group(1)
|
|
393
|
+
|
|
394
|
+
LINE_NUMBER_WHEN_NOT_FOUND = 1
|
|
395
|
+
return LINE_NUMBER_WHEN_NOT_FOUND
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def clean_file(input_file):
|
|
399
|
+
with open(input_file, mode="rb") as file:
|
|
400
|
+
content = file.read()
|
|
401
|
+
|
|
402
|
+
if content.startswith(b'\xef\xbb\xbf'):
|
|
403
|
+
content = content[3:]
|
|
404
|
+
|
|
405
|
+
cleaned_file = input_file + ".cleaned"
|
|
406
|
+
with open(cleaned_file, mode="wb") as file:
|
|
407
|
+
file.write(content)
|
|
408
|
+
|
|
409
|
+
return cleaned_file
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
import_sarif.epilog = '''
|
|
413
|
+
'''
|
|
414
|
+
EPILOG = '''
|
|
415
|
+
Examples:
|
|
416
|
+
|
|
417
|
+
\b
|
|
418
|
+
1 - Import results on SARIF file to Conviso Platform:
|
|
419
|
+
$ export CONVISO_API_KEY='your-api-key'
|
|
420
|
+
$ {command} --input-file /path/to/file.sarif
|
|
421
|
+
|
|
422
|
+
''' # noqa: E501
|
|
423
|
+
|
|
424
|
+
SHORT_HELP = "Perform import of vulnerabilities from SARIF file to Conviso Platform"
|
|
425
|
+
|
|
426
|
+
command = 'conviso findings import-sarif'
|
|
427
|
+
import_sarif.short_help = SHORT_HELP
|
|
428
|
+
import_sarif.epilog = EPILOG.format(
|
|
429
|
+
command=command,
|
|
430
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def print_help(ctx, param, value):
|
|
5
|
+
if not value or ctx.resilient_parsing:
|
|
6
|
+
return
|
|
7
|
+
click.echo(ctx.get_help())
|
|
8
|
+
ctx.exit()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
help_option = click.option(
|
|
12
|
+
'-h', '--help',
|
|
13
|
+
is_flag=True,
|
|
14
|
+
callback=print_help,
|
|
15
|
+
expose_value=False,
|
|
16
|
+
is_eager=True,
|
|
17
|
+
help="Show this message and exit."
|
|
18
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from convisoappsec.flowcli import help_option
|
|
4
|
+
from .run import run
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.group()
|
|
8
|
+
@help_option
|
|
9
|
+
def iac():
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
iac.add_command(run)
|
|
14
|
+
|
|
15
|
+
iac.epilog = '''
|
|
16
|
+
Run flow iac COMMAND --help for more information on a command.
|
|
17
|
+
'''
|