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,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
|