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.
Files changed (128) hide show
  1. conviso_ast-3.0.0.data/scripts/flow_bash_completer.sh +21 -0
  2. conviso_ast-3.0.0.data/scripts/flow_fish_completer.fish +1 -0
  3. conviso_ast-3.0.0.data/scripts/flow_zsh_completer.sh +32 -0
  4. conviso_ast-3.0.0.dist-info/METADATA +37 -0
  5. conviso_ast-3.0.0.dist-info/RECORD +128 -0
  6. conviso_ast-3.0.0.dist-info/WHEEL +5 -0
  7. conviso_ast-3.0.0.dist-info/entry_points.txt +3 -0
  8. conviso_ast-3.0.0.dist-info/top_level.txt +1 -0
  9. convisoappsec/__init__.py +0 -0
  10. convisoappsec/common/__init__.py +5 -0
  11. convisoappsec/common/box.py +251 -0
  12. convisoappsec/common/cleaner.py +78 -0
  13. convisoappsec/common/docker.py +399 -0
  14. convisoappsec/common/exceptions.py +8 -0
  15. convisoappsec/common/git_data_parser.py +76 -0
  16. convisoappsec/common/graphql/__init__.py +0 -0
  17. convisoappsec/common/graphql/error_handlers.py +75 -0
  18. convisoappsec/common/graphql/errors.py +16 -0
  19. convisoappsec/common/graphql/low_client.py +51 -0
  20. convisoappsec/common/retry_handler.py +40 -0
  21. convisoappsec/common/strings.py +8 -0
  22. convisoappsec/flow/__init__.py +3 -0
  23. convisoappsec/flow/api.py +104 -0
  24. convisoappsec/flow/cleaner.py +118 -0
  25. convisoappsec/flow/graphql_api/__init__.py +0 -0
  26. convisoappsec/flow/graphql_api/beta/__init__.py +0 -0
  27. convisoappsec/flow/graphql_api/beta/client.py +18 -0
  28. convisoappsec/flow/graphql_api/beta/models/__init__.py +0 -0
  29. convisoappsec/flow/graphql_api/beta/models/issues/__init__.py +0 -0
  30. convisoappsec/flow/graphql_api/beta/models/issues/container.py +72 -0
  31. convisoappsec/flow/graphql_api/beta/models/issues/iac.py +6 -0
  32. convisoappsec/flow/graphql_api/beta/models/issues/normalize.py +13 -0
  33. convisoappsec/flow/graphql_api/beta/models/issues/sast.py +53 -0
  34. convisoappsec/flow/graphql_api/beta/models/issues/sca.py +78 -0
  35. convisoappsec/flow/graphql_api/beta/resources_api.py +142 -0
  36. convisoappsec/flow/graphql_api/beta/schemas/__init__.py +0 -0
  37. convisoappsec/flow/graphql_api/beta/schemas/mutations/__init__.py +61 -0
  38. convisoappsec/flow/graphql_api/beta/schemas/resolvers/__init__.py +0 -0
  39. convisoappsec/flow/graphql_api/v1/__init__.py +0 -0
  40. convisoappsec/flow/graphql_api/v1/client.py +46 -0
  41. convisoappsec/flow/graphql_api/v1/models/__init__.py +0 -0
  42. convisoappsec/flow/graphql_api/v1/models/asset.py +14 -0
  43. convisoappsec/flow/graphql_api/v1/models/issues.py +16 -0
  44. convisoappsec/flow/graphql_api/v1/models/project.py +35 -0
  45. convisoappsec/flow/graphql_api/v1/resources_api.py +489 -0
  46. convisoappsec/flow/graphql_api/v1/schemas/__init__.py +0 -0
  47. convisoappsec/flow/graphql_api/v1/schemas/mutations/__init__.py +212 -0
  48. convisoappsec/flow/graphql_api/v1/schemas/resolvers/__init__.py +180 -0
  49. convisoappsec/flow/source_code_scanner/__init__.py +9 -0
  50. convisoappsec/flow/source_code_scanner/exceptions.py +2 -0
  51. convisoappsec/flow/source_code_scanner/scc.py +68 -0
  52. convisoappsec/flow/source_code_scanner/source_code_scanner.py +177 -0
  53. convisoappsec/flow/util/__init__.py +7 -0
  54. convisoappsec/flow/util/ci_provider.py +99 -0
  55. convisoappsec/flow/util/metrics.py +16 -0
  56. convisoappsec/flow/util/source_code_compressor.py +22 -0
  57. convisoappsec/flow/version_control_system_adapter.py +528 -0
  58. convisoappsec/flow/version_searchers/__init__.py +9 -0
  59. convisoappsec/flow/version_searchers/sorted_by_versioning_style.py +85 -0
  60. convisoappsec/flow/version_searchers/timebased_version_seacher.py +39 -0
  61. convisoappsec/flow/version_searchers/version_searcher_result.py +33 -0
  62. convisoappsec/flow/versioning_style/__init__.py +0 -0
  63. convisoappsec/flow/versioning_style/semantic_versioning.py +44 -0
  64. convisoappsec/flowcli/__init__.py +3 -0
  65. convisoappsec/flowcli/__main__.py +4 -0
  66. convisoappsec/flowcli/assets/__init__.py +4 -0
  67. convisoappsec/flowcli/assets/create.py +88 -0
  68. convisoappsec/flowcli/assets/entrypoint.py +20 -0
  69. convisoappsec/flowcli/assets/ls.py +63 -0
  70. convisoappsec/flowcli/ast/__init__.py +3 -0
  71. convisoappsec/flowcli/ast/entrypoint.py +427 -0
  72. convisoappsec/flowcli/common.py +175 -0
  73. convisoappsec/flowcli/companies/__init__.py +0 -0
  74. convisoappsec/flowcli/companies/ls.py +25 -0
  75. convisoappsec/flowcli/container/__init__.py +3 -0
  76. convisoappsec/flowcli/container/entrypoint.py +17 -0
  77. convisoappsec/flowcli/container/run.py +306 -0
  78. convisoappsec/flowcli/context.py +49 -0
  79. convisoappsec/flowcli/deploy/__init__.py +0 -0
  80. convisoappsec/flowcli/deploy/create/__init__.py +4 -0
  81. convisoappsec/flowcli/deploy/create/context.py +12 -0
  82. convisoappsec/flowcli/deploy/create/entrypoint.py +31 -0
  83. convisoappsec/flowcli/deploy/create/with_/__init__.py +3 -0
  84. convisoappsec/flowcli/deploy/create/with_/entrypoint.py +20 -0
  85. convisoappsec/flowcli/deploy/create/with_/tag_tracker/__init__.py +4 -0
  86. convisoappsec/flowcli/deploy/create/with_/tag_tracker/context.py +11 -0
  87. convisoappsec/flowcli/deploy/create/with_/tag_tracker/entrypoint.py +30 -0
  88. convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/__init__.py +4 -0
  89. convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/entrypoint.py +21 -0
  90. convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/time_.py +84 -0
  91. convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/versioning_style.py +115 -0
  92. convisoappsec/flowcli/deploy/create/with_/values.py +133 -0
  93. convisoappsec/flowcli/entrypoint.py +103 -0
  94. convisoappsec/flowcli/environment_checker.py +45 -0
  95. convisoappsec/flowcli/findings/__init__.py +4 -0
  96. convisoappsec/flowcli/findings/create/__init__.py +4 -0
  97. convisoappsec/flowcli/findings/create/entrypoint.py +18 -0
  98. convisoappsec/flowcli/findings/create/with_/__init__.py +3 -0
  99. convisoappsec/flowcli/findings/create/with_/entrypoint.py +19 -0
  100. convisoappsec/flowcli/findings/create/with_/version_tracker.py +93 -0
  101. convisoappsec/flowcli/findings/entrypoint.py +19 -0
  102. convisoappsec/flowcli/findings/import_sarif/__init__.py +4 -0
  103. convisoappsec/flowcli/findings/import_sarif/entrypoint.py +430 -0
  104. convisoappsec/flowcli/help_option.py +18 -0
  105. convisoappsec/flowcli/iac/__init__.py +3 -0
  106. convisoappsec/flowcli/iac/entrypoint.py +17 -0
  107. convisoappsec/flowcli/iac/run.py +328 -0
  108. convisoappsec/flowcli/requirements_verifier.py +132 -0
  109. convisoappsec/flowcli/sast/__init__.py +3 -0
  110. convisoappsec/flowcli/sast/entrypoint.py +17 -0
  111. convisoappsec/flowcli/sast/run.py +485 -0
  112. convisoappsec/flowcli/sbom/__init__.py +3 -0
  113. convisoappsec/flowcli/sbom/entrypoint.py +17 -0
  114. convisoappsec/flowcli/sbom/generate.py +235 -0
  115. convisoappsec/flowcli/sca/__init__.py +3 -0
  116. convisoappsec/flowcli/sca/entrypoint.py +17 -0
  117. convisoappsec/flowcli/sca/run.py +479 -0
  118. convisoappsec/flowcli/vulnerability/__init__.py +3 -0
  119. convisoappsec/flowcli/vulnerability/assert_security_rules.py +201 -0
  120. convisoappsec/flowcli/vulnerability/container_vulnerability_manager.py +175 -0
  121. convisoappsec/flowcli/vulnerability/entrypoint.py +18 -0
  122. convisoappsec/flowcli/vulnerability/rules_schema.json +53 -0
  123. convisoappsec/flowcli/vulnerability/run.py +487 -0
  124. convisoappsec/logger.py +29 -0
  125. convisoappsec/sast/__init__.py +0 -0
  126. convisoappsec/sast/decision.py +45 -0
  127. convisoappsec/sast/sastbox.py +296 -0
  128. convisoappsec/version.py +1 -0
@@ -0,0 +1,485 @@
1
+ import sys
2
+ import click
3
+ import traceback
4
+ import time
5
+ import json
6
+ from copy import deepcopy as clone
7
+ from base64 import b64decode
8
+ from re import search as regex_search
9
+ from convisoappsec.flow import GitAdapter
10
+ from convisoappsec.flowcli import help_option
11
+ from convisoappsec.flowcli.common import (asset_id_option, on_http_error)
12
+ from convisoappsec.flowcli.context import pass_flow_context
13
+ from convisoappsec.sast.decision import Decision, Severity
14
+ from convisoappsec.sast.sastbox import SASTBox
15
+ from convisoappsec.flowcli.requirements_verifier import RequirementsVerifier
16
+ from docker.errors import APIError
17
+ from convisoappsec.logger import LOGGER
18
+ from convisoappsec.common.cleaner import Cleaner
19
+
20
+
21
+ def log_func(msg, new_line=True):
22
+ click.echo(msg, nl=new_line, err=True)
23
+
24
+
25
+ def find_blocked_issues(
26
+ results_filepaths, overall_threshold, severity_threshold, severity
27
+ ):
28
+ blocked_issues = []
29
+
30
+ for result_path in results_filepaths:
31
+ report_decision = Decision(result_path)
32
+
33
+ overall_policy = report_decision.block_from_findings(overall_threshold)
34
+ severity_policy = report_decision.block_from_severity(
35
+ severity, severity_threshold
36
+ )
37
+
38
+ if overall_policy or severity_policy:
39
+ blocked_issues.append(report_decision)
40
+ click.echo(
41
+ "Failing execution due one or more blocking flags", file=sys.stderr
42
+ )
43
+
44
+ click.echo(result_path)
45
+
46
+ return blocked_issues
47
+
48
+
49
+ def print_blocked_issues(blocked_issues):
50
+ for issue_decision in blocked_issues:
51
+ issues = issue_decision.filtered_issues()
52
+
53
+ for issue in issues:
54
+ click.echo(
55
+ "{issue_name}\n{filepath} at line {line_index}\n".format(
56
+ issue_name=issue["title"],
57
+ filepath=issue["filename"],
58
+ line_index=issue["line"],
59
+ )
60
+ )
61
+ pass
62
+
63
+
64
+ def perform_sastbox_scan(
65
+ conviso_rest_api, sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login, repository_dir, end_commit, start_commit, logger
66
+ ):
67
+
68
+ max_retries = 5
69
+ retries = 0
70
+ sastbox = SASTBox(registry=sastbox_registry, repository_name=sastbox_repository_name, tag=sastbox_tag)
71
+ pull_progress_bar = click.progressbar(length=sastbox.size, label="Performing SAST download...")
72
+
73
+ while retries < max_retries:
74
+ try:
75
+ if not sastbox_skip_login:
76
+ logger("Checking SASTBox authorization...")
77
+ token = conviso_rest_api.docker_registry.get_sast_token()
78
+ sastbox.login(token)
79
+
80
+ with pull_progress_bar as progressbar:
81
+ for downloaded_chunk in sastbox.pull():
82
+ progressbar.update(downloaded_chunk)
83
+ break
84
+ except APIError as e:
85
+ retries += 1
86
+ logger(f"Retrying {retries}/{max_retries}...")
87
+ time.sleep(1)
88
+
89
+ if retries == max_retries:
90
+ logger("Max retries reached. Failed to perform SAST download.")
91
+ raise Exception(f"Max retries reached. Could not complete the SAST download. Error: {str(e)}")
92
+
93
+ logger("Starting SAST scan diff...")
94
+
95
+ reports = sastbox.run_scan_diff(repository_dir, end_commit, start_commit, log=logger)
96
+
97
+ logger("SAST scan diff done.")
98
+
99
+ results_filepaths = []
100
+ for r in reports:
101
+ try:
102
+ file_path = str(r)
103
+ results_filepaths.append(file_path)
104
+ except Exception as e:
105
+ click.echo(f"Error decoding file path: {r} with error {e}.", file=sys.stderr)
106
+
107
+ return results_filepaths
108
+
109
+
110
+ def parse_conviso_references(references=[]):
111
+ divider = "\n"
112
+
113
+ references_to_join = []
114
+
115
+ for reference in references:
116
+ if reference:
117
+ references_to_join.append(reference)
118
+
119
+ return divider.join(references_to_join)
120
+
121
+
122
+ def parse_code_snippet(encoded_base64):
123
+ try:
124
+ decoded_text = b64decode(encoded_base64).decode("utf-8")
125
+ except UnicodeDecodeError:
126
+ try:
127
+ decoded_text = b64decode(encoded_base64, validate=False).decode("latin-1")
128
+ except Exception as e:
129
+ print("Error handling decoding error:", e, file=sys.stderr)
130
+ decoded_text = ''
131
+
132
+ lines = decoded_text.split("\n")
133
+
134
+ cleaned_lines = []
135
+ for line in lines:
136
+ cleaned_line = line.split(": ", 1)[-1]
137
+ cleaned_lines.append(cleaned_line)
138
+
139
+ code_snippet = "\n".join(cleaned_lines)
140
+
141
+ return code_snippet
142
+
143
+
144
+ def parse_first_line_number(encoded_base64):
145
+ try:
146
+ decoded_text = b64decode(encoded_base64).decode("utf-8")
147
+ except UnicodeDecodeError:
148
+ try:
149
+ decoded_text = b64decode(encoded_base64, validate=False).decode("latin-1")
150
+ except Exception as e:
151
+ print("Error handling decoding error:", e, file=sys.stderr)
152
+ decoded_text = ''
153
+
154
+ regex = r"^(\d+):"
155
+
156
+ result = regex_search(regex, decoded_text)
157
+
158
+ if result and result.group(1):
159
+ return result.group(1)
160
+
161
+ line_number_when_not_found = 1
162
+ return line_number_when_not_found
163
+
164
+
165
+ def deploy_results_to_conviso_beta(
166
+ flow_context, results_filepaths, asset_id, company_id, commit_ref=None, deploy_id=None, control_sync_status_id=None
167
+ ):
168
+ """Send SAST results to the Conviso platform."""
169
+
170
+ results_context = click.progressbar(
171
+ results_filepaths, label="🔧 Sending SAST report to the Conviso Platform ..."
172
+ )
173
+
174
+ with results_context as reports:
175
+ for report_path in reports:
176
+ api_key = flow_context.key
177
+ max_retries = 5
178
+ conviso_api = flow_context.create_conviso_graphql_client()
179
+
180
+ for attempt in range(1, max_retries + 1):
181
+ try:
182
+ conviso_api.issues.send_issues_file(
183
+ company_id=company_id, asset_id=asset_id, file_path=report_path, api_key=api_key,
184
+ vulnerability_type='SAST_FINDING',
185
+ deploy_id=deploy_id, commit_ref=commit_ref, control_sync_status_id=control_sync_status_id
186
+ )
187
+ break
188
+ except Exception as e:
189
+ if attempt == max_retries:
190
+ print(f"Failed after {max_retries} attempts: {e}")
191
+ else:
192
+ wait_time = 2 ** attempt # Exponential backoff (2s, 4s, 8s, 16s)
193
+ print(f"Retry {attempt}/{max_retries} failed. Retrying in {wait_time} seconds...")
194
+ time.sleep(wait_time)
195
+
196
+
197
+ @click.command()
198
+ @asset_id_option(required=False)
199
+ @click.option(
200
+ "-s",
201
+ "--start-commit",
202
+ required=False,
203
+ help="If no value is set so the empty tree hash commit is used.",
204
+ )
205
+ @click.option(
206
+ "-e",
207
+ "--end-commit",
208
+ required=False,
209
+ help="""If no value is set so the HEAD commit
210
+ from the current branch is used""",
211
+ )
212
+ @click.option(
213
+ "-r",
214
+ "--repository-dir",
215
+ default=".",
216
+ show_default=True,
217
+ type=click.Path(
218
+ exists=True,
219
+ resolve_path=True,
220
+ ),
221
+ required=False,
222
+ help="The source code repository directory.",
223
+ )
224
+ @click.option(
225
+ "--fail-on-severity-threshold",
226
+ required=False,
227
+ help="If the threshold of the informed severity and higher has reach, then the command will fail after send the results to AppSec Flow.\n \
228
+ The severity levels are: UNDEFINED, INFO, LOW, MEDIUM, HIGH, CRITICAL.",
229
+ type=click.Tuple([str, int]),
230
+ default=(None, None),
231
+ )
232
+ @click.option(
233
+ "--fail-on-threshold",
234
+ required=False,
235
+ help="If the threshold has reach then the command will fail after send the result to AppSec Flow",
236
+ type=int,
237
+ default=False,
238
+ )
239
+ @click.option(
240
+ "--send-to-flow/--no-send-to-flow",
241
+ default=True,
242
+ show_default=True,
243
+ required=False,
244
+ hidden=True,
245
+ help="""Enable or disable the ability of send analysis result
246
+ reports to flow.""",
247
+ )
248
+ @click.option(
249
+ "--deploy-id",
250
+ default=None,
251
+ required=False,
252
+ hidden=True,
253
+ envvar=("CONVISO_DEPLOY_ID", "FLOW_DEPLOY_ID")
254
+ )
255
+ @click.option(
256
+ "--sastbox-registry",
257
+ default="",
258
+ required=False,
259
+ hidden=True,
260
+ envvar=("CONVISO_SASTBOX_REGISTRY", "FLOW_SASTBOX_REGISTRY"),
261
+ )
262
+ @click.option(
263
+ "--sastbox-repository-name",
264
+ default="",
265
+ required=False,
266
+ hidden=True,
267
+ envvar=("CONVISO_SASTBOX_REPOSITORY_NAME", "FLOW_SASTBOX_REPOSITORY_NAME"),
268
+ )
269
+ @click.option(
270
+ "--sastbox-tag",
271
+ default=SASTBox.DEFAULT_TAG,
272
+ required=False,
273
+ hidden=True,
274
+ envvar=("CONVISO_SASTBOX_TAG", "FLOW_SASTBOX_TAG"),
275
+ )
276
+ @click.option(
277
+ "--sastbox-skip-login/--sastbox-no-skip-login",
278
+ default=False,
279
+ required=False,
280
+ hidden=True,
281
+ envvar=("CONVISO_SASTBOX_SKIP_LOGIN", "FLOW_SASTBOX_SKIP_LOGIN"),
282
+ )
283
+ @click.option(
284
+ '--experimental',
285
+ default=False,
286
+ is_flag=True,
287
+ hidden=True,
288
+ help="Enable experimental features.",
289
+ )
290
+ @click.option(
291
+ "--company-id",
292
+ required=False,
293
+ envvar=("CONVISO_COMPANY_ID", "FLOW_COMPANY_ID"),
294
+ help="Company ID on Conviso Platform",
295
+ )
296
+ @click.option(
297
+ '--asset-name',
298
+ required=False,
299
+ envvar=("CONVISO_ASSET_NAME", "FLOW_ASSET_NAME"),
300
+ help="Provides a asset name.",
301
+ )
302
+ @click.option(
303
+ '--vulnerability-auto-close',
304
+ default=False,
305
+ is_flag=True,
306
+ hidden=True,
307
+ help="Enable auto fixing vulnerabilities on cp.",
308
+ )
309
+ @click.option(
310
+ '--from-ast',
311
+ default=False,
312
+ is_flag=True,
313
+ hidden=True,
314
+ help="Internal use only.",
315
+ )
316
+ @click.option(
317
+ '--cleanup',
318
+ default=False,
319
+ is_flag=True,
320
+ show_default=True,
321
+ help="Clean up system resources, including temporary files, stopped containers, unused Docker images and volumes.",
322
+ )
323
+ @click.option(
324
+ '--control-sync-status-id',
325
+ required=False,
326
+ hidden=True,
327
+ help="Control sync status id.",
328
+ )
329
+ @help_option
330
+ @pass_flow_context
331
+ @click.pass_context
332
+ def run(context, flow_context, asset_id, company_id, end_commit, start_commit, repository_dir, send_to_flow, deploy_id,
333
+ sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login, fail_on_threshold, fail_on_severity_threshold,
334
+ experimental, asset_name, vulnerability_auto_close, from_ast, cleanup, control_sync_status_id):
335
+ """
336
+ This command will perform SAST analysis at the source code. The analysis
337
+ results can be reported or not to flow application. The analysis can be
338
+ applied to specific commit range.
339
+
340
+ This command will write the analysis reports files paths to stdout.
341
+ """
342
+ if not from_ast:
343
+ prepared_context = RequirementsVerifier.prepare_context(clone(context))
344
+
345
+ params_to_copy = [
346
+ 'asset_id', 'company_id', 'start_commit', 'end_commit',
347
+ 'repository_dir', 'send_to_flow', 'deploy_id', 'sastbox_registry',
348
+ 'sastbox_repository_name', 'sastbox_tag', 'sastbox_skip_login',
349
+ 'experimental', 'asset_name', 'vulnerability_auto_close', 'company_id',
350
+ 'cleanup'
351
+ ]
352
+
353
+ for param_name in params_to_copy:
354
+ context.params[param_name] = (
355
+ locals()[param_name] or prepared_context.params[param_name]
356
+ )
357
+
358
+ perform_command(context, flow_context, context.params['asset_id'], context.params['deploy_id'], context.params['end_commit'],
359
+ context.params['start_commit'], context.params['repository_dir'], context.params['send_to_flow'], context.params['sastbox_registry'],
360
+ context.params['sastbox_repository_name'], context.params['sastbox_tag'], context.params['sastbox_skip_login'],
361
+ context.params['fail_on_threshold'], context.params['fail_on_severity_threshold'], context.params['experimental'],
362
+ context.params['company_id'], context.params['cleanup'], from_ast, control_sync_status_id)
363
+
364
+ def perform_command(context, flow_context, asset_id, deploy_id, end_commit, start_commit, repository_dir, send_to_flow, sastbox_registry, sastbox_repository_name, sastbox_tag,
365
+ sastbox_skip_login, fail_on_threshold, fail_on_severity_threshold, experimental, company_id, cleanup, from_ast, control_sync_status_id):
366
+
367
+ if send_to_flow and experimental and not asset_id:
368
+ raise click.MissingParameter(
369
+ "It is required when sending reports to Conviso Platform using experimental API.",
370
+ param_type="option",
371
+ param_hint="--asset-id",
372
+ )
373
+
374
+ severity, severity_threshold = fail_on_severity_threshold
375
+ overall_threshold = fail_on_threshold
376
+
377
+ if severity and not Severity.has_value(severity):
378
+ raise click.BadParameter(
379
+ "{} is not a valid Severity. Use a valid Severity value:\n{}".format(
380
+ severity, [severity.name for severity in Severity]
381
+ ),
382
+ param_hint="--fail-on-severity-threshold",
383
+ )
384
+
385
+ try:
386
+ git_adapter = GitAdapter(repository_dir)
387
+
388
+ end_commit = end_commit or git_adapter.head_commit
389
+
390
+ start_commit = start_commit or git_adapter.empty_repository_tree_commit
391
+
392
+ if start_commit == end_commit:
393
+ click.echo(
394
+ "Previous commit ({0}) and current commit ({1}) are the same; nothing to do.".format(
395
+ start_commit, end_commit
396
+ ),
397
+ file=sys.stderr,
398
+ )
399
+ return
400
+
401
+ conviso_rest_api = flow_context.create_conviso_rest_api_client()
402
+
403
+ results_filepaths = perform_sastbox_scan(
404
+ conviso_rest_api, sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login, repository_dir, end_commit, start_commit, log_func,
405
+ )
406
+
407
+ # this set total sast vulnerability found in sast scan to send to control sync status
408
+ context.params['sast_vulnerability_count'] = total_vulnerability_count(results_filepaths[0])
409
+
410
+ if send_to_flow:
411
+ deploy_results_to_conviso_beta(
412
+ flow_context, results_filepaths, asset_id, company_id, commit_ref=end_commit, deploy_id=deploy_id, control_sync_status_id=control_sync_status_id
413
+ )
414
+
415
+ blocked_issues = find_blocked_issues(
416
+ results_filepaths, overall_threshold, severity_threshold, severity
417
+ )
418
+
419
+ if blocked_issues:
420
+ print_blocked_issues(blocked_issues)
421
+ sys.exit(1)
422
+
423
+ if cleanup and from_ast == False:
424
+ LOGGER.info("🧹 Cleaning up ...")
425
+ cleaner = Cleaner()
426
+ cleaner.cleanup()
427
+
428
+ except Exception as e:
429
+ traceback.print_exc()
430
+ on_http_error(e)
431
+ raise click.ClickException(str(e)) from e
432
+
433
+ def total_vulnerability_count(file_path: str) -> int:
434
+ """
435
+ Extract the total vulnerability count from a SAST scan result file.
436
+
437
+ Args:
438
+ file_path (str): Path to JSON result file containing vulnerability scan results.
439
+ The file should have a 'summary' section with
440
+ 'issues_count.total' field.
441
+
442
+ Returns:
443
+ int: Total number of vulnerabilities found in the scan result file.
444
+ Returns 0 if the file doesn't exist or no vulnerabilities are found.
445
+ """
446
+
447
+ try:
448
+ with open(file_path, 'r', encoding='utf-8') as f:
449
+ data = json.load(f)
450
+ return data['summary']['issues_count']['total']
451
+ except (FileNotFoundError, KeyError, json.JSONDecodeError):
452
+ return 0
453
+
454
+ EPILOG = """
455
+ Examples:
456
+
457
+ \b
458
+ 1 - Reporting the results to flow api:
459
+ 1.1 - Running an analysis at all commit range:
460
+ $ export CONVISO_API_KEY='your-api-key'
461
+ $ {command}
462
+
463
+ \b
464
+ 1.2 - Running an analysis at specific commit range:
465
+ $ export CONVISO_API_KEY='your-api-key'
466
+ $ {command} --start-commit "$(git rev-parse HEAD~5)" --end-commit "$(git rev-parse HEAD)"
467
+
468
+ \b
469
+ 2 - Using flags to break the job on findings ocurrence:
470
+ 2.1 - Running an analysis and break the build if there is 10 findings or more:
471
+ $ export CONVISO_API_KEY='your-api-key'
472
+ $ {command} --fail-on-threshold 10
473
+ \b
474
+ 2.2 - Running an analysis and break the build if there is 5 findings with HIGH severity or higher
475
+ $ export CONVISO_API_KEY='your-api-key'
476
+ $ {command} --fail-on-severity-threshold HIGH 5
477
+ """ # noqa: E501
478
+
479
+ SHORT_HELP = "Perform SAST analysis"
480
+
481
+ command = "conviso sast run"
482
+ run.short_help = SHORT_HELP
483
+ run.epilog = EPILOG.format(
484
+ command=command,
485
+ )
@@ -0,0 +1,3 @@
1
+ from .entrypoint import sbom
2
+
3
+ __all__ = ['sbom']
@@ -0,0 +1,17 @@
1
+ import click
2
+
3
+ from convisoappsec.flowcli import help_option
4
+ from .generate import generate
5
+
6
+
7
+ @click.group()
8
+ @help_option
9
+ def sbom():
10
+ pass
11
+
12
+
13
+ sbom.add_command(generate)
14
+
15
+ sbom.epilog = '''
16
+ Run conviso sbom COMMAND --help for more information on a command.
17
+ '''