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,427 @@
1
+ import subprocess
2
+ import traceback
3
+ import click
4
+ from convisoappsec.flowcli import help_option
5
+ from convisoappsec.flowcli.common import DeployFormatter, PerformDeployException, asset_id_option
6
+ from convisoappsec.flowcli.deploy.create.context import pass_create_context
7
+ from convisoappsec.flowcli.deploy.create.with_.values import values
8
+ from convisoappsec.flowcli.requirements_verifier import RequirementsVerifier
9
+ from convisoappsec.flowcli.sast import sast
10
+ from convisoappsec.flowcli.sca import sca
11
+ from convisoappsec.flowcli.iac import iac
12
+ from convisoappsec.flowcli.vulnerability import vulnerability
13
+ from copy import deepcopy as clone
14
+ from convisoappsec.flow import GitAdapter
15
+ from convisoappsec.flowcli.context import pass_flow_context
16
+ from convisoappsec.logger import LOGGER, log_and_notify_ast_event
17
+ from convisoappsec.common.cleaner import Cleaner
18
+
19
+
20
+ def get_default_params_values(cmd_params):
21
+ """ Further information in https://click.palletsprojects.com/en/8.1.x/api/?highlight=params#click.Command.params
22
+
23
+ Args:
24
+ cmd_params (List[click.core.Parameter]):
25
+
26
+ Returns:
27
+ dict: default params values dictionarie
28
+ """
29
+ default_params = {}
30
+ for param in cmd_params:
31
+ unwanted = param.name in ['help', 'verbosity']
32
+ if not unwanted:
33
+ default_params.update({param.name: param.default})
34
+ return default_params
35
+
36
+
37
+ def parse_params(ctx_params: dict, expected_params: list):
38
+ """ Parse the params from the context extracting the expected params values to the context.
39
+
40
+ Args:
41
+ ctx_params (dict): context params: Further information at https://click.palletsprojects.com/en/8.1.x/api/?highlight=context%20param#click.Context.params
42
+ expected_params (list): Further information at https://click.palletsprojects.com/en/8.1.x/api/?highlight=params#click.Command.params
43
+
44
+ Returns:
45
+ dict: parsed_params: parsed params as key and value
46
+ """
47
+ parsed_params = get_default_params_values(expected_params)
48
+ for param in ctx_params:
49
+ if param in parsed_params:
50
+ parsed_params.update({param: ctx_params.get(param)})
51
+ return parsed_params
52
+
53
+
54
+ def perform_sast(context, control_sync_status_id) -> int:
55
+ """Setup and runs the "sast run" command.
56
+
57
+ Args:
58
+ context (<class 'click.core.Context'>): cloned context
59
+ control_sync_status_id (str): control sync status id
60
+ """
61
+ sast_run = sast.commands.get('run')
62
+
63
+ specific_params = {
64
+ "deploy_id": context.obj.deploy['deploy_id'],
65
+ "start_commit": context.obj.deploy['previous_commit'],
66
+ "end_commit": context.obj.deploy['current_commit'],
67
+ "control_sync_status_id": control_sync_status_id
68
+ }
69
+ context.params.update(specific_params)
70
+ context.params = parse_params(context.params, sast_run.params)
71
+ try:
72
+ LOGGER.info(
73
+ 'Running SAST on deploy ID "{deploy_id}"...'
74
+ .format(deploy_id=context.params["deploy_id"])
75
+ )
76
+ sast_run.invoke(context)
77
+
78
+ return context.params.get('sast_vulnerability_count', 0)
79
+
80
+ except Exception as err:
81
+ raise click.ClickException(str(err)) from err
82
+
83
+
84
+ def perform_sca(context, control_sync_status_id) -> int:
85
+ """Setup and runs the "sca run" command.
86
+
87
+ Args:
88
+ context (<class 'click.core.Context'>): cloned context
89
+ control_sync_status_id (str): control sync status id
90
+ """
91
+ sca_run = sca.commands.get('run')
92
+ context.params.update(
93
+ {"deploy_id": context.obj.deploy['deploy_id'],
94
+ "control_sync_status_id": control_sync_status_id}
95
+ )
96
+
97
+ context.params = parse_params(context.params, sca_run.params)
98
+ try:
99
+ LOGGER.info(
100
+ 'Running SCA on deploy ID "{deploy_id}"...'
101
+ .format(deploy_id=context.params["deploy_id"])
102
+ )
103
+ sca_run.invoke(context)
104
+
105
+ return context.params.get('sca_vulnerability_count', 0)
106
+
107
+ except Exception as err:
108
+ raise click.ClickException(str(err)) from err
109
+
110
+
111
+ def perform_iac(context, control_sync_status_id) -> None:
112
+ """Setup and runs the "iac run" command.
113
+
114
+ Args:
115
+ context (<class 'click.core.Context'>): clonned context
116
+ control_sync_status_id (str): control sync status id
117
+ """
118
+ iac_run = iac.commands.get('run')
119
+ context.params.update(
120
+ {"deploy_id": context.obj.deploy['deploy_id'],
121
+ "control_sync_status_id": control_sync_status_id}
122
+ )
123
+ context.params = parse_params(context.params, iac_run.params)
124
+
125
+ try:
126
+ LOGGER.info(
127
+ 'Running IAC on deploy ID "{deploy_id}"...'
128
+ .format(deploy_id=context.params["deploy_id"])
129
+ )
130
+ iac_run.invoke(context)
131
+ except Exception as err:
132
+ raise click.ClickException(str(err)) from err
133
+
134
+
135
+ def perform_vulnerabilities_service(context, company_id, control_sync_status_id) -> None:
136
+ auto_close_run = vulnerability.commands.get('run')
137
+
138
+ specific_params = {
139
+ "deploy_id": context.obj.deploy['deploy_id'],
140
+ "start_commit": context.obj.deploy['previous_commit'],
141
+ "end_commit": context.obj.deploy['current_commit'],
142
+ "company_id": context.params['company_id'] or company_id,
143
+ "control_sync_status_id": control_sync_status_id
144
+ }
145
+ context.params.update(specific_params)
146
+ context.params = parse_params(context.params, auto_close_run.params)
147
+
148
+ try:
149
+ LOGGER.info("[*] Verifying if any vulnerability was fixed...")
150
+ auto_close_run.invoke(context)
151
+ except Exception as err:
152
+ raise click.ClickException(str(err)) from err
153
+
154
+
155
+ def perform_deploy(context, flow_context, prepared_context, control_sync_status_id):
156
+ context.obj.output_formatter = DeployFormatter(format=DeployFormatter.DEFAULT)
157
+ context.params = parse_params(context.params, values.params)
158
+ repository_dir = context.params['repository_dir']
159
+ asset_id = prepared_context.params.get('asset_id')
160
+
161
+ if not asset_id:
162
+ raise PerformDeployException("Asset ID is required")
163
+
164
+ LOGGER.info("Creating new deploy...")
165
+ try:
166
+
167
+ created_deploy = values.invoke(context)
168
+
169
+ if created_deploy is None:
170
+ conviso_api = flow_context.create_conviso_graphql_client()
171
+ conviso_api.control_sync_status.increase_count(
172
+ control_sync_status_id=control_sync_status_id,
173
+ asset_id=prepared_context.params['asset_id'],
174
+ success_count=1 # increase by one just to be success
175
+ )
176
+ return None
177
+
178
+ conviso_api = flow_context.create_conviso_graphql_client()
179
+ api_key = flow_context.key
180
+ git_adapter = GitAdapter(repository_dir)
181
+
182
+ branch_name = get_branch_name(git_adapter, repository_dir)
183
+
184
+ LOGGER.info(f"Creating deploy: asset_id={asset_id}, "
185
+ f"previous={created_deploy['previous_commit']}, "
186
+ f"current={created_deploy['current_commit']}")
187
+
188
+ response = conviso_api.deploys.create_deploy(
189
+ asset_id=asset_id,
190
+ previous_commit=created_deploy['previous_commit'],
191
+ current_commit=created_deploy['current_commit'],
192
+ branch_name=branch_name,
193
+ api_key=api_key,
194
+ commit_history=created_deploy['commit_history']
195
+ )
196
+
197
+ response_deploy_id = response['createDeploy']['deploy']['id']
198
+ deploy_params = {
199
+ "deploy_id": response_deploy_id,
200
+ "current_commit": created_deploy['current_commit'],
201
+ "previous_commit": created_deploy['previous_commit'],
202
+ }
203
+ created_deploy.update(deploy_params)
204
+
205
+ return created_deploy
206
+
207
+ except Exception as err:
208
+ error_message = str(err)
209
+ error_text = "A deploy with the same previous and current commit already exists"
210
+ found = False
211
+
212
+ if error_text in error_message:
213
+ found = True
214
+ elif hasattr(err, 'errors') and isinstance(err.errors, list):
215
+ for nested_err in err.errors:
216
+ if isinstance(nested_err, dict) and error_text in nested_err.get('message', ''):
217
+ found = True
218
+ break
219
+ elif isinstance(nested_err, str) and error_text in nested_err:
220
+ found = True
221
+ break
222
+
223
+ elif error_text in repr(err):
224
+ found = True
225
+
226
+ if found:
227
+ LOGGER.warning("Deploy with same commits already exists")
228
+ return None
229
+ else:
230
+ raise PerformDeployException(f"Failed to create deploy: {error_message}") from err
231
+
232
+
233
+ def get_branch_name(git_adapter, repository_dir):
234
+ """Gets branch name"""
235
+ try:
236
+ return git_adapter.get_branch_name()
237
+ except Exception as e:
238
+ LOGGER.warning(f"HEAD is detached or error getting branch: {e}")
239
+ LOGGER.info("Looking for most recent branch...")
240
+
241
+ try:
242
+ result = subprocess.run(
243
+ ["git", "for-each-ref", "--sort=-creatordate",
244
+ "--format=%(refname:short)", "refs/heads/"],
245
+ cwd=repository_dir,
246
+ stdout=subprocess.PIPE,
247
+ stderr=subprocess.PIPE,
248
+ check=True,
249
+ timeout=10
250
+ )
251
+ branches = result.stdout.decode().strip().splitlines()
252
+ if branches:
253
+ branch_name = branches[0]
254
+ LOGGER.info(f"Using branch: {branch_name}")
255
+ return branch_name
256
+ else:
257
+ LOGGER.warning("No branches found")
258
+ return "unknown"
259
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired) as e:
260
+ LOGGER.error(f"Error executing git command: {e}")
261
+ return "unknown"
262
+
263
+
264
+ @click.command(
265
+ context_settings=dict(
266
+ allow_extra_args=True,
267
+ ignore_unknown_options=True
268
+ )
269
+ )
270
+ @asset_id_option(required=False)
271
+ @click.option(
272
+ "--send-to-flow/--no-send-to-flow",
273
+ default=True,
274
+ show_default=True,
275
+ required=False,
276
+ help="""Enable or disable the ability of send analysis result
277
+ reports to flow.""",
278
+ hidden=True
279
+ )
280
+ @click.option(
281
+ '-r',
282
+ '--repository-dir',
283
+ default=".",
284
+ show_default=True,
285
+ type=click.Path(exists=True, resolve_path=True),
286
+ required=False,
287
+ help="""The source code repository directory.""",
288
+ )
289
+ @click.option(
290
+ "-c",
291
+ "--current-commit",
292
+ required=False,
293
+ help="If no value is given the HEAD commit of branch is used. [DEPLOY]",
294
+ )
295
+ @click.option(
296
+ "-p",
297
+ "--previous-commit",
298
+ required=False,
299
+ help="""If no value is given, the value is retrieved from the lastest
300
+ deploy at flow application. [DEPLOY]""",
301
+ )
302
+ @click.option(
303
+ "--company-id",
304
+ required=False,
305
+ envvar=("CONVISO_COMPANY_ID", "FLOW_COMPANY_ID"),
306
+ help="Company ID on Conviso Platform",
307
+ )
308
+ @click.option(
309
+ '--asset-name',
310
+ required=False,
311
+ envvar=("CONVISO_ASSET_NAME", "FLOW_ASSET_NAME"),
312
+ help="Provides a asset name.",
313
+ )
314
+ @click.option(
315
+ '--vulnerability-auto-close',
316
+ default=False,
317
+ is_flag=True,
318
+ help="Enable auto fixing vulnerabilities on cp.",
319
+ )
320
+ @click.option(
321
+ '--cleanup',
322
+ default=False,
323
+ is_flag=True,
324
+ show_default=True,
325
+ help="Clean up system resources, including temporary files, stopped containers, unused Docker images and volumes.",
326
+ )
327
+ @help_option
328
+ @pass_flow_context
329
+ @pass_create_context
330
+ @click.pass_context
331
+ def run(context, create_context, flow_context, **kwargs):
332
+ """ AST - Application Security Testing. Unifies deploy issue, SAST and SCA analyses. """
333
+ increase_failure_count = 1
334
+
335
+ try:
336
+ prepared_context = RequirementsVerifier.prepare_context(clone(context), from_ast=True)
337
+
338
+
339
+ # After ensuring we have both asset and user permissions, AST is ready to start. We then create a control sync status
340
+ # on the Conviso platform, which will appear on the scans page and in the recent scans list.
341
+ conviso_api = flow_context.create_conviso_graphql_client()
342
+ control_sync_status = conviso_api.control_sync_status.create_control_sync_status(
343
+ asset_id=prepared_context.params['asset_id']
344
+ )
345
+ conviso_api.control_sync_status.update_control_sync_status(control_sync_status_id=control_sync_status['id'])
346
+
347
+ try:
348
+ prepared_context.obj.deploy = perform_deploy(
349
+ clone(prepared_context), flow_context, prepared_context, control_sync_status['id']
350
+ )
351
+
352
+ if prepared_context.obj.deploy is None:
353
+ return
354
+
355
+ total_sast = perform_sast(clone(prepared_context), control_sync_status_id=control_sync_status['id'])
356
+ total_sca = perform_sca(clone(prepared_context), control_sync_status_id=control_sync_status['id'])
357
+ perform_iac(clone(prepared_context), control_sync_status_id=control_sync_status['id'])
358
+
359
+ total_vulnerability_count = total_sast + total_sca
360
+
361
+ company_id = prepared_context.params['company_id']
362
+
363
+ if context.params['vulnerability_auto_close'] is True:
364
+ try:
365
+ perform_vulnerabilities_service(clone(prepared_context), company_id, control_sync_status['id'])
366
+ except Exception:
367
+ LOGGER.info("An issue occurred while attempting to fix vulnerabilities. Our technical team has been notified.")
368
+ full_trace = traceback.format_exc()
369
+ log_and_notify_ast_event(flow_context=flow_context, company_id=company_id,
370
+ asset_id=prepared_context.params['asset_id'], ast_log=full_trace)
371
+ return
372
+
373
+ if context.params.get('cleanup'):
374
+ try:
375
+ LOGGER.info("🧹 Cleaning up ...")
376
+ cleaner = Cleaner()
377
+ cleaner.cleanup()
378
+ except Exception as e:
379
+ LOGGER.info(f"An error occurred while cleaning up. Our technical team has been notified.")
380
+ full_trace = traceback.format_exc()
381
+ log_and_notify_ast_event(
382
+ flow_context=flow_context, company_id=company_id, asset_id=prepared_context.params['asset_id'],
383
+ ast_log=full_trace
384
+ )
385
+ return
386
+
387
+ conviso_api.control_sync_status.update_control_sync_status(
388
+ control_sync_status_id=control_sync_status['id'],
389
+ external_vulnerability_count=total_vulnerability_count
390
+ )
391
+
392
+ # increase success_count by one just to match external_vulnerability_count when ast runs successfully and if
393
+ # success count and external_vulnerability_count are equals, the scan status will be moved from pending to success.
394
+ conviso_api.control_sync_status.increase_count(
395
+ control_sync_status_id=control_sync_status['id'],
396
+ asset_id=prepared_context.params['asset_id'],
397
+ success_count=total_vulnerability_count
398
+ )
399
+
400
+ except Exception as err:
401
+ failure_details = f"{str(err)}, {traceback.format_exc()}"
402
+
403
+ conviso_api.control_sync_status.increase_count(
404
+ control_sync_status_id=control_sync_status['id'],
405
+ asset_id=prepared_context.params['asset_id'],
406
+ failure_count=increase_failure_count,
407
+ failure_reason=str(failure_details)
408
+ )
409
+ raise click.ClickException(str(err)) from err
410
+
411
+ except Exception as err:
412
+ error_message = str(err)
413
+
414
+ if "A deploy with the same previous and current commit already exists" in error_message:
415
+ LOGGER.warning("Deploy with same commits already exists")
416
+ return None
417
+ else:
418
+ LOGGER.error(f"AST initialization failed. Please contact support with the following error: {err}")
419
+ raise click.ClickException(str(err)) from err
420
+
421
+
422
+ @click.group()
423
+ def ast():
424
+ pass
425
+
426
+
427
+ ast.add_command(run)
@@ -0,0 +1,175 @@
1
+ import io
2
+ import sys
3
+ from shlex import quote
4
+
5
+ import click
6
+ import requests
7
+
8
+ from convisoappsec.flow.util.ci_provider import CIProvider
9
+
10
+ class CreateDeployException(Exception):
11
+ pass
12
+
13
+ class PerformDeployException(Exception):
14
+ pass
15
+
16
+ class DeployFormatter(object):
17
+ DEFAULT = 'default'
18
+ ENV_VARS = 'env_vars'
19
+
20
+ def __init__(self, format):
21
+ self.stategy = self._select_strategy(format)
22
+
23
+ def format(self, deploy):
24
+ return self.stategy(deploy)
25
+
26
+ def _select_strategy(self, format):
27
+ format_strategies = {
28
+ self.DEFAULT: self.default_format,
29
+ self. ENV_VARS: self.env_vars_format,
30
+ }
31
+
32
+ strategy = format_strategies.get(format)
33
+
34
+ if not strategy:
35
+ msg_fmt = "Allowed deploy formats[{0}]. Given {1}"
36
+ msg = msg_fmt.format(
37
+ "|".join(self.FORMATS()),
38
+ format,
39
+ )
40
+
41
+ raise ValueError(msg)
42
+
43
+ return strategy
44
+
45
+ @classmethod
46
+ def FORMATS(cls):
47
+ return [
48
+ cls.DEFAULT,
49
+ cls.ENV_VARS,
50
+ ]
51
+
52
+ def env_vars_format(self, deploy):
53
+ to_env_vars_translation_args = [
54
+ # (env_var_name, attrib_name, attrib_default_val)
55
+ ('FLOW_DEPLOY_ID', 'id', 0),
56
+ ('FLOW_DEPLOY_CREATED_AT', 'created_at', ''),
57
+ ('FLOW_DEPLOY_CURRENT_VERSION_TAG', 'current_tag', ''),
58
+ ('FLOW_DEPLOY_PREVIOUS_VERSION_TAG', 'previous_tag', ''),
59
+ ('FLOW_DEPLOY_CURRENT_VERSION_COMMIT', 'current_commit', ''),
60
+ ('FLOW_DEPLOY_PREVIOUS_VERSION_COMMIT', 'previous_commit', ''),
61
+ ('CONVISO_DEPLOY_ID', 'id', 0),
62
+ ('CONVISO_DEPLOY_CREATED_AT', 'created_at', ''),
63
+ ('CONVISO_DEPLOY_CURRENT_VERSION_TAG', 'current_tag', ''),
64
+ ('CONVISO_DEPLOY_PREVIOUS_VERSION_TAG', 'previous_tag', ''),
65
+ ('CONVISO_DEPLOY_CURRENT_VERSION_COMMIT', 'current_commit', ''),
66
+ ('CONVISO_DEPLOY_PREVIOUS_VERSION_COMMIT', 'previous_commit', ''),
67
+ ]
68
+
69
+ env_vars = {}
70
+
71
+ for arg in to_env_vars_translation_args:
72
+ env_var_name, attrib_name, attrib_default_val = arg
73
+
74
+ env_var_val = env_vars[env_var_name] = deploy.get(
75
+ attrib_name, attrib_default_val
76
+ )
77
+
78
+ env_vars[env_var_name] = str(env_var_val)
79
+
80
+ buffer = io.StringIO()
81
+
82
+ for var_name, var_val in env_vars.items():
83
+ line_fmt = "export {name}={value}"
84
+ line = line_fmt.format(
85
+ name=quote(var_name),
86
+ value=quote(var_val),
87
+ )
88
+
89
+ print(line, file=buffer)
90
+
91
+ buffer.seek(0)
92
+ return buffer.read()
93
+
94
+ def default_format(self, src_deploy):
95
+ deploy = {
96
+ 'id': 0,
97
+ 'current_tag': '',
98
+ 'previous_tag': '',
99
+ 'current_commit': '',
100
+ 'previous_commit': '',
101
+ 'created_at': '',
102
+ }
103
+
104
+ deploy.update(src_deploy)
105
+
106
+ default_fmt = '''
107
+ Deploy stats:
108
+ current_version.commit={current_commit}
109
+ current_version.tag={current_tag}
110
+ previous_version.commit={previous_commit}
111
+ previous_version.tag={previous_tag}
112
+ '''
113
+
114
+ return default_fmt.format(
115
+ **deploy
116
+ )
117
+
118
+
119
+ def notify_created_deploy(deploy):
120
+ formatter = DeployFormatter(DeployFormatter.DEFAULT)
121
+ click.echo(formatter.format(deploy))
122
+
123
+
124
+ def asset_id_option(*args, **kwargs):
125
+ kwargs["envvar"] = kwargs.get("envvar", ("CONVISO_ASSET_ID", "FLOW_ASSET_ID"))
126
+ kwargs["show_envvar"] = kwargs.get("show_envvar", True)
127
+ kwargs["required"] = kwargs.get("required", False)
128
+ kwargs["hidden"] = kwargs.get("hidden", False)
129
+ asset_id_param = "--asset-id"
130
+ paramsdecl = args
131
+
132
+ if asset_id_param not in args:
133
+ paramsdecl = args + (asset_id_param, )
134
+
135
+ return click.option(
136
+ *paramsdecl,
137
+ type=int,
138
+ **kwargs
139
+ )
140
+
141
+ def on_http_error(error):
142
+ try:
143
+ raise error
144
+ except requests.exceptions.HTTPError as e:
145
+ message = error.response.text
146
+
147
+ if not message:
148
+ return
149
+
150
+ click.echo(
151
+ "HttpErrorResponse: {0}".format(message),
152
+ file=sys.stderr
153
+ )
154
+
155
+
156
+ def __try_retrieve_ci_provider(provider_name):
157
+ try:
158
+ return CIProvider[provider_name]
159
+ except KeyError as e:
160
+ error_msg_fmt = 'Received for parameter provider_name[{}]. Expected values are: [{}]'
161
+ expected_values = '|'.join(CIProvider.names())
162
+ error_msg = error_msg_fmt.format(provider_name, expected_values)
163
+
164
+ raise ValueError(error_msg) from e
165
+
166
+
167
+ def process_ci_provider_option(provider_name, env={}):
168
+ if provider_name is not None:
169
+ return __try_retrieve_ci_provider(provider_name)
170
+
171
+ for provider in CIProvider:
172
+ if provider.env_vars_exists(env):
173
+ return provider
174
+
175
+ return CIProvider.OTHER
File without changes
@@ -0,0 +1,25 @@
1
+ import click
2
+ from convisoappsec.flowcli.common import on_http_error
3
+ from convisoappsec.common import safe_join_url
4
+ from convisoappsec.flow.graphql_api.v1.client import ConvisoGraphQLClient
5
+
6
+ class Companies():
7
+ def ls(self, flow_context, company_id=None):
8
+ api_key = flow_context.key
9
+
10
+ try:
11
+ url = safe_join_url(flow_context.url, "/graphql")
12
+ conviso_api = ConvisoGraphQLClient(api_url=url,api_key=api_key)
13
+
14
+ return perform_command(conviso_api, company_id)
15
+ except Exception as exception:
16
+ on_http_error(exception)
17
+ raise click.ClickException(str(exception)) from exception
18
+
19
+ def perform_command(conviso_api, company_id):
20
+ if company_id is not None:
21
+ companies = conviso_api.companies.get_company_by_id(company_id)
22
+ else:
23
+ companies = conviso_api.companies.get_companies()
24
+
25
+ return companies
@@ -0,0 +1,3 @@
1
+ from .entrypoint import container
2
+
3
+ __all__ = ['container']
@@ -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 container():
10
+ pass
11
+
12
+
13
+ container.add_command(run)
14
+
15
+ container.epilog = '''
16
+ Run conviso container COMMAND --help for more information on a command.
17
+ '''