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.
- conviso_ast-3.0.0.data/scripts/flow_bash_completer.sh +21 -0
- conviso_ast-3.0.0.data/scripts/flow_fish_completer.fish +1 -0
- conviso_ast-3.0.0.data/scripts/flow_zsh_completer.sh +32 -0
- conviso_ast-3.0.0.dist-info/METADATA +37 -0
- conviso_ast-3.0.0.dist-info/RECORD +128 -0
- conviso_ast-3.0.0.dist-info/WHEEL +5 -0
- conviso_ast-3.0.0.dist-info/entry_points.txt +3 -0
- conviso_ast-3.0.0.dist-info/top_level.txt +1 -0
- convisoappsec/__init__.py +0 -0
- convisoappsec/common/__init__.py +5 -0
- convisoappsec/common/box.py +251 -0
- convisoappsec/common/cleaner.py +78 -0
- convisoappsec/common/docker.py +399 -0
- convisoappsec/common/exceptions.py +8 -0
- convisoappsec/common/git_data_parser.py +76 -0
- convisoappsec/common/graphql/__init__.py +0 -0
- convisoappsec/common/graphql/error_handlers.py +75 -0
- convisoappsec/common/graphql/errors.py +16 -0
- convisoappsec/common/graphql/low_client.py +51 -0
- convisoappsec/common/retry_handler.py +40 -0
- convisoappsec/common/strings.py +8 -0
- convisoappsec/flow/__init__.py +3 -0
- convisoappsec/flow/api.py +104 -0
- convisoappsec/flow/cleaner.py +118 -0
- convisoappsec/flow/graphql_api/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/client.py +18 -0
- convisoappsec/flow/graphql_api/beta/models/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/models/issues/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/models/issues/container.py +72 -0
- convisoappsec/flow/graphql_api/beta/models/issues/iac.py +6 -0
- convisoappsec/flow/graphql_api/beta/models/issues/normalize.py +13 -0
- convisoappsec/flow/graphql_api/beta/models/issues/sast.py +53 -0
- convisoappsec/flow/graphql_api/beta/models/issues/sca.py +78 -0
- convisoappsec/flow/graphql_api/beta/resources_api.py +142 -0
- convisoappsec/flow/graphql_api/beta/schemas/__init__.py +0 -0
- convisoappsec/flow/graphql_api/beta/schemas/mutations/__init__.py +61 -0
- convisoappsec/flow/graphql_api/beta/schemas/resolvers/__init__.py +0 -0
- convisoappsec/flow/graphql_api/v1/__init__.py +0 -0
- convisoappsec/flow/graphql_api/v1/client.py +46 -0
- convisoappsec/flow/graphql_api/v1/models/__init__.py +0 -0
- convisoappsec/flow/graphql_api/v1/models/asset.py +14 -0
- convisoappsec/flow/graphql_api/v1/models/issues.py +16 -0
- convisoappsec/flow/graphql_api/v1/models/project.py +35 -0
- convisoappsec/flow/graphql_api/v1/resources_api.py +489 -0
- convisoappsec/flow/graphql_api/v1/schemas/__init__.py +0 -0
- convisoappsec/flow/graphql_api/v1/schemas/mutations/__init__.py +212 -0
- convisoappsec/flow/graphql_api/v1/schemas/resolvers/__init__.py +180 -0
- convisoappsec/flow/source_code_scanner/__init__.py +9 -0
- convisoappsec/flow/source_code_scanner/exceptions.py +2 -0
- convisoappsec/flow/source_code_scanner/scc.py +68 -0
- convisoappsec/flow/source_code_scanner/source_code_scanner.py +177 -0
- convisoappsec/flow/util/__init__.py +7 -0
- convisoappsec/flow/util/ci_provider.py +99 -0
- convisoappsec/flow/util/metrics.py +16 -0
- convisoappsec/flow/util/source_code_compressor.py +22 -0
- convisoappsec/flow/version_control_system_adapter.py +528 -0
- convisoappsec/flow/version_searchers/__init__.py +9 -0
- convisoappsec/flow/version_searchers/sorted_by_versioning_style.py +85 -0
- convisoappsec/flow/version_searchers/timebased_version_seacher.py +39 -0
- convisoappsec/flow/version_searchers/version_searcher_result.py +33 -0
- convisoappsec/flow/versioning_style/__init__.py +0 -0
- convisoappsec/flow/versioning_style/semantic_versioning.py +44 -0
- convisoappsec/flowcli/__init__.py +3 -0
- convisoappsec/flowcli/__main__.py +4 -0
- convisoappsec/flowcli/assets/__init__.py +4 -0
- convisoappsec/flowcli/assets/create.py +88 -0
- convisoappsec/flowcli/assets/entrypoint.py +20 -0
- convisoappsec/flowcli/assets/ls.py +63 -0
- convisoappsec/flowcli/ast/__init__.py +3 -0
- convisoappsec/flowcli/ast/entrypoint.py +427 -0
- convisoappsec/flowcli/common.py +175 -0
- convisoappsec/flowcli/companies/__init__.py +0 -0
- convisoappsec/flowcli/companies/ls.py +25 -0
- convisoappsec/flowcli/container/__init__.py +3 -0
- convisoappsec/flowcli/container/entrypoint.py +17 -0
- convisoappsec/flowcli/container/run.py +306 -0
- convisoappsec/flowcli/context.py +49 -0
- convisoappsec/flowcli/deploy/__init__.py +0 -0
- convisoappsec/flowcli/deploy/create/__init__.py +4 -0
- convisoappsec/flowcli/deploy/create/context.py +12 -0
- convisoappsec/flowcli/deploy/create/entrypoint.py +31 -0
- convisoappsec/flowcli/deploy/create/with_/__init__.py +3 -0
- convisoappsec/flowcli/deploy/create/with_/entrypoint.py +20 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/__init__.py +4 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/context.py +11 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/entrypoint.py +30 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/__init__.py +4 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/entrypoint.py +21 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/time_.py +84 -0
- convisoappsec/flowcli/deploy/create/with_/tag_tracker/sort_by/versioning_style.py +115 -0
- convisoappsec/flowcli/deploy/create/with_/values.py +133 -0
- convisoappsec/flowcli/entrypoint.py +103 -0
- convisoappsec/flowcli/environment_checker.py +45 -0
- convisoappsec/flowcli/findings/__init__.py +4 -0
- convisoappsec/flowcli/findings/create/__init__.py +4 -0
- convisoappsec/flowcli/findings/create/entrypoint.py +18 -0
- convisoappsec/flowcli/findings/create/with_/__init__.py +3 -0
- convisoappsec/flowcli/findings/create/with_/entrypoint.py +19 -0
- convisoappsec/flowcli/findings/create/with_/version_tracker.py +93 -0
- convisoappsec/flowcli/findings/entrypoint.py +19 -0
- convisoappsec/flowcli/findings/import_sarif/__init__.py +4 -0
- convisoappsec/flowcli/findings/import_sarif/entrypoint.py +430 -0
- convisoappsec/flowcli/help_option.py +18 -0
- convisoappsec/flowcli/iac/__init__.py +3 -0
- convisoappsec/flowcli/iac/entrypoint.py +17 -0
- convisoappsec/flowcli/iac/run.py +328 -0
- convisoappsec/flowcli/requirements_verifier.py +132 -0
- convisoappsec/flowcli/sast/__init__.py +3 -0
- convisoappsec/flowcli/sast/entrypoint.py +17 -0
- convisoappsec/flowcli/sast/run.py +485 -0
- convisoappsec/flowcli/sbom/__init__.py +3 -0
- convisoappsec/flowcli/sbom/entrypoint.py +17 -0
- convisoappsec/flowcli/sbom/generate.py +235 -0
- convisoappsec/flowcli/sca/__init__.py +3 -0
- convisoappsec/flowcli/sca/entrypoint.py +17 -0
- convisoappsec/flowcli/sca/run.py +479 -0
- convisoappsec/flowcli/vulnerability/__init__.py +3 -0
- convisoappsec/flowcli/vulnerability/assert_security_rules.py +201 -0
- convisoappsec/flowcli/vulnerability/container_vulnerability_manager.py +175 -0
- convisoappsec/flowcli/vulnerability/entrypoint.py +18 -0
- convisoappsec/flowcli/vulnerability/rules_schema.json +53 -0
- convisoappsec/flowcli/vulnerability/run.py +487 -0
- convisoappsec/logger.py +29 -0
- convisoappsec/sast/__init__.py +0 -0
- convisoappsec/sast/decision.py +45 -0
- convisoappsec/sast/sastbox.py +296 -0
- convisoappsec/version.py +1 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import click
|
|
3
|
+
import click_log
|
|
4
|
+
import traceback
|
|
5
|
+
from convisoappsec.common.retry_handler import RetryHandler
|
|
6
|
+
from copy import deepcopy as clone
|
|
7
|
+
from convisoappsec.common.box import ContainerWrapper
|
|
8
|
+
from convisoappsec.flow.graphql_api.beta.models.issues.iac import CreateIacFindingInput
|
|
9
|
+
from convisoappsec.flowcli import help_option
|
|
10
|
+
from convisoappsec.flowcli.common import asset_id_option, on_http_error
|
|
11
|
+
from convisoappsec.flowcli.context import pass_flow_context
|
|
12
|
+
from convisoappsec.logger import LOGGER, log_and_notify_ast_event
|
|
13
|
+
from convisoappsec.flowcli.requirements_verifier import RequirementsVerifier
|
|
14
|
+
from convisoappsec.flow import GitAdapter
|
|
15
|
+
from convisoappsec.common.graphql.errors import ResponseError
|
|
16
|
+
from convisoappsec.common.cleaner import Cleaner
|
|
17
|
+
|
|
18
|
+
click_log.basic_config(LOGGER)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@click.command()
|
|
22
|
+
@click_log.simple_verbosity_option(LOGGER)
|
|
23
|
+
@asset_id_option(required=False)
|
|
24
|
+
@click.option(
|
|
25
|
+
'-r',
|
|
26
|
+
'--repository-dir',
|
|
27
|
+
default=".",
|
|
28
|
+
show_default=True,
|
|
29
|
+
type=click.Path(
|
|
30
|
+
exists=True,
|
|
31
|
+
resolve_path=True,
|
|
32
|
+
),
|
|
33
|
+
required=False,
|
|
34
|
+
help="The source code repository directory.",
|
|
35
|
+
)
|
|
36
|
+
@click.option(
|
|
37
|
+
"--send-to-flow/--no-send-to-flow",
|
|
38
|
+
default=True,
|
|
39
|
+
show_default=True,
|
|
40
|
+
required=False,
|
|
41
|
+
help="""Enable or disable the ability of send analysis result
|
|
42
|
+
reports to flow.""",
|
|
43
|
+
hidden=True
|
|
44
|
+
)
|
|
45
|
+
@click.option(
|
|
46
|
+
"--scanner-timeout",
|
|
47
|
+
hidden=True,
|
|
48
|
+
required=False,
|
|
49
|
+
default=7200,
|
|
50
|
+
type=int,
|
|
51
|
+
help="Set timeout for each scanner"
|
|
52
|
+
)
|
|
53
|
+
@click.option(
|
|
54
|
+
"--parallel-workers",
|
|
55
|
+
hidden=True,
|
|
56
|
+
required=False,
|
|
57
|
+
default=2,
|
|
58
|
+
type=int,
|
|
59
|
+
help="Set max parallel workers"
|
|
60
|
+
)
|
|
61
|
+
@click.option(
|
|
62
|
+
"--deploy-id",
|
|
63
|
+
default=None,
|
|
64
|
+
required=False,
|
|
65
|
+
hidden=True,
|
|
66
|
+
envvar=("CONVISO_DEPLOY_ID", "FLOW_DEPLOY_ID")
|
|
67
|
+
)
|
|
68
|
+
@click.option(
|
|
69
|
+
'--experimental',
|
|
70
|
+
default=False,
|
|
71
|
+
is_flag=True,
|
|
72
|
+
hidden=True,
|
|
73
|
+
help="Enable experimental features.",
|
|
74
|
+
)
|
|
75
|
+
@click.option(
|
|
76
|
+
"--company-id",
|
|
77
|
+
required=False,
|
|
78
|
+
envvar=("CONVISO_COMPANY_ID", "FLOW_COMPANY_ID"),
|
|
79
|
+
help="Company ID on Conviso Platform",
|
|
80
|
+
)
|
|
81
|
+
@click.option(
|
|
82
|
+
'--asset-name',
|
|
83
|
+
required=False,
|
|
84
|
+
envvar=("CONVISO_ASSET_NAME", "FLOW_ASSET_NAME"),
|
|
85
|
+
help="Provides a asset name.",
|
|
86
|
+
)
|
|
87
|
+
@click.option(
|
|
88
|
+
'--from-ast',
|
|
89
|
+
default=False,
|
|
90
|
+
is_flag=True,
|
|
91
|
+
hidden=True,
|
|
92
|
+
help="Internal use only.",
|
|
93
|
+
)
|
|
94
|
+
@click.option(
|
|
95
|
+
'--cleanup',
|
|
96
|
+
default=False,
|
|
97
|
+
is_flag=True,
|
|
98
|
+
show_default=True,
|
|
99
|
+
help="Clean up system resources, including temporary files, stopped containers, unused Docker images and volumes.",
|
|
100
|
+
)
|
|
101
|
+
@click.option(
|
|
102
|
+
'--control-sync-status-id',
|
|
103
|
+
required=False,
|
|
104
|
+
hidden=True,
|
|
105
|
+
help="Control sync status id.",
|
|
106
|
+
)
|
|
107
|
+
@click.option(
|
|
108
|
+
'--control-sync-status-id',
|
|
109
|
+
required=False,
|
|
110
|
+
hidden=True,
|
|
111
|
+
help="Control sync status id.",
|
|
112
|
+
)
|
|
113
|
+
@help_option
|
|
114
|
+
@pass_flow_context
|
|
115
|
+
@click.pass_context
|
|
116
|
+
def run(context, flow_context, asset_id, company_id, repository_dir, send_to_flow, scanner_timeout,
|
|
117
|
+
parallel_workers, deploy_id, experimental, asset_name, from_ast, cleanup, control_sync_status_id):
|
|
118
|
+
"""
|
|
119
|
+
This command will perform IAC analysis at the source code. The analysis
|
|
120
|
+
results can be reported or not to flow application.
|
|
121
|
+
"""
|
|
122
|
+
if not from_ast:
|
|
123
|
+
prepared_context = RequirementsVerifier.prepare_context(clone(context))
|
|
124
|
+
|
|
125
|
+
params_to_copy = [
|
|
126
|
+
'asset_id', 'company_id', 'repository_dir', 'send_to_flow',
|
|
127
|
+
'deploy_id', 'scanner_timeout', 'parallel_workers', 'experimental', 'cleanup'
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
for param_name in params_to_copy:
|
|
131
|
+
context.params[param_name] = (
|
|
132
|
+
locals()[param_name] or prepared_context.params[param_name]
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
perform_command(
|
|
136
|
+
flow_context, context.params['asset_id'], context.params['company_id'], context.params['repository_dir'],
|
|
137
|
+
context.params['send_to_flow'], context.params['scanner_timeout'], context.params['deploy_id'],
|
|
138
|
+
context.params['experimental'], context.params['cleanup'], from_ast, control_sync_status_id
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def deploy_results_to_conviso(
|
|
143
|
+
conviso_api, results_filepaths, asset_id, company_id, flow_context, deploy_id, commit_ref=None, control_sync_status_id=None
|
|
144
|
+
):
|
|
145
|
+
results_context = click.progressbar(results_filepaths, label="Sending results to the Conviso Platform...")
|
|
146
|
+
|
|
147
|
+
with results_context as reports:
|
|
148
|
+
for report_path in reports:
|
|
149
|
+
try:
|
|
150
|
+
with open(report_path) as report_file:
|
|
151
|
+
data = parse_data(json.load(report_file))
|
|
152
|
+
except Exception:
|
|
153
|
+
LOGGER.warn(f"⚠️ Error processing report file. Our technical team has been notified.")
|
|
154
|
+
full_trace = traceback.format_exc()
|
|
155
|
+
log_and_notify_ast_event(
|
|
156
|
+
flow_context=flow_context, company_id=company_id, asset_id=asset_id,
|
|
157
|
+
ast_log=str(full_trace)
|
|
158
|
+
)
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
for issue in data:
|
|
162
|
+
try:
|
|
163
|
+
issue_model = CreateIacFindingInput(
|
|
164
|
+
asset_id=asset_id,
|
|
165
|
+
file_name=issue.get("file_name"),
|
|
166
|
+
vulnerable_line=issue.get("vulnerable_line"),
|
|
167
|
+
title=issue.get("title"),
|
|
168
|
+
description=issue.get("description"),
|
|
169
|
+
severity=issue.get("severity"),
|
|
170
|
+
code_snippet=parse_code_snippet(issue.get("code_snippet")),
|
|
171
|
+
reference=parse_conviso_references(issue.get("reference", "")),
|
|
172
|
+
first_line=issue.get("first_line"),
|
|
173
|
+
category=format_cwe_id(issue.get("cwe")),
|
|
174
|
+
original_issue_id_from_tool=issue.get('hash_issue', []),
|
|
175
|
+
solution=issue.get("solution"),
|
|
176
|
+
control_sync_status_id=control_sync_status_id
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
conviso_api.issues.create_iac(issue_model)
|
|
180
|
+
|
|
181
|
+
except ResponseError as error:
|
|
182
|
+
if error.code == 'RECORD_NOT_UNIQUE':
|
|
183
|
+
continue
|
|
184
|
+
else:
|
|
185
|
+
retry_handler = RetryHandler(
|
|
186
|
+
flow_context=flow_context, company_id=company_id, asset_id=asset_id
|
|
187
|
+
)
|
|
188
|
+
retry_handler.execute_with_retry(conviso_api.issues.create_iac, issue_model)
|
|
189
|
+
except Exception:
|
|
190
|
+
retry_handler = RetryHandler(
|
|
191
|
+
flow_context=flow_context, company_id=company_id, asset_id=asset_id
|
|
192
|
+
)
|
|
193
|
+
retry_handler.execute_with_retry(conviso_api.issues.create_iac, issue_model)
|
|
194
|
+
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def perform_command(
|
|
199
|
+
flow_context, asset_id, company_id, repository_dir, send_to_flow, scanner_timeout,
|
|
200
|
+
deploy_id, experimental, cleanup, from_ast, control_sync_status_id
|
|
201
|
+
):
|
|
202
|
+
|
|
203
|
+
if send_to_flow and experimental and not asset_id:
|
|
204
|
+
raise click.MissingParameter(
|
|
205
|
+
"It is required when sending reports to Conviso Platform using experimental API.",
|
|
206
|
+
param_type="option",
|
|
207
|
+
param_hint="--asset-id",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
REQUIRED_CODEBASE_PATH = '/code'
|
|
212
|
+
IAC_IMAGE_NAME = 'iac_scanner_checkov'
|
|
213
|
+
IAC_SCAN_FILENAME = '/{}.json'.format(IAC_IMAGE_NAME)
|
|
214
|
+
containers_map = {
|
|
215
|
+
IAC_IMAGE_NAME: {
|
|
216
|
+
'repository_dir': repository_dir,
|
|
217
|
+
'repository_name': IAC_IMAGE_NAME,
|
|
218
|
+
'tag': 'unstable',
|
|
219
|
+
'command': [
|
|
220
|
+
'-c', REQUIRED_CODEBASE_PATH,
|
|
221
|
+
'-o', IAC_SCAN_FILENAME,
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
LOGGER.info('💬 Preparing Environment...')
|
|
227
|
+
conviso_rest_api = flow_context.create_conviso_rest_api_client()
|
|
228
|
+
token = conviso_rest_api.docker_registry.get_sast_token()
|
|
229
|
+
scanners_wrapper = ContainerWrapper(
|
|
230
|
+
token=token,
|
|
231
|
+
containers_map=containers_map,
|
|
232
|
+
logger=LOGGER,
|
|
233
|
+
timeout=scanner_timeout
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
LOGGER.info('💬 Starting IaC...')
|
|
237
|
+
scanners_wrapper.run()
|
|
238
|
+
|
|
239
|
+
results_filepaths = []
|
|
240
|
+
for r in scanners_wrapper.scanners:
|
|
241
|
+
report_filepath = r.results
|
|
242
|
+
if report_filepath:
|
|
243
|
+
results_filepaths.append(report_filepath)
|
|
244
|
+
|
|
245
|
+
LOGGER.info('💬 Processing Results...')
|
|
246
|
+
if send_to_flow:
|
|
247
|
+
git_adapater = GitAdapter(repository_dir)
|
|
248
|
+
end_commit = git_adapater.head_commit
|
|
249
|
+
conviso_beta_api = flow_context.create_conviso_api_client_beta()
|
|
250
|
+
|
|
251
|
+
deploy_results_to_conviso(
|
|
252
|
+
conviso_beta_api, results_filepaths, asset_id, company_id, flow_context, deploy_id=deploy_id,
|
|
253
|
+
commit_ref=end_commit, control_sync_status_id=control_sync_status_id
|
|
254
|
+
)
|
|
255
|
+
LOGGER.info('✅ IaC Scan Finished.')
|
|
256
|
+
|
|
257
|
+
if cleanup and from_ast == False:
|
|
258
|
+
LOGGER.info("🧹 Cleaning up ...")
|
|
259
|
+
cleaner = Cleaner()
|
|
260
|
+
cleaner.cleanup()
|
|
261
|
+
|
|
262
|
+
except Exception as e:
|
|
263
|
+
on_http_error(e)
|
|
264
|
+
raise click.ClickException(str(e)) from e
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def parse_conviso_references(references=[]):
|
|
268
|
+
DIVIDER = "\n"
|
|
269
|
+
|
|
270
|
+
return DIVIDER.join(references)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def parse_code_snippet(code_snippet):
|
|
274
|
+
lines = code_snippet.split("\n")
|
|
275
|
+
cleaned_lines = [line.rstrip() for line in lines if line.strip()]
|
|
276
|
+
code_snippet = "\n".join(cleaned_lines)
|
|
277
|
+
|
|
278
|
+
return code_snippet
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def format_cwe_id(cwe_input):
|
|
282
|
+
cwe_str = str(cwe_input).strip()
|
|
283
|
+
|
|
284
|
+
if cwe_str.upper().startswith("CWE-"):
|
|
285
|
+
return cwe_str
|
|
286
|
+
return f"CWE-{cwe_str}"
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def parse_data(sarif_result):
|
|
290
|
+
vulnerabilities = []
|
|
291
|
+
|
|
292
|
+
for result in sarif_result['runs'][0]['results']:
|
|
293
|
+
|
|
294
|
+
vulnerability = {
|
|
295
|
+
'file_name': result['locations'][0]['physicalLocation']['artifactLocation']['uri'],
|
|
296
|
+
'vulnerable_line': result['locations'][0]['physicalLocation']['region']['startLine'],
|
|
297
|
+
'code_snippet': result['locations'][0]['physicalLocation']['contextRegion']['snippet']['text'],
|
|
298
|
+
'title': sarif_result['runs'][0]['tool']['driver']['rules'][result['ruleIndex']]['name'],
|
|
299
|
+
'description': result['message']['text'],
|
|
300
|
+
'severity': result['level'],
|
|
301
|
+
'first_line': result['locations'][0]['physicalLocation']['contextRegion']['startLine'],
|
|
302
|
+
'cwe': result['properties']['cweId'],
|
|
303
|
+
'solution': result['properties']['solution'],
|
|
304
|
+
'hash_issue': result['partialFingerprints']['hashIssueV2']
|
|
305
|
+
}
|
|
306
|
+
vulnerabilities.append(vulnerability)
|
|
307
|
+
|
|
308
|
+
return vulnerabilities
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
EPILOG = '''
|
|
312
|
+
Examples:
|
|
313
|
+
|
|
314
|
+
\b
|
|
315
|
+
1 - Reporting the results to Conviso Platform API:
|
|
316
|
+
1.1 - Running an analysis at all commit range:
|
|
317
|
+
$ export CONVISO_API_KEY='your-api-key'
|
|
318
|
+
$ {command}
|
|
319
|
+
|
|
320
|
+
''' # noqa: E501
|
|
321
|
+
|
|
322
|
+
SHORT_HELP = "Perform Infrastructure Code analysis"
|
|
323
|
+
|
|
324
|
+
command = 'conviso iac run'
|
|
325
|
+
run.short_help = SHORT_HELP
|
|
326
|
+
run.epilog = EPILOG.format(
|
|
327
|
+
command=command,
|
|
328
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from convisoappsec.flowcli.common import CreateDeployException
|
|
3
|
+
from convisoappsec.logger import LOGGER
|
|
4
|
+
from convisoappsec.flowcli.companies.ls import Companies
|
|
5
|
+
from convisoappsec.flow.graphql_api.v1.models.asset import AssetInput
|
|
6
|
+
from convisoappsec.common.git_data_parser import GitDataParser
|
|
7
|
+
from .context import pass_flow_context
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RequirementsVerifier:
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
@pass_flow_context
|
|
14
|
+
def list_assets(flow_context, company_id, asset_name, scan_type):
|
|
15
|
+
conviso_api = flow_context.create_conviso_graphql_client()
|
|
16
|
+
|
|
17
|
+
asset_model = AssetInput(
|
|
18
|
+
int(company_id),
|
|
19
|
+
asset_name,
|
|
20
|
+
scan_type
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return conviso_api.assets.list_assets(asset_model)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
@pass_flow_context
|
|
27
|
+
def create_asset(flow_context, company_id, asset_name, scan_type):
|
|
28
|
+
conviso_api = flow_context.create_conviso_graphql_client()
|
|
29
|
+
|
|
30
|
+
asset_model = AssetInput(
|
|
31
|
+
int(company_id),
|
|
32
|
+
asset_name,
|
|
33
|
+
scan_type
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return conviso_api.assets.create_asset(asset_model)
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def sarif_asset_assignment(context, asset):
|
|
40
|
+
""" assignment asset when is a sarif import """
|
|
41
|
+
context.params['asset_id'] = asset['id']
|
|
42
|
+
context.params['experimental'] = True
|
|
43
|
+
|
|
44
|
+
return context
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def find_or_create_asset(context, company_id, old_name, new_name):
|
|
48
|
+
""" Method to find or create asset on conviso platform """
|
|
49
|
+
try:
|
|
50
|
+
existing_assets = RequirementsVerifier.list_assets(company_id, new_name, 'SAST')
|
|
51
|
+
if not existing_assets:
|
|
52
|
+
existing_assets = RequirementsVerifier.list_assets(company_id, old_name, 'SAST')
|
|
53
|
+
for asset in existing_assets:
|
|
54
|
+
if asset['name'] == old_name or asset['name'] == new_name:
|
|
55
|
+
LOGGER.info('✅ Asset found...')
|
|
56
|
+
context.params['asset_name'] = asset['name']
|
|
57
|
+
return [asset]
|
|
58
|
+
LOGGER.info('💬 Asset not found; creating...')
|
|
59
|
+
new_asset = RequirementsVerifier.create_asset(company_id, new_name, 'SAST')
|
|
60
|
+
context.params['asset_name'] = new_name
|
|
61
|
+
return [new_asset]
|
|
62
|
+
except Exception as e:
|
|
63
|
+
raise Exception("Error: {}".format(e))
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def create_asset_with_custom_name(context, company_id, asset_name):
|
|
67
|
+
""" Create an asset with custom name pass with a custom name """
|
|
68
|
+
if not asset_name or not asset_name.strip(): # Check for None or blank string
|
|
69
|
+
raise ValueError("Asset name cannot be None or blank.")
|
|
70
|
+
|
|
71
|
+
# we need to verify if already has an asset with the name provided.
|
|
72
|
+
# because graphql will return an error if already has.
|
|
73
|
+
existing_asset = RequirementsVerifier.list_assets(company_id, asset_name, 'SAST')
|
|
74
|
+
|
|
75
|
+
if not existing_asset:
|
|
76
|
+
LOGGER.info("💬 Asset not found; creating with name {}...".format(asset_name))
|
|
77
|
+
asset = RequirementsVerifier.create_asset(company_id, asset_name, 'SAST')
|
|
78
|
+
else:
|
|
79
|
+
LOGGER.info('✅ Asset found...')
|
|
80
|
+
asset = existing_asset[0]
|
|
81
|
+
|
|
82
|
+
context.params['asset_name'] = asset_name
|
|
83
|
+
|
|
84
|
+
return asset
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
@pass_flow_context
|
|
88
|
+
def prepare_context(flow_context, context, from_ast=False):
|
|
89
|
+
""" Due to the new vulnerability management we need to do some checks before continuing the flow """
|
|
90
|
+
|
|
91
|
+
if from_ast is True:
|
|
92
|
+
context.params['from_ast'] = True
|
|
93
|
+
|
|
94
|
+
companies = Companies()
|
|
95
|
+
company_id = context.params['company_id']
|
|
96
|
+
|
|
97
|
+
if company_id is not None:
|
|
98
|
+
companies_filtered = [companies.ls(flow_context, company_id=company_id)]
|
|
99
|
+
else:
|
|
100
|
+
companies_filtered = companies.ls(flow_context)
|
|
101
|
+
|
|
102
|
+
if len(companies_filtered) > 1:
|
|
103
|
+
raise CreateDeployException(
|
|
104
|
+
"❌ Deploy not created. You have access to multiple companies; please specify one using CONVISO_COMPANY_ID."
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
company = companies_filtered[0]
|
|
108
|
+
company_id = company['id']
|
|
109
|
+
|
|
110
|
+
if context.params['asset_name'] is not None:
|
|
111
|
+
# if user use --asset-name param or envvar CONVISO_ASSET_NAME, FLOW_ASSET_NAME
|
|
112
|
+
asset_name = context.params['asset_name']
|
|
113
|
+
asset = RequirementsVerifier.create_asset_with_custom_name(context, company_id, asset_name)
|
|
114
|
+
else:
|
|
115
|
+
pattern = r"\([^)]*\)" # eliminating what is in parentheses
|
|
116
|
+
old_asset_name = GitDataParser(context.params['repository_dir']).parse_name()
|
|
117
|
+
new_asset_name = re.sub(pattern, '', old_asset_name).strip()
|
|
118
|
+
|
|
119
|
+
assets = RequirementsVerifier.find_or_create_asset(context, company_id, old_asset_name, new_asset_name)
|
|
120
|
+
asset = assets[0]
|
|
121
|
+
|
|
122
|
+
if 'input_file' in context.params:
|
|
123
|
+
# sarif only uses assets, not requiring the creation of a project.
|
|
124
|
+
RequirementsVerifier.sarif_asset_assignment(context, asset)
|
|
125
|
+
|
|
126
|
+
return context
|
|
127
|
+
|
|
128
|
+
context.params['asset_id'] = asset['id']
|
|
129
|
+
context.params['experimental'] = True
|
|
130
|
+
context.params['company_id'] = company_id
|
|
131
|
+
|
|
132
|
+
return context
|
|
@@ -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 sast():
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
sast.add_command(run)
|
|
14
|
+
|
|
15
|
+
sast.epilog = '''
|
|
16
|
+
Run flow sast COMMAND --help for more information on a command.
|
|
17
|
+
'''
|