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,487 @@
1
+ import click
2
+ import json
3
+ from convisoappsec.flowcli import help_option
4
+ from convisoappsec.flowcli.context import pass_flow_context
5
+ from convisoappsec.sast.sastbox import SASTBox
6
+ from convisoappsec.flowcli.common import (asset_id_option, on_http_error)
7
+ from convisoappsec.flowcli.requirements_verifier import RequirementsVerifier
8
+ from convisoappsec.flow import GitAdapter
9
+ from convisoappsec.common.box import ContainerWrapper
10
+ from convisoappsec.common.graphql.errors import ResponseError
11
+ from convisoappsec.flow.cleaner import RunnerCleanup
12
+ from convisoappsec.logger import LOGGER
13
+
14
+
15
+ @click.command()
16
+ @asset_id_option(required=False)
17
+ @click.option(
18
+ "-s",
19
+ "--start-commit",
20
+ required=False,
21
+ help="If no value is set so the empty tree hash commit is used.",
22
+ )
23
+ @click.option(
24
+ "-e",
25
+ "--end-commit",
26
+ required=False,
27
+ help="""If no value is set so the HEAD commit
28
+ from the current branch is used""",
29
+ )
30
+ @click.option(
31
+ "-r",
32
+ "--repository-dir",
33
+ default=".",
34
+ show_default=True,
35
+ type=click.Path(
36
+ exists=True,
37
+ resolve_path=True,
38
+ ),
39
+ required=False,
40
+ help="The source code repository directory.",
41
+ )
42
+ @click.option(
43
+ "--fail-on-severity-threshold",
44
+ required=False,
45
+ 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 \
46
+ The severity levels are: UNDEFINED, INFO, LOW, MEDIUM, HIGH, CRITICAL.",
47
+ type=click.Tuple([str, int]),
48
+ default=(None, None),
49
+ )
50
+ @click.option(
51
+ "--fail-on-threshold",
52
+ required=False,
53
+ help="If the threshold has reach then the command will fail after send the result to AppSec Flow",
54
+ type=int,
55
+ default=False,
56
+ )
57
+ @click.option(
58
+ "--send-to-flow/--no-send-to-flow",
59
+ default=True,
60
+ show_default=True,
61
+ required=False,
62
+ hidden=True,
63
+ help="""Enable or disable the ability of send analysis result
64
+ reports to flow.""",
65
+ )
66
+ @click.option(
67
+ "--deploy-id",
68
+ default=None,
69
+ required=False,
70
+ hidden=True,
71
+ envvar=("CONVISO_DEPLOY_ID", "FLOW_DEPLOY_ID")
72
+ )
73
+ @click.option(
74
+ "--sastbox-registry",
75
+ default="",
76
+ required=False,
77
+ hidden=True,
78
+ envvar=("CONVISO_SASTBOX_REGISTRY", "FLOW_SASTBOX_REGISTRY"),
79
+ )
80
+ @click.option(
81
+ "--sastbox-repository-name",
82
+ default="",
83
+ required=False,
84
+ hidden=True,
85
+ envvar=("CONVISO_SASTBOX_REPOSITORY_NAME", "FLOW_SASTBOX_REPOSITORY_NAME"),
86
+ )
87
+ @click.option(
88
+ "--sastbox-tag",
89
+ default=SASTBox.DEFAULT_TAG,
90
+ required=False,
91
+ hidden=True,
92
+ envvar=("CONVISO_SASTBOX_TAG", "FLOW_SASTBOX_TAG"),
93
+ )
94
+ @click.option(
95
+ "--sastbox-skip-login/--sastbox-no-skip-login",
96
+ default=False,
97
+ required=False,
98
+ hidden=True,
99
+ envvar=("CONVISO_SASTBOX_SKIP_LOGIN", "FLOW_SASTBOX_SKIP_LOGIN"),
100
+ )
101
+ @click.option(
102
+ '--experimental',
103
+ default=False,
104
+ is_flag=True,
105
+ hidden=True,
106
+ help="Enable experimental features.",
107
+ )
108
+ @click.option(
109
+ "--company-id",
110
+ required=False,
111
+ envvar=("CONVISO_COMPANY_ID", "FLOW_COMPANY_ID"),
112
+ help="Company ID on Conviso Platform",
113
+ )
114
+ @click.option(
115
+ '--asset-name',
116
+ required=False,
117
+ envvar=("CONVISO_ASSET_NAME", "FLOW_ASSET_NAME"),
118
+ help="Provides a asset name.",
119
+ )
120
+ @click.option(
121
+ '--vulnerability-auto-close',
122
+ default=False,
123
+ is_flag=True,
124
+ hidden=True,
125
+ help="Enable auto fixing vulnerabilities on cp.",
126
+ )
127
+ @click.option(
128
+ '--from-ast',
129
+ default=False,
130
+ is_flag=True,
131
+ hidden=True,
132
+ help="Internal use only.",
133
+ )
134
+ @click.option(
135
+ '--cleanup',
136
+ default=False,
137
+ is_flag=True,
138
+ show_default=True,
139
+ help="Clean up system resources, including temporary files, stopped containers, unused Docker images and volumes.",
140
+ )
141
+ @click.option(
142
+ '--control-sync-status-id',
143
+ required=False,
144
+ hidden=True,
145
+ help="Control sync status id.",
146
+ )
147
+ @help_option
148
+ @pass_flow_context
149
+ @click.pass_context
150
+ def run(context, flow_context, asset_id, company_id, end_commit, start_commit, repository_dir, send_to_flow, deploy_id,
151
+ sastbox_registry, sastbox_repository_name, sastbox_tag, sastbox_skip_login, fail_on_threshold,
152
+ fail_on_severity_threshold, experimental, asset_name, vulnerability_auto_close, from_ast, cleanup,
153
+ control_sync_status_id):
154
+
155
+ if context.params.get('cleanup'):
156
+ try:
157
+ log_func("Cleaning before runs auto close ...")
158
+ cleaner = RunnerCleanup()
159
+ cleaner.cleanup_all()
160
+ except Exception as e:
161
+ log_func(f"Failed to clean, trying any way ... Error: {str(e)}")
162
+
163
+ if not from_ast:
164
+ prepared_context = RequirementsVerifier.prepare_context(context)
165
+
166
+ params_to_copy = [
167
+ 'asset_id', 'company_id', 'start_commit', 'end_commit',
168
+ 'repository_dir', 'send_to_flow', 'deploy_id', 'sastbox_registry',
169
+ 'sastbox_repository_name', 'sastbox_tag', 'sastbox_skip_login',
170
+ 'experimental', 'asset_name', 'vulnerability_auto_close', 'company_id'
171
+ ]
172
+
173
+ for param_name in params_to_copy:
174
+ context.params[param_name] = (
175
+ locals()[param_name] or prepared_context.params[param_name]
176
+ )
177
+
178
+ conviso_api = flow_context.create_conviso_api_client_beta()
179
+ company_id = company_id or context.params['company_id']
180
+ asset_id = context.params['asset_id']
181
+ statuses = ['CREATED', 'IDENTIFIED', 'IN_PROGRESS', 'AWAITING_VALIDATION', 'FIX_ACCEPTED']
182
+
183
+ page = 1
184
+ merged_issues_sast = []
185
+ merged_issues_sca = []
186
+
187
+ log_func("Running automatic closure of vulnerabilities...")
188
+
189
+ while True:
190
+ try:
191
+ issues_from_cp = conviso_api.issues.auto_close_vulnerabilities(
192
+ company_id, asset_id, statuses, page
193
+ )
194
+ except ResponseError as error:
195
+ if 'Variable $company_id' in str(error):
196
+ log_func(f"Invalid company_id passed: {company_id}", fg='red')
197
+ else:
198
+ log_func(f"error: {error}", fg='red')
199
+
200
+ log_func("⚠️ Auto-close will not be performed at this time. Please set it using --company-id and try again.", fg='red')
201
+ return
202
+
203
+ total_pages = issues_from_cp['metadata']['totalPages']
204
+ issues_collection = issues_from_cp['collection']
205
+ issues_collection = [item for item in issues_collection if item['scanSource'] == 'conviso_ast']
206
+ sast_issues = [item for item in issues_collection if item['type'] == 'SAST_FINDING']
207
+ sca_issues = [item for item in issues_collection if item['type'] == 'SCA_FINDING']
208
+
209
+ # could not be if and else because on the response could contain both.
210
+ if sast_issues:
211
+ merged_issues_sast.extend(sast_issues)
212
+
213
+ if sca_issues:
214
+ merged_issues_sca.extend(sca_issues)
215
+
216
+ if total_pages == 0 or total_pages == page:
217
+ break
218
+ else:
219
+ page += 1
220
+
221
+ # sast issues filter
222
+ sast_issues_with_fix_accepted = [item for item in merged_issues_sast if item['status'] == 'FIX_ACCEPTED']
223
+ sast_issues_without_fix_accepted = [item for item in merged_issues_sast if item['status'] != 'FIX_ACCEPTED']
224
+
225
+ # sca issues filter
226
+ sca_issues_with_fix_accepted = [item for item in merged_issues_sca if item['status'] == 'FIX_ACCEPTED']
227
+ sca_issues_without_fix_accepted = [item for item in merged_issues_sca if item['status'] != 'FIX_ACCEPTED']
228
+
229
+ if len(issues_from_cp) == 0:
230
+ log_func("No vulnerabilities were found on the Conviso Platform!")
231
+ return
232
+
233
+ # Starting executing the ast again
234
+ sast_hash_issues = perform_sastbox_scan(sastbox_registry, sastbox_repository_name, sastbox_tag, repository_dir)
235
+
236
+ sca_hash_issues = perform_sca_scan(repository_dir=repository_dir)
237
+ iac_hash_issues = perform_iac_scan(repository_dir=repository_dir)
238
+
239
+ # we need to append the two lists because at the moment this was made, iac and sast has sast as type on cp.
240
+ sast_hash_issues = sast_hash_issues + iac_hash_issues
241
+ # end ast execution
242
+
243
+ set_of_sast_hash_issues = set(sast_hash_issues)
244
+ set_of_sca_hash_issues = set(sca_hash_issues)
245
+
246
+ close_sast_issues(conviso_api, sast_issues_without_fix_accepted, set_of_sast_hash_issues, control_sync_status_id)
247
+ close_sca_issues(conviso_api, sca_issues_without_fix_accepted, set_of_sca_hash_issues, control_sync_status_id)
248
+
249
+ sast_issues_to_reopen = [
250
+ {'id': item['id'], 'originalIssueIdFromTool': item['originalIssueIdFromTool']}
251
+ for item in sast_issues_with_fix_accepted if item['originalIssueIdFromTool'] in sast_hash_issues
252
+ ]
253
+
254
+ sca_issues_to_reopen = [
255
+ {'id': item['id'], 'originalIssueIdFromTool': item['originalIssueIdFromTool']}
256
+ for item in sca_issues_with_fix_accepted if item['originalIssueIdFromTool'] in sca_hash_issues
257
+ ]
258
+
259
+ if sast_issues_to_reopen:
260
+ log_func("SAST: reopening {issues} vulnerability/vulnerabilities on conviso platform ...".format(
261
+ issues=len(sast_issues_to_reopen))
262
+ )
263
+
264
+ reopen_issues(conviso_api, sast_issues_to_reopen, control_sync_status_id)
265
+
266
+ if sca_issues_to_reopen:
267
+ log_func("SCA: reopening {issues} vulnerability/vulnerabilities on conviso platform ...".format(
268
+ issues=len(sca_issues_to_reopen))
269
+ )
270
+
271
+ reopen_issues(conviso_api, sca_issues_to_reopen, control_sync_status_id)
272
+
273
+
274
+ def close_sast_issues(conviso_api, issues_from_cp, issues_from_current_scan, control_sync_status_id):
275
+ """
276
+ method to close sast issues on conviso platform
277
+
278
+ # issues_from_cp are issues already on conviso platform.
279
+ # issues_from_current_scan are issues identify on each time the ast command runs, these are always from a full code
280
+ base scan
281
+ """
282
+ log_func("SAST: Verifying if any vulnerability was solved...")
283
+
284
+ differences = [
285
+ {'id': item['id'], 'originalIssueIdFromTool': item['originalIssueIdFromTool']}
286
+ for item in issues_from_cp if item['originalIssueIdFromTool'] not in issues_from_current_scan
287
+ ]
288
+
289
+ if len(differences) == 0:
290
+ log_func("No vulnerabilities have been fixed yet...")
291
+ return
292
+
293
+ log_func("SAST: Fixing {issues} vulnerabilities on the Conviso Platform...".format(issues=len(differences)))
294
+
295
+ for issue in differences:
296
+ issue_id = issue['id']
297
+ status = 'FIX_ACCEPTED'
298
+ reason = ("The vulnerability is no longer found in the specified file/path. Its status has been updated by "
299
+ "Conviso AST")
300
+
301
+ conviso_api.issues.update_issue_status(
302
+ issue_id=issue_id, status=status, reason=reason, control_sync_status_id=control_sync_status_id
303
+ )
304
+
305
+
306
+ def close_sca_issues(conviso_api, issues_from_cp, issues_from_current_scan, control_sync_status_id):
307
+ """ method to close sca issues on conviso platform """
308
+
309
+ log_func("SCA: Verifying if any vulnerability was solved...")
310
+ differences = [
311
+ {'id': item['id'], 'originalIssueIdFromTool': item['originalIssueIdFromTool']}
312
+ for item in issues_from_cp if item['originalIssueIdFromTool'] not in issues_from_current_scan
313
+ ]
314
+
315
+ if len(differences) == 0:
316
+ log_func("No vulnerabilities have been fixed yet...")
317
+ return
318
+
319
+ log_func("SCA: Fixing {issues} vulnerabilities on the Conviso Platform...".format(issues=len(differences)))
320
+
321
+ for issue in differences:
322
+ issue_id = issue['id']
323
+ status = 'FIX_ACCEPTED'
324
+ reason = ("The vulnerability is no longer found in the specified file/path. Its status has been updated by "
325
+ "Conviso AST")
326
+
327
+ conviso_api.issues.update_issue_status(
328
+ issue_id=issue_id, status=status, reason=reason, control_sync_status_id=control_sync_status_id
329
+ )
330
+
331
+
332
+ def reopen_issues(conviso_api, issues, control_sync_status_id):
333
+ """ Method to reopen issues when was detected again after a full scan is performed """
334
+
335
+ for issue in issues:
336
+ issue_id = issue['id']
337
+ status = 'IDENTIFIED'
338
+ reason = 'Status has been updated from Fixed to Identified by Conviso AST'
339
+
340
+ conviso_api.issues.update_issue_status(
341
+ issue_id=issue_id, status=status, reason=reason, control_sync_status_id=control_sync_status_id
342
+ )
343
+
344
+ def perform_sastbox_scan(sastbox_registry, sastbox_repository_name, sastbox_tag, repository_dir):
345
+ LOGGER.info(' 🔍 [SAST] Running scan...')
346
+
347
+ sastbox = SASTBox(registry=sastbox_registry, repository_name=sastbox_repository_name, tag=sastbox_tag)
348
+ git_adapter = GitAdapter(repository_dir, unshallow_repository=False)
349
+
350
+ reports = sastbox.run_scan_diff(
351
+ repository_dir,
352
+ git_adapter.head_commit,
353
+ git_adapter.empty_repository_tree_commit
354
+ )
355
+
356
+ try:
357
+ first_report = next(reports)
358
+ with open(str(first_report), 'r') as f:
359
+ issues = json.load(f).get("issues", [])
360
+
361
+ return [
362
+ issue.get("hash_issue") or issue.get("hash_issue_v2")
363
+ for issue in issues
364
+ if issue.get("hash_issue") or issue.get("hash_issue_v2")
365
+ ]
366
+
367
+ except (StopIteration, Exception) as e:
368
+ if isinstance(e, StopIteration):
369
+ return []
370
+ return []
371
+
372
+ @pass_flow_context
373
+ def perform_sca_scan(flow_context, repository_dir):
374
+ LOGGER.info(' 🔍 [SCA] Running scan...')
375
+
376
+ try:
377
+ REQUIRED_CODEBASE_PATH = '/code'
378
+ OSV_SCANNER_IMAGE_NAME = 'osv_scanner'
379
+
380
+ scanners = {
381
+ OSV_SCANNER_IMAGE_NAME: {
382
+ 'repository_name': OSV_SCANNER_IMAGE_NAME,
383
+ 'tag': 'latest',
384
+ 'command': [
385
+ '-c', REQUIRED_CODEBASE_PATH,
386
+ '-f', 'json',
387
+ '-o', '/{}.json'.format(OSV_SCANNER_IMAGE_NAME)
388
+ ],
389
+ 'repository_dir': repository_dir
390
+ },
391
+ }
392
+
393
+ conviso_rest_api = flow_context.create_conviso_rest_api_client()
394
+ token = conviso_rest_api.docker_registry.get_sast_token()
395
+ scabox = ContainerWrapper(
396
+ token=token,
397
+ containers_map=scanners,
398
+ logger=None,
399
+ timeout=7200
400
+ )
401
+
402
+ scabox.run()
403
+
404
+ results_filepaths = []
405
+ for unit in scabox.scanners:
406
+ file_path = unit.results
407
+ if file_path:
408
+ results_filepaths.append(file_path)
409
+
410
+ hash_issues = []
411
+
412
+ for report_path in results_filepaths:
413
+ try:
414
+ with open(report_path, 'r') as report_file:
415
+ report_content = json.load(report_file)
416
+ issues = report_content.get("issues", [])
417
+ hash_issues.extend(issue.get("hash_issue") for issue in issues)
418
+
419
+ except (FileNotFoundError, json.JSONDecodeError) as e:
420
+ print(f"Error processing {report_path}: {e}")
421
+
422
+ return hash_issues
423
+
424
+ except Exception as e:
425
+ on_http_error(e)
426
+ raise click.ClickException(str(e)) from e
427
+
428
+
429
+ @pass_flow_context
430
+ def perform_iac_scan(flow_context, repository_dir):
431
+ """ Perform an iac scan """
432
+ LOGGER.info(' 🔍 [IaC] Running scan...')
433
+
434
+ try:
435
+ REQUIRED_CODEBASE_PATH = '/code'
436
+ IAC_IMAGE_NAME = 'iac_scanner_checkov'
437
+ IAC_SCAN_FILENAME = '/{}.json'.format(IAC_IMAGE_NAME)
438
+ containers_map = {
439
+ IAC_IMAGE_NAME: {
440
+ 'repository_dir': repository_dir,
441
+ 'repository_name': IAC_IMAGE_NAME,
442
+ 'tag': 'unstable',
443
+ 'command': [
444
+ '-c', REQUIRED_CODEBASE_PATH,
445
+ '-o', IAC_SCAN_FILENAME,
446
+ ],
447
+ },
448
+ }
449
+
450
+ conviso_rest_api = flow_context.create_conviso_rest_api_client()
451
+ token = conviso_rest_api.docker_registry.get_sast_token()
452
+ scanners_wrapper = ContainerWrapper(
453
+ token=token,
454
+ containers_map=containers_map,
455
+ logger=None,
456
+ timeout=7200
457
+ )
458
+
459
+ scanners_wrapper.run()
460
+
461
+ results_filepaths = [] # [str(r.results) for r in scanners_wrapper.scanners]
462
+ hash_issues = []
463
+ for r in scanners_wrapper.scanners:
464
+ report_filepath = r.results
465
+ if report_filepath:
466
+ results_filepaths.append(report_filepath)
467
+
468
+ for report_path in results_filepaths:
469
+ try:
470
+ with open(report_path) as report_file:
471
+ report_content = json.load(report_file)
472
+ results = report_content.get("runs", [])[0].get("results", [])
473
+ for result in results:
474
+ partial_fingerprints = result.get("partialFingerprints", {})
475
+ hash_issue = partial_fingerprints.get("hashIssueV2")
476
+ if hash_issue:
477
+ hash_issues.append(hash_issue)
478
+ except (FileNotFoundError, json.JSONDecodeError) as e:
479
+ print(f"Error processing {report_path}: {e}")
480
+
481
+ return hash_issues
482
+ except Exception as e:
483
+ on_http_error(e)
484
+ raise click.ClickException(str(e)) from e
485
+
486
+ def log_func(msg, new_line=True, fg='blue'):
487
+ click.echo(click.style(msg, bold=True, fg=fg), nl=new_line, err=True)
@@ -0,0 +1,29 @@
1
+ import logging
2
+
3
+ logging.basicConfig(
4
+ filename='output.log',
5
+ filemode='w',
6
+ level=logging.DEBUG,
7
+ format='%(levelname)s: %(filename)s: %(threadName)s: %(name)s: %(message)s'
8
+ )
9
+
10
+ LOGGER = logging.getLogger(__name__)
11
+
12
+
13
+ def log_and_notify_ast_event(flow_context, company_id, asset_id, ast_log):
14
+ """
15
+ Logs an AST (Application Security Test) event for a specific company and asset,
16
+ and sends notifications.
17
+
18
+ This method performs the following actions:
19
+ 1. Sends the provided AST log to a monitoring platform like Datadog for logging and tracking.
20
+ 2. Sends a Slack notification with relevant details about the AST event.
21
+
22
+ Args:
23
+ flow_context: The context of the AST execution.
24
+ company_id (int): The ID of the company for which the AST event is being logged.
25
+ asset_id (int): The ID of the asset associated with the AST event.
26
+ ast_log (string): A string with the log details of the AST event.
27
+ """
28
+ conviso_api = flow_context.create_conviso_graphql_client()
29
+ conviso_api.ast_errors.send_ast_error(company_id = company_id, asset_id = asset_id, log = ast_log)
File without changes
@@ -0,0 +1,45 @@
1
+ from enum import IntEnum
2
+ import json
3
+
4
+ class Severity(IntEnum):
5
+ # Sastbox [:undefined, :info, :low, :medium, :high, :critical]
6
+ UNDEFINED = 0
7
+ INFO = 1
8
+ LOW = 2
9
+ MEDIUM = 3
10
+ HIGH = 4
11
+ CRITICAL = 5
12
+
13
+ @classmethod
14
+ def has_value(cls, value):
15
+ return value in cls._member_names_
16
+
17
+
18
+
19
+ class Decision:
20
+
21
+
22
+ def __init__(self, report):
23
+ self.report = open(report)
24
+ self.json = json.load(self.report)
25
+ self.issues = self.json.get('issues')
26
+ self.sev = self.issues
27
+
28
+ def block_from_severity(self, severity, threshold=1):
29
+ if not severity:
30
+ return False
31
+ severity = Severity[severity]
32
+ self.sev = [issue for issue in self.issues if Severity[issue.get('severity').upper()] >= severity]
33
+ return len(self.sev) >= threshold
34
+
35
+ def block_from_findings(self, threshold):
36
+ if not threshold:
37
+ return False
38
+ return len(self.issues) >= threshold
39
+
40
+ def filtered_issues(self):
41
+ return self.sev
42
+
43
+
44
+
45
+