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,296 @@
1
+ import tempfile
2
+ import tarfile
3
+ import docker
4
+ import os
5
+ import click
6
+ import json
7
+ import docker.errors
8
+ from io import BytesIO
9
+ from contextlib import suppress
10
+ from pathlib import Path
11
+ from convisoappsec.flowcli.context import pass_flow_context
12
+ from convisoappsec.flow import GitAdapter
13
+
14
+ bitbucket = os.getenv('BITBUCKET_CLONE_DIR')
15
+
16
+
17
+ class SASTBox(object):
18
+ REGISTRY = 'public.ecr.aws/convisoappsec'
19
+ REPOSITORY_NAME = 'sastbox_v2'
20
+ DEFAULT_TAG = 'unstable'
21
+ CONTAINER_CODE_DIR = bitbucket or '/code'
22
+ CONTAINER_REPORTS_DIR = '/tmp'
23
+ WORKSPACE_REPORT_PATH = CONTAINER_CODE_DIR
24
+ JSON_REPORT_PATTERN = 'output.json'
25
+ SUCCESS_EXIT_CODE = 1
26
+ USER_ENV_VAR = "USER"
27
+
28
+ def __init__(self, registry=None, repository_name=None, tag=None):
29
+ self.docker = docker.from_env(
30
+ version="auto"
31
+ )
32
+ self.container = None
33
+ self.registry = registry or self.REGISTRY
34
+ self.repository_name = repository_name or self.REPOSITORY_NAME
35
+ self.tag = tag or self.DEFAULT_TAG
36
+
37
+ def login(self, password, username='AWS'):
38
+ login_args = {
39
+ 'registry': self.REGISTRY,
40
+ 'username': username,
41
+ 'password': password,
42
+ 'reauth': True,
43
+ }
44
+
45
+ login_result = self.docker.login(**login_args)
46
+ return login_result
47
+
48
+ def run_scan_diff(self, code_dir, current_commit, previous_commit, log=None):
49
+ return self._scan_diff(code_dir, current_commit, previous_commit, log)
50
+
51
+ @property
52
+ def size(self):
53
+ try:
54
+ registry_data = self.docker.images.get_registry_data(
55
+ self.image
56
+ )
57
+ descriptor = registry_data.attrs.get('Descriptor', {})
58
+ return descriptor.get('size') * 1024 * 1024
59
+ except docker.errors.APIError:
60
+ return 6300 * 1024 * 1024
61
+
62
+ def pull(self):
63
+ size = self.size
64
+ layers = {}
65
+ for line in self.docker.api.pull(
66
+ self.repository, tag=self.tag, stream=True, decode=True
67
+ ):
68
+ status = line.get('status', '')
69
+ detail = line.get('progressDetail', {})
70
+
71
+ if status == 'Downloading':
72
+ with suppress(Exception):
73
+ layer_id = line.get('id')
74
+ layer = layers.get(layer_id, {})
75
+ layer.update(detail)
76
+ layers[layer_id] = layer
77
+
78
+ for layer in layers.values():
79
+ current = layer.get('current')
80
+ total = layer.get('total')
81
+
82
+ if (current / total) > 0.98 and not layer.get('done'):
83
+ yield current
84
+ layer.update({'done': True})
85
+
86
+ yield size
87
+
88
+ def _scan_diff(self, code_dir, current_commit, previous_commit, log):
89
+ environment = {
90
+ 'PREVIOUS_COMMIT': previous_commit,
91
+ 'CURRENT_COMMIT': current_commit,
92
+ 'SASTBOX_REPORTS_DIR': self.CONTAINER_REPORTS_DIR,
93
+ 'SASTBOX_REPORT_DIR': '/tmp',
94
+ 'SASTBOX_REPORT_PATTERN': '*.sarif',
95
+ 'SASTBOX_CODE_DIR': self.CONTAINER_CODE_DIR,
96
+ }
97
+
98
+ command_parts = [
99
+ 'ruby', 'manager/sastbox_cli.rb',
100
+ '-c', self.CONTAINER_CODE_DIR,
101
+ '-a',
102
+ '-o', '/tmp/output.sarif',
103
+ f'--diff={previous_commit},{current_commit}',
104
+ '&&',
105
+ 'cp', '$(find "$SASTBOX_REPORT_DIR" -name "$SASTBOX_REPORT_PATTERN")', '$SASTBOX_REPORTS_DIR'
106
+ ]
107
+ command = ' '.join(command_parts)
108
+
109
+ # Configure container creation
110
+ create_args = {
111
+ 'image': self.image,
112
+ 'entrypoint': ['sh', '-c'],
113
+ 'command': [command],
114
+ 'tty': True,
115
+ 'detach': True,
116
+ 'environment': environment,
117
+ }
118
+
119
+ try:
120
+ try:
121
+ self.container = self.docker.containers.create(**create_args)
122
+ except docker.errors.APIError as e:
123
+ raise RuntimeError(f"Failed to create container: {e}")
124
+
125
+ # Create and upload source code tarball
126
+ source_code_tarball_file = tempfile.TemporaryFile()
127
+ try:
128
+ source_code_tarball = tarfile.open(mode="w|gz", fileobj=source_code_tarball_file)
129
+ source_code_tarball.add(
130
+ name=code_dir,
131
+ arcname=self.CONTAINER_CODE_DIR,
132
+ filter=lambda tarinfo: tarinfo if not tarinfo.name.endswith('.zip') else None
133
+ )
134
+ source_code_tarball.close()
135
+ source_code_tarball_file.seek(0)
136
+ try:
137
+ self.container.put_archive("/", source_code_tarball_file)
138
+ except docker.errors.APIError as e:
139
+ raise RuntimeError(f"Failed to upload tarball: {e}")
140
+ finally:
141
+ source_code_tarball_file.close()
142
+
143
+ # Start the container and stream logs
144
+ try:
145
+ self.container.start()
146
+ except docker.errors.APIError as e:
147
+ raise RuntimeError(f"Failed to start container: {e}")
148
+
149
+ for line in self.container.logs(stream=True):
150
+ if log:
151
+ log(line, new_line=False)
152
+
153
+ self.recovery_technologies_file()
154
+
155
+ wait_result = self.container.wait()
156
+ status_code = wait_result.get('StatusCode')
157
+
158
+ if status_code != self.SUCCESS_EXIT_CODE:
159
+ logs = self.container.logs().decode('utf-8')
160
+ raise RuntimeError(f"SASTBox exited with status code {status_code}\nLogs:\n{logs}")
161
+
162
+ # Retrieve and extract reports
163
+ try:
164
+ chunks, _ = self.container.get_archive(self.CONTAINER_REPORTS_DIR)
165
+ except docker.errors.APIError as e:
166
+ raise RuntimeError(f"Failed to retrieve reports: {e}")
167
+
168
+ reports_tarball_file = tempfile.TemporaryFile()
169
+ try:
170
+ for chunk in chunks:
171
+ reports_tarball_file.write(chunk)
172
+ tempdir = tempfile.mkdtemp()
173
+ reports_tarball_file.seek(0)
174
+ reports_tarball = tarfile.open(mode="r|", fileobj=reports_tarball_file)
175
+ reports_tarball.extractall(path=tempdir)
176
+ reports_tarball.close()
177
+ finally:
178
+ reports_tarball_file.close()
179
+
180
+ # Verify reports exist and return their paths
181
+ reports = self._list_reports_paths(tempdir)
182
+ if not reports:
183
+ raise RuntimeError("No reports found in the container")
184
+
185
+ except docker.errors.APIError as e:
186
+ raise RuntimeError(f"Failed to retrieve reports: {e}")
187
+
188
+ return reports
189
+
190
+ @property
191
+ def repository(self):
192
+ return "{registry}/{repository_name}".format(
193
+ registry=self.registry,
194
+ repository_name=self.repository_name,
195
+ )
196
+
197
+ @property
198
+ def image(self):
199
+ return "{repository}:{tag}".format(
200
+ repository=self.repository,
201
+ tag=self.tag,
202
+ )
203
+
204
+ def __del__(self):
205
+ with suppress(Exception):
206
+ self.container.remove(v=True, force=True)
207
+
208
+ @classmethod
209
+ def _list_reports_paths(cls, root_dir):
210
+ root_dir = root_dir + cls.CONTAINER_REPORTS_DIR
211
+ sastbox_reports_dir = Path(root_dir)
212
+
213
+ for report in sastbox_reports_dir.glob(cls.JSON_REPORT_PATTERN):
214
+ yield report
215
+
216
+ def recovery_technologies_file(self):
217
+ """ Method to recover a fingerprint file inside the container with founded technology """
218
+ try:
219
+ generator_object, _ = self.container.get_archive('/tmp/')
220
+ file_content = b"".join(generator_object)
221
+ file_content_stream = BytesIO(file_content)
222
+ tar = tarfile.open(fileobj=file_content_stream)
223
+ file_names = tar.getnames()
224
+
225
+ fingerprint_file = next(
226
+ (file for file in file_names if file.startswith('tmp/fingerprint') and file.endswith('.json')), None)
227
+
228
+ if not fingerprint_file:
229
+ log_func("No file starting with 'fingerprint' and ending with '.json' found.")
230
+ return
231
+
232
+ actual_filename = fingerprint_file.split('/')[-1]
233
+
234
+ generator_object, _ = self.container.get_archive(f'/tmp/{actual_filename}')
235
+ file_content = b"".join(generator_object)
236
+ file_content_stream = BytesIO(file_content)
237
+ tar = tarfile.open(fileobj=file_content_stream)
238
+ file_data = tar.extractfile(actual_filename)
239
+ content = json.loads(file_data.read())
240
+ technologies = content['result']['technologies']
241
+ except Exception as error:
242
+ msg = "\U0001F4AC Something goes wrong when trying to recover the technologies, continuing ..."
243
+ log_func(msg)
244
+ technologies = []
245
+
246
+ if technologies is None:
247
+ return
248
+
249
+ self.update_asset_technologies(technologies=technologies)
250
+
251
+ @staticmethod
252
+ @pass_flow_context
253
+ @click.pass_context
254
+ def update_asset_technologies(flow_context, context, technologies):
255
+ """
256
+ Update technologies on asset.
257
+ Args:
258
+ flow_context (dict): Flow context containing parameters.
259
+ context (object): Object containing necessary methods (e.g., create_conviso_graphql_client).
260
+ technologies (list): List of technologies to be updated.
261
+ Returns:
262
+ dict: Response from the API call.
263
+ """
264
+
265
+ # this prevents a broken execution when something goes wrong.
266
+ try:
267
+ git_adapter = GitAdapter(flow_context.params['repository_dir'])
268
+ repo_url = git_adapter.repo_url()
269
+
270
+ company_id = flow_context.params.get('company_id')
271
+ asset_id = flow_context.params.get('asset_id')
272
+ asset_name = flow_context.params.get('asset_name')
273
+ unwanted_technologies = {
274
+ 'unknown', 'json', 'text', 'ini', 'diff', 'xml', 'markdown', 'csv', 'gemfile.lock', 'html+erb',
275
+ 'javascript+erb', 'robots.txt', 'yaml', 'batchfile', 'java properties', 'svg', 'json with comments'
276
+ }
277
+ updated_technologies = [tech for tech in technologies if tech not in unwanted_technologies]
278
+ conviso_api = context.create_conviso_graphql_client()
279
+
280
+ response = conviso_api.assets.update_asset(
281
+ company_id=int(company_id),
282
+ asset_id=asset_id,
283
+ asset_name=asset_name,
284
+ technologies=updated_technologies,
285
+ repo_url=repo_url
286
+ )
287
+ except Exception as error:
288
+ msg = "\U0001F4AC Something goes wrong when trying to send technologies to the CP, continuing... {error}".format(error=error)
289
+ log_func(msg)
290
+
291
+ response = None
292
+
293
+ return response
294
+
295
+ def log_func(msg, new_line=True):
296
+ click.echo(msg, nl=new_line, err=True)
@@ -0,0 +1 @@
1
+ __version__ = '3.0.0'