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,46 @@
1
+ from convisoappsec.common.graphql.low_client import GraphQLClient
2
+ from convisoappsec.flow.graphql_api.v1.resources_api import AssetsAPI, CompaniesApi, IssuesApi, DeploysApi, \
3
+ SbomApi, LogAstError, ContainerApi, ControlSyncStatus
4
+
5
+
6
+ class ConvisoGraphQLClient():
7
+ DEFAULT_AUTHORIZATION_HEADER_NAME = 'x-api-key'
8
+
9
+ def __init__(self, api_url, api_key):
10
+ headers = {
11
+ self.DEFAULT_AUTHORIZATION_HEADER_NAME: api_key
12
+ }
13
+
14
+ self.__low_client = GraphQLClient(api_url, headers)
15
+
16
+ @property
17
+ def assets(self):
18
+ return AssetsAPI(self.__low_client)
19
+
20
+ @property
21
+ def companies(self):
22
+ return CompaniesApi(self.__low_client)
23
+
24
+ @property
25
+ def issues(self):
26
+ return IssuesApi(self.__low_client)
27
+
28
+ @property
29
+ def deploys(self):
30
+ return DeploysApi(self.__low_client)
31
+
32
+ @property
33
+ def sbom(self):
34
+ return SbomApi(self.__low_client)
35
+
36
+ @property
37
+ def container(self):
38
+ return ContainerApi(self.__low_client)
39
+
40
+ @property
41
+ def ast_errors(self):
42
+ return LogAstError(self.__low_client)
43
+
44
+ @property
45
+ def control_sync_status(self):
46
+ return ControlSyncStatus(self.__low_client)
File without changes
@@ -0,0 +1,14 @@
1
+ class AssetInput:
2
+ def __init__(self, company_id, name, scan_type=None, repo_url=None):
3
+ self.name = name
4
+ self.company_id = company_id
5
+ self.scan_type = scan_type
6
+ self.repo_url = repo_url
7
+
8
+ def to_graphql_dict(self):
9
+ return {
10
+ "companyId": self.company_id,
11
+ "name": self.name,
12
+ "scanType": self.scan_type,
13
+ "repoUrl": self.repo_url
14
+ }
@@ -0,0 +1,16 @@
1
+ class IssuesInput:
2
+ def __init__(self, company_id, asset_id, page=1, per_page=50):
3
+ self.company_id = company_id
4
+ self.asset_id = asset_id
5
+ self.page = page
6
+ self.per_page = per_page
7
+ self.statuses = ['CREATED', 'IDENTIFIED']
8
+
9
+ def to_graphql_dict(self):
10
+ return {
11
+ "companyId": self.company_id,
12
+ "page": self.page,
13
+ "perPage": self.per_page,
14
+ "assetIds": self.asset_id,
15
+ "statuses": self.statuses
16
+ }
@@ -0,0 +1,35 @@
1
+ from datetime import datetime, timezone
2
+
3
+
4
+ class CreateProjectInput:
5
+ def __init__(self, company_id, asset_id, label):
6
+ self.company_id = company_id
7
+ self.asset_id = asset_id
8
+ self.label = label
9
+
10
+ def to_graphql_dict(self):
11
+ current_datetime = datetime.now(timezone.utc)
12
+ formatted_date = current_datetime.isoformat()
13
+
14
+ return {
15
+ "companyId": int(self.company_id),
16
+ "assetsIds": int(self.asset_id),
17
+ "label": self.label,
18
+ "startDate": str(formatted_date),
19
+ "typeId": 33,
20
+ "playbooksIds": 1,
21
+ "goal": '',
22
+ "scope": self.company_id
23
+ }
24
+
25
+
26
+ class UpdateProjectInput:
27
+ def __init__(self, project_id, asset_id):
28
+ self.project_id = project_id
29
+ self.asset_id = asset_id
30
+
31
+ def to_graphql_dict(self):
32
+ return {
33
+ "projectId": int(self.project_id),
34
+ "assetsIds": [int(self.asset_id)],
35
+ }
@@ -0,0 +1,489 @@
1
+ import jmespath
2
+ import json
3
+ import requests
4
+
5
+ from convisoappsec.flow.graphql_api.v1.models.asset import AssetInput
6
+ from convisoappsec.flow.graphql_api.v1.schemas import mutations, resolvers
7
+ from urllib.parse import urlparse
8
+ from convisoappsec.version import __version__
9
+
10
+
11
+ class AssetsAPI(object):
12
+ """ To operations on Asset's resources in Conviso Platform. """
13
+
14
+ def __init__(self, conviso_graphql_client):
15
+ self.__conviso_graphql_client = conviso_graphql_client
16
+
17
+ def create_asset(self, asset_input: AssetInput):
18
+ graphql_variables = asset_input.to_graphql_dict()
19
+
20
+ graphql_body_response = self.__conviso_graphql_client.execute(
21
+ mutations.CREATE_ASSET,
22
+ graphql_variables
23
+ )
24
+
25
+ expected_path = 'createAsset.asset'
26
+
27
+ asset = jmespath.search(
28
+ expected_path,
29
+ graphql_body_response,
30
+ )
31
+
32
+ return asset
33
+
34
+ def update_asset(self, company_id, asset_id, asset_name, technologies=None, repo_url=None):
35
+ graphql_variables = {
36
+ "id": asset_id,
37
+ "companyId": company_id,
38
+ "name": asset_name,
39
+ "tecnologyList": technologies,
40
+ "repoUrl": repo_url
41
+ }
42
+
43
+ graphql_body_response = self.__conviso_graphql_client.execute(
44
+ mutations.UPDATE_ASSET,
45
+ graphql_variables
46
+ )
47
+
48
+ expected_path = 'updateAsset.asset'
49
+
50
+ asset = jmespath.search(
51
+ expected_path,
52
+ graphql_body_response,
53
+ )
54
+
55
+ return asset
56
+
57
+ def get_by_company_id_or_name(self, company_id, asset_name, page, limit):
58
+ graphql_variables = {
59
+ "id": company_id,
60
+ 'name': asset_name,
61
+ "page": page,
62
+ "limit": limit
63
+ }
64
+
65
+ graphql_body_response = self.__conviso_graphql_client.execute(
66
+ resolvers.GET_ASSETS,
67
+ graphql_variables
68
+ )
69
+
70
+ expected_path = 'assets.collection'
71
+
72
+ assets_by_company = jmespath.search(
73
+ expected_path,
74
+ graphql_body_response,
75
+ )
76
+
77
+ return assets_by_company
78
+
79
+ def get_asset_url(self, company_id, asset_id):
80
+ parsed_url = urlparse(self.__conviso_graphql_client.url)
81
+
82
+ asset_path = '/scopes/{}/assets/{}'.format(
83
+ company_id,
84
+ asset_id
85
+ )
86
+
87
+ parsed_url = parsed_url._replace(path=asset_path)
88
+
89
+ return parsed_url.geturl()
90
+
91
+ def list_assets(self, params, page=1, limit=32):
92
+ graphql_variables = {
93
+ "id": params.company_id,
94
+ "name": params.name,
95
+ "page": page,
96
+ "limit": limit
97
+ }
98
+
99
+ graphql_body_response = self.__conviso_graphql_client.execute(
100
+ resolvers.GET_ASSETS,
101
+ graphql_variables
102
+ )
103
+
104
+ expected_path = 'assets.collection'
105
+
106
+ assets = jmespath.search(
107
+ expected_path,
108
+ graphql_body_response,
109
+ )
110
+
111
+ return assets
112
+
113
+ class CompaniesApi(object):
114
+ """ To operations on Companies resources in Conviso Platform. """
115
+
116
+ def __init__(self, conviso_graphql_client):
117
+ self.__conviso_graphql_client = conviso_graphql_client
118
+
119
+ def get_company_by_id(self, company_id):
120
+ graphql_variables = {
121
+ "company_id": company_id,
122
+ }
123
+
124
+ graphql_body_response = self.__conviso_graphql_client.execute(
125
+ resolvers.GET_COMPANY,
126
+ graphql_variables
127
+ )
128
+
129
+ expected_path = 'company'
130
+ company = jmespath.search(
131
+ expected_path,
132
+ graphql_body_response,
133
+ )
134
+ return company
135
+
136
+ def get_companies(self, page=1, limit=10):
137
+ graphql_variables = {
138
+ "page": page,
139
+ "limit": limit
140
+ }
141
+
142
+ graphql_body_response = self.__conviso_graphql_client.execute(
143
+ resolvers.GET_COMPANIES,
144
+ graphql_variables
145
+ )
146
+
147
+ expected_path = 'companies.collection'
148
+
149
+ companies = jmespath.search(
150
+ expected_path,
151
+ graphql_body_response,
152
+ )
153
+
154
+ return companies
155
+
156
+
157
+ class IssuesApi(object):
158
+ """ To operations on Issue's resources in Conviso Platform. """
159
+
160
+ def __init__(self, conviso_graphql_client):
161
+ self.__conviso_graphql_client = conviso_graphql_client
162
+
163
+ def get_issues_stats(self, asset_id, company_id, statuses, end_date=None):
164
+ """ Return issue stats filter by asset and company """
165
+
166
+ graphql_variables = {
167
+ 'asset_id': asset_id,
168
+ 'company_id': company_id,
169
+ 'statuses': statuses,
170
+ 'end_date': end_date
171
+ }
172
+
173
+ graphql_body_response = self.__conviso_graphql_client.execute(
174
+ resolvers.GET_ISSUES_STATS,
175
+ graphql_variables
176
+ )
177
+
178
+ expected_path = 'issuesStats.severities'
179
+
180
+ issues_stats = jmespath.search(
181
+ expected_path,
182
+ graphql_body_response,
183
+ )
184
+
185
+ return issues_stats
186
+
187
+ def send_issues_file(self, company_id, asset_id, file_path, api_key, vulnerability_type='SAST_FINDING',
188
+ deploy_id=None, commit_ref=None, control_sync_status_id=None):
189
+ """Send issues file to Conviso platform"""
190
+
191
+ url = self.__conviso_graphql_client.url
192
+
193
+ if not isinstance(vulnerability_type, list):
194
+ vulnerability_type = [vulnerability_type]
195
+
196
+ vulnerability_type = [v.upper() for v in vulnerability_type]
197
+
198
+ variables = {
199
+ "companyId": company_id,
200
+ "assetId": asset_id,
201
+ "vulnerabilityTypes": vulnerability_type,
202
+ "deployId": deploy_id,
203
+ "commitRef": commit_ref,
204
+ "controlSyncStatusId": control_sync_status_id,
205
+ "file": None
206
+ }
207
+
208
+ operations = {
209
+ "query": mutations.IMPORT_FINDINGS,
210
+ "variables": variables
211
+ }
212
+
213
+ file_map = {"0": ["variables.file"]}
214
+
215
+ headers = {
216
+ 'x-api-key': api_key,
217
+ "User-Agent": f"AST:{__version__}"
218
+ }
219
+
220
+ try:
221
+ with open(file_path, 'rb') as file:
222
+ files = {
223
+ 'operations': (None, json.dumps(operations), 'application/json'),
224
+ 'map': (None, json.dumps(file_map), 'application/json'),
225
+ '0': (file_path, file, 'application/octet-stream')
226
+ }
227
+
228
+ response = requests.post(url, files=files, headers=headers)
229
+ response.raise_for_status()
230
+ json_response = response.json()
231
+
232
+ self._handle_graphql_errors(json_response)
233
+ return json_response.get('data')
234
+
235
+ except requests.RequestException as e:
236
+ raise Exception(f"GraphQL request failed: {e}")
237
+
238
+ @staticmethod
239
+ def _handle_graphql_errors(json_response):
240
+ """Handle GraphQL errors"""
241
+ errors = json_response.get('errors')
242
+ if errors:
243
+ error_messages = [error.get('message', 'Unknown error') for error in errors]
244
+ raise Exception(f"GraphQL request failed with errors: {', '.join(error_messages)}")
245
+
246
+
247
+ class DeploysApi(object):
248
+ """ Class for deploys resources """
249
+
250
+ def __init__(self, conviso_graphql_client):
251
+ self._conviso_graphql_client = conviso_graphql_client
252
+
253
+ def get_deploys_by_asset(self, asset_id):
254
+ """ Returns deploys based on the provided asset id """
255
+
256
+ graphql_variables = {
257
+ 'asset_id': asset_id
258
+ }
259
+
260
+ graphql_body_response = self._conviso_graphql_client.execute(
261
+ resolvers.GET_DEPLOYS_BY_ASSET,
262
+ graphql_variables
263
+ )
264
+
265
+ expected_path = 'deploysByAsset.collection'
266
+
267
+ deploys = jmespath.search(
268
+ expected_path,
269
+ graphql_body_response
270
+ )
271
+
272
+ return deploys
273
+
274
+ def create_deploy(self, asset_id, previous_commit, current_commit, branch_name, api_key,
275
+ commit_history):
276
+ """create a deployment"""
277
+ url = self._conviso_graphql_client.url
278
+ operations = {
279
+ "query": mutations.CREATE_DEPLOY,
280
+ "variables": {
281
+ "assetId": asset_id,
282
+ "previousCommit": previous_commit,
283
+ "currentCommit": current_commit,
284
+ "branchName": branch_name,
285
+ "diffContent": None,
286
+ "commitHistory": None
287
+ }
288
+ }
289
+ file_map = {
290
+ "0": ["variables.diffContent"],
291
+ "1": ["variables.commitHistory"]
292
+ }
293
+
294
+ with open(commit_history, 'rb') as f:
295
+ commit_history_data = f.read()
296
+
297
+ files = {
298
+ 'operations': (None, json.dumps(operations), 'application/json'),
299
+ 'map': (None, json.dumps(file_map), 'application/json'),
300
+ '0': (commit_history, commit_history_data, 'application/octet-stream'),
301
+ '1': (commit_history, commit_history_data, 'application/octet-stream'),
302
+ }
303
+
304
+ headers = {
305
+ 'x-api-key': api_key,
306
+ "User-Agent": f"AST:{__version__}"
307
+ }
308
+
309
+ response = requests.post(url, files=files, headers=headers)
310
+ response.raise_for_status()
311
+ json_response = response.json()
312
+ self._handle_graphql_errors(json_response)
313
+ return json_response.get('data')
314
+
315
+ # TODO: extract
316
+ @staticmethod
317
+ def _handle_graphql_errors(json_response):
318
+ """ Handle GraphQL errors """
319
+ if 'errors' in json_response:
320
+ errors = json_response['errors']
321
+ for error in errors:
322
+ print(f"GraphQL Error: {error.get('message')}")
323
+ raise Exception("GraphQL request failed with errors.")
324
+
325
+
326
+ class BaseApi:
327
+ """ Base class for handling file uploads to Conviso platform """
328
+
329
+ def __init__(self, conviso_graphql_client):
330
+ self._conviso_graphql_client = conviso_graphql_client
331
+
332
+ def send_file(self, company_id, asset_id, file_path, api_key, mutation):
333
+ """ Generic method for sending a file """
334
+ url = self._conviso_graphql_client.url
335
+
336
+ operations = {
337
+ "query": mutation,
338
+ "variables": {
339
+ "companyId": company_id,
340
+ "assetId": asset_id,
341
+ "file": None
342
+ }
343
+ }
344
+
345
+ file_map = {"0": ["variables.file"]}
346
+
347
+ with open(file_path, 'rb') as file:
348
+ files = {
349
+ 'operations': (None, json.dumps(operations), 'application/json'),
350
+ 'map': (None, json.dumps(file_map), 'application/json'),
351
+ '0': (file_path, file, 'application/octet-stream')
352
+ }
353
+
354
+ headers = {
355
+ 'x-api-key': api_key,
356
+ "User-Agent": f"AST:{__version__}"
357
+ }
358
+
359
+ response = requests.post(url, files=files, headers=headers)
360
+ response.raise_for_status()
361
+ json_response = response.json()
362
+
363
+ self._handle_graphql_errors(json_response)
364
+ return json_response.get('data')
365
+
366
+ @staticmethod
367
+ def _handle_graphql_errors(json_response):
368
+ """ Handle GraphQL errors """
369
+ if 'errors' in json_response:
370
+ for error in json_response['errors']:
371
+ print(f"GraphQL Error: {error.get('message')}")
372
+ raise Exception("GraphQL request failed with errors.")
373
+
374
+
375
+ class SbomApi(BaseApi):
376
+ """ Class for SBOM file resources """
377
+
378
+ def send_sbom_file(self, company_id, asset_id, file_path, api_key):
379
+ return self.send_file(company_id, asset_id, file_path, api_key, mutations.IMPORT_SBOM)
380
+
381
+
382
+ class ContainerApi(BaseApi):
383
+ """ Class for container finding file resources """
384
+
385
+ def send_container_file(self, company_id, asset_id, file_path, api_key):
386
+ return self.send_file(company_id, asset_id, file_path, api_key, mutations.IMPORT_CONTAINER)
387
+
388
+
389
+ class LogAstError(object):
390
+ """ Class to send AST errors to Conviso Platform """
391
+ def __init__(self, conviso_graphql_client):
392
+ self._conviso_graphql_client = conviso_graphql_client
393
+
394
+ def send_ast_error(self, company_id, asset_id, log):
395
+ """ send log with company and asset to Conviso Platform """
396
+
397
+ graphql_variables = {
398
+ 'companyId': str(company_id),
399
+ 'assetId': str(asset_id),
400
+ 'log': log
401
+ }
402
+
403
+ graphql_body_response = self._conviso_graphql_client.execute(
404
+ mutations.LOG_AST_ERROR,
405
+ graphql_variables
406
+ )
407
+
408
+ expected_path = 'success'
409
+
410
+ success = jmespath.search(
411
+ expected_path,
412
+ graphql_body_response
413
+ )
414
+
415
+ return success
416
+
417
+ class ControlSyncStatus:
418
+ """ Create control sync status on conviso platform """
419
+
420
+ def __init__(self, conviso_client):
421
+ self._conviso_graphql_client = conviso_client
422
+
423
+ def create_control_sync_status(self, asset_id):
424
+ """ When AST start will create a control sync status using this method """
425
+
426
+ graphql_variables = {
427
+ 'assetId': str(asset_id)
428
+ }
429
+
430
+ graphql_response = self._conviso_graphql_client.execute(
431
+ mutations.CREATE_CONTROL_SYNC_STATUS,
432
+ graphql_variables
433
+ )
434
+
435
+ expected_path = 'createControlSyncStatus.controlSyncStatus'
436
+
437
+ control_sync_status = jmespath.search(
438
+ expected_path,
439
+ graphql_response
440
+ )
441
+
442
+ return control_sync_status
443
+
444
+ def update_control_sync_status(self, control_sync_status_id, external_vulnerability_count=1):
445
+ """ This update control sync status by 1 only to set success or failure later """
446
+
447
+ graphql_variables = {
448
+ 'id': str(control_sync_status_id),
449
+ 'externalVulnerabilityCount': external_vulnerability_count
450
+ }
451
+
452
+ graphql_response = self._conviso_graphql_client.execute(
453
+ mutations.UPDATE_CONTROL_SYNC_STATUS,
454
+ graphql_variables
455
+ )
456
+
457
+ expected_path = 'updateControlSyncStatus.controlSyncStatus'
458
+
459
+ control_sync_status = jmespath.search(
460
+ expected_path,
461
+ graphql_response
462
+ )
463
+
464
+ return control_sync_status
465
+
466
+ def increase_count(self, control_sync_status_id, asset_id, success_count=0, failure_count=0, failure_reason=None):
467
+ """ This is used when scan runs successfully """
468
+
469
+ graphql_variables = {
470
+ 'controlSyncStatusId': str(control_sync_status_id),
471
+ 'assetId': str(asset_id),
472
+ 'successCount': success_count,
473
+ 'failureCount': failure_count,
474
+ 'failureReason': failure_reason
475
+ }
476
+
477
+ graphql_response = self._conviso_graphql_client.execute(
478
+ mutations.INCREASE_ISSUE_SCAN_COUNT,
479
+ graphql_variables
480
+ )
481
+
482
+ expected_path = 'increaseIssueScanCount.controlSyncStatus'
483
+
484
+ result = jmespath.search(
485
+ expected_path,
486
+ graphql_response
487
+ )
488
+
489
+ return result
File without changes