devsecops-engine-tools 1.60.0__py3-none-any.whl → 1.61.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.
Potentially problematic release.
This version of devsecops-engine-tools might be problematic. Click here for more details.
- devsecops_engine_tools/engine_sast/engine_iac/src/domain/model/context_iac.py +2 -1
- devsecops_engine_tools/engine_sast/engine_iac/src/domain/model/gateways/tool_gateway.py +4 -5
- devsecops_engine_tools/engine_sast/engine_iac/src/domain/usecases/iac_scan.py +14 -12
- devsecops_engine_tools/engine_sast/engine_iac/src/infrastructure/driven_adapters/checkov/checkov_deserealizator.py +11 -8
- devsecops_engine_tools/engine_sast/engine_iac/src/infrastructure/driven_adapters/checkov/checkov_tool.py +230 -206
- devsecops_engine_tools/engine_sast/engine_iac/src/infrastructure/driven_adapters/kics/kics_tool.py +143 -93
- devsecops_engine_tools/engine_sast/engine_iac/src/infrastructure/driven_adapters/kubescape/kubescape_tool.py +80 -65
- devsecops_engine_tools/engine_sca/engine_container/src/domain/model/context_container.py +2 -1
- devsecops_engine_tools/engine_sca/engine_container/src/domain/model/gateways/deserealizator_gateway.py +7 -2
- devsecops_engine_tools/engine_sca/engine_container/src/domain/usecases/container_sca_scan.py +53 -52
- devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/prisma_cloud/prisma_deserialize_output.py +3 -3
- devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/trivy_tool/trivy_deserialize_output.py +50 -31
- devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/entry_points/entry_point_tool.py +9 -5
- devsecops_engine_tools/version.py +1 -1
- {devsecops_engine_tools-1.60.0.dist-info → devsecops_engine_tools-1.61.0.dist-info}/METADATA +1 -1
- {devsecops_engine_tools-1.60.0.dist-info → devsecops_engine_tools-1.61.0.dist-info}/RECORD +19 -19
- {devsecops_engine_tools-1.60.0.dist-info → devsecops_engine_tools-1.61.0.dist-info}/WHEEL +0 -0
- {devsecops_engine_tools-1.60.0.dist-info → devsecops_engine_tools-1.61.0.dist-info}/entry_points.txt +0 -0
- {devsecops_engine_tools-1.60.0.dist-info → devsecops_engine_tools-1.61.0.dist-info}/top_level.txt +0 -0
devsecops_engine_tools/engine_sast/engine_iac/src/infrastructure/driven_adapters/kics/kics_tool.py
CHANGED
|
@@ -7,7 +7,7 @@ from devsecops_engine_tools.engine_sast.engine_iac.src.domain.model.gateways.too
|
|
|
7
7
|
ToolGateway,
|
|
8
8
|
)
|
|
9
9
|
from devsecops_engine_tools.engine_sast.engine_iac.src.infrastructure.driven_adapters.kics.kics_deserealizator import (
|
|
10
|
-
KicsDeserealizator
|
|
10
|
+
KicsDeserealizator,
|
|
11
11
|
)
|
|
12
12
|
from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
|
|
13
13
|
from devsecops_engine_tools.engine_utilities import settings
|
|
@@ -35,10 +35,90 @@ class KicsTool(ToolGateway):
|
|
|
35
35
|
"googledeploymentmanager": "GoogleDeploymentManager",
|
|
36
36
|
"knative": "Knative",
|
|
37
37
|
"pulumi": "Pulumi",
|
|
38
|
-
"serverlessfw": "ServerlessFW"
|
|
38
|
+
"serverlessfw": "ServerlessFW",
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
def
|
|
41
|
+
def run_tool(
|
|
42
|
+
self, config_tool, folders_to_scan, work_folder, platform_to_scan, **kwargs
|
|
43
|
+
):
|
|
44
|
+
kics_version = config_tool[self.TOOL_KICS]["CLI_VERSION"]
|
|
45
|
+
path_kics = config_tool[self.TOOL_KICS]["PATH_KICS"]
|
|
46
|
+
download_kics_assets = config_tool[self.TOOL_KICS]["DOWNLOAD_KICS_ASSETS"]
|
|
47
|
+
|
|
48
|
+
os_platform = platform.system()
|
|
49
|
+
path_kics = (
|
|
50
|
+
path_kics.replace("/", "\\") if os_platform == "Windows" else path_kics
|
|
51
|
+
)
|
|
52
|
+
work_folder = (
|
|
53
|
+
work_folder.replace("/", "\\") if os_platform == "Windows" else work_folder
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
command_prefix = (
|
|
57
|
+
f"{work_folder}\\{path_kics}.exe"
|
|
58
|
+
if os_platform == "Windows"
|
|
59
|
+
else f"{work_folder}/{path_kics}"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if not self._validate_kics(command_prefix):
|
|
63
|
+
logger.info("KICS binary not found or invalid, downloading assets...")
|
|
64
|
+
|
|
65
|
+
if download_kics_assets:
|
|
66
|
+
self._get_assets(kics_version, work_folder)
|
|
67
|
+
|
|
68
|
+
queries = self._get_queries(config_tool, platform_to_scan)
|
|
69
|
+
self._execute_kics(
|
|
70
|
+
folders_to_scan,
|
|
71
|
+
command_prefix,
|
|
72
|
+
platform_to_scan,
|
|
73
|
+
work_folder,
|
|
74
|
+
os_platform,
|
|
75
|
+
queries,
|
|
76
|
+
)
|
|
77
|
+
data = self._load_results(work_folder, queries)
|
|
78
|
+
|
|
79
|
+
if data:
|
|
80
|
+
kics_deserealizator = KicsDeserealizator()
|
|
81
|
+
total_vulnerabilities = kics_deserealizator.calculate_total_vulnerabilities(
|
|
82
|
+
data
|
|
83
|
+
)
|
|
84
|
+
path_file = os.path.join(work_folder, "results.json")
|
|
85
|
+
|
|
86
|
+
if total_vulnerabilities == 0:
|
|
87
|
+
return [], path_file
|
|
88
|
+
|
|
89
|
+
filtered_results = kics_deserealizator.get_findings(data)
|
|
90
|
+
finding_list = kics_deserealizator.get_list_finding(filtered_results)
|
|
91
|
+
|
|
92
|
+
return finding_list, path_file
|
|
93
|
+
return [], None
|
|
94
|
+
|
|
95
|
+
def get_iac_context_from_results(self, path_file_results):
|
|
96
|
+
# TODO: Implement this method
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
def _validate_kics(self, command_prefix):
|
|
100
|
+
try:
|
|
101
|
+
result = subprocess.run(
|
|
102
|
+
[command_prefix, "version"], capture_output=True, text=True
|
|
103
|
+
)
|
|
104
|
+
if result.returncode == 0:
|
|
105
|
+
return True
|
|
106
|
+
else:
|
|
107
|
+
logger.error(f"KICS binary not valid: {result.stderr}")
|
|
108
|
+
return False
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.error(f"Error validating KICS binary: {e}")
|
|
111
|
+
|
|
112
|
+
def _get_assets(self, kics_version, work_folder):
|
|
113
|
+
name_zip = "assets_compressed.zip"
|
|
114
|
+
assets_url = f"https://github.com/Checkmarx/kics/releases/download/v{kics_version}/extracted-info.zip"
|
|
115
|
+
self._download(name_zip, assets_url)
|
|
116
|
+
|
|
117
|
+
directory_assets = f"{work_folder}/kics-devsecops"
|
|
118
|
+
utils = Utils()
|
|
119
|
+
utils.unzip_file(name_zip, directory_assets)
|
|
120
|
+
|
|
121
|
+
def _download(self, file, url):
|
|
42
122
|
try:
|
|
43
123
|
response = requests.get(url)
|
|
44
124
|
with open(file, "wb") as f:
|
|
@@ -46,13 +126,39 @@ class KicsTool(ToolGateway):
|
|
|
46
126
|
except Exception as ex:
|
|
47
127
|
logger.error(f"An error ocurred downloading {file} {ex}")
|
|
48
128
|
|
|
49
|
-
def
|
|
129
|
+
def _get_queries(self, config_tool, platform_to_scan):
|
|
130
|
+
try:
|
|
131
|
+
queries = []
|
|
132
|
+
for platform in platform_to_scan:
|
|
133
|
+
platform = platform.strip().upper()
|
|
134
|
+
if f"RULES_{platform}" not in config_tool[self.TOOL_KICS]["RULES"]:
|
|
135
|
+
logger.error(f"Platform {platform} not found in RULES")
|
|
136
|
+
queries = [
|
|
137
|
+
{key: [value["checkID"], value["overrideID"]],
|
|
138
|
+
"severity": value["severity"]}
|
|
139
|
+
for key, value in config_tool[self.TOOL_KICS]["RULES"][f"RULES_{platform}"].items()
|
|
140
|
+
]
|
|
141
|
+
return queries
|
|
142
|
+
except Exception as e:
|
|
143
|
+
logger.error(f"Error writing queries file: {e}")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _execute_kics(
|
|
147
|
+
self,
|
|
148
|
+
folders_to_scan,
|
|
149
|
+
prefix,
|
|
150
|
+
platform_to_scan,
|
|
151
|
+
work_folder,
|
|
152
|
+
os_platform,
|
|
153
|
+
queries,
|
|
154
|
+
):
|
|
50
155
|
folders = ','.join(folders_to_scan)
|
|
51
|
-
queries = ','.join(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
156
|
+
queries = ','.join(
|
|
157
|
+
uuid for query in queries for uuid in list(query.values())[0]
|
|
158
|
+
) if queries else ""
|
|
159
|
+
mapped_platforms = [
|
|
160
|
+
self.scan_type_platform_mapping.get(platform.lower(), platform)
|
|
161
|
+
for platform in platform_to_scan ] if platform_to_scan != ["all"] else list(self.scan_type_platform_mapping.values())
|
|
56
162
|
platforms = ','.join(mapped_platforms)
|
|
57
163
|
|
|
58
164
|
command = [
|
|
@@ -65,30 +171,47 @@ class KicsTool(ToolGateway):
|
|
|
65
171
|
"--include-queries",
|
|
66
172
|
queries,
|
|
67
173
|
"-q",
|
|
68
|
-
|
|
69
|
-
|
|
174
|
+
(
|
|
175
|
+
f"{work_folder}\\kics-devsecops\\assets\\queries"
|
|
176
|
+
if os_platform == "Windows"
|
|
177
|
+
else f"{work_folder}/kics-devsecops/assets/queries"
|
|
178
|
+
),
|
|
70
179
|
"--report-formats",
|
|
71
180
|
"json",
|
|
72
181
|
"-o",
|
|
73
|
-
work_folder
|
|
182
|
+
work_folder,
|
|
74
183
|
]
|
|
75
184
|
try:
|
|
76
185
|
subprocess.run(command, capture_output=True)
|
|
77
186
|
except subprocess.CalledProcessError as e:
|
|
78
187
|
logger.error(f"Error during KICS execution: {e}")
|
|
79
|
-
|
|
80
|
-
|
|
188
|
+
return []
|
|
189
|
+
|
|
190
|
+
def _load_results(self, work_folder, queries):
|
|
81
191
|
try:
|
|
82
192
|
results_path = os.path.join(work_folder, "results.json")
|
|
83
193
|
with open(results_path, "r") as f:
|
|
84
194
|
data = json.load(f)
|
|
85
195
|
|
|
196
|
+
query_id_to_info = {}
|
|
197
|
+
for query in queries:
|
|
198
|
+
severity = query.get("severity")
|
|
199
|
+
for custom_id, ids in query.items():
|
|
200
|
+
if custom_id == "severity":
|
|
201
|
+
continue
|
|
202
|
+
for query_id in ids:
|
|
203
|
+
if query_id != "":
|
|
204
|
+
query_id_to_info[query_id] = {
|
|
205
|
+
"severity": severity,
|
|
206
|
+
"custom_id": custom_id
|
|
207
|
+
}
|
|
208
|
+
|
|
86
209
|
for finding in data.get("queries", []):
|
|
87
|
-
|
|
88
|
-
if
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
210
|
+
query_id = finding.get("query_id")
|
|
211
|
+
if query_id in query_id_to_info:
|
|
212
|
+
info = query_id_to_info[query_id]
|
|
213
|
+
finding["severity"] = info["severity"].upper()
|
|
214
|
+
finding["custom_id"] = info["custom_id"]
|
|
92
215
|
|
|
93
216
|
with open(results_path, "w") as f:
|
|
94
217
|
json.dump(data, f, indent=4)
|
|
@@ -96,77 +219,4 @@ class KicsTool(ToolGateway):
|
|
|
96
219
|
return data
|
|
97
220
|
except Exception as ex:
|
|
98
221
|
logger.error(f"An error occurred loading or modifying KICS results {ex}")
|
|
99
|
-
return None
|
|
100
|
-
|
|
101
|
-
def get_assets(self, kics_version, work_folder):
|
|
102
|
-
name_zip = "assets_compressed.zip"
|
|
103
|
-
assets_url = f"https://github.com/Checkmarx/kics/releases/download/v{kics_version}/extracted-info.zip"
|
|
104
|
-
self.download(name_zip, assets_url)
|
|
105
|
-
|
|
106
|
-
directory_assets = f"{work_folder}/kics-devsecops"
|
|
107
|
-
utils = Utils()
|
|
108
|
-
utils.unzip_file(name_zip, directory_assets)
|
|
109
|
-
|
|
110
|
-
def validate_kics(self, command_prefix):
|
|
111
|
-
try:
|
|
112
|
-
result = subprocess.run([command_prefix, "version"], capture_output=True, text=True)
|
|
113
|
-
if result.returncode == 0:
|
|
114
|
-
return True
|
|
115
|
-
else:
|
|
116
|
-
logger.error(f"KICS binary not valid: {result.stderr}")
|
|
117
|
-
return False
|
|
118
|
-
except Exception as e:
|
|
119
|
-
logger.error(f"Error validating KICS binary: {e}")
|
|
120
|
-
|
|
121
|
-
def get_queries(self, config_tool, platform_to_scan):
|
|
122
|
-
try:
|
|
123
|
-
queries = []
|
|
124
|
-
for platform in platform_to_scan:
|
|
125
|
-
platform = platform.strip().upper()
|
|
126
|
-
if f"RULES_{platform}" not in config_tool[self.TOOL_KICS]["RULES"]:
|
|
127
|
-
logger.error(f"Platform {platform} not found in RULES")
|
|
128
|
-
queries = [{key: value["checkID"]} for key, value in config_tool[self.TOOL_KICS]["RULES"][f"RULES_{platform}"].items()]
|
|
129
|
-
return queries
|
|
130
|
-
except Exception as e:
|
|
131
|
-
logger.error(f"Error writing queries file: {e}")
|
|
132
|
-
|
|
133
|
-
def run_tool(
|
|
134
|
-
self, config_tool, folders_to_scan, work_folder, platform_to_scan, **kwargs
|
|
135
|
-
):
|
|
136
|
-
kics_version = config_tool[self.TOOL_KICS]["CLI_VERSION"]
|
|
137
|
-
path_kics = config_tool[self.TOOL_KICS]["PATH_KICS"]
|
|
138
|
-
download_kics_assets = config_tool[self.TOOL_KICS]["DOWNLOAD_KICS_ASSETS"]
|
|
139
|
-
|
|
140
|
-
os_platform = platform.system()
|
|
141
|
-
path_kics = path_kics.replace("/", "\\") if os_platform == "Windows" else path_kics
|
|
142
|
-
work_folder = work_folder.replace("/", "\\") if os_platform == "Windows" else work_folder
|
|
143
|
-
|
|
144
|
-
command_prefix = f"{work_folder}\\{path_kics}.exe" if os_platform == "Windows" else f"{work_folder}/{path_kics}"
|
|
145
|
-
|
|
146
|
-
if not self.validate_kics(command_prefix):
|
|
147
|
-
logger.info("KICS binary not found or invalid, downloading assets...")
|
|
148
|
-
|
|
149
|
-
if download_kics_assets:
|
|
150
|
-
self.get_assets(kics_version, work_folder)
|
|
151
|
-
|
|
152
|
-
queries = self.get_queries(config_tool, platform_to_scan)
|
|
153
|
-
self.execute_kics(folders_to_scan, command_prefix, platform_to_scan, work_folder, os_platform, queries)
|
|
154
|
-
data = self.load_results(work_folder, queries)
|
|
155
|
-
|
|
156
|
-
if data:
|
|
157
|
-
kics_deserealizator = KicsDeserealizator()
|
|
158
|
-
total_vulnerabilities = kics_deserealizator.calculate_total_vulnerabilities(data)
|
|
159
|
-
path_file = os.path.join(work_folder, "results.json")
|
|
160
|
-
|
|
161
|
-
if total_vulnerabilities == 0:
|
|
162
|
-
return [], path_file
|
|
163
|
-
|
|
164
|
-
filtered_results = kics_deserealizator.get_findings(data)
|
|
165
|
-
finding_list = kics_deserealizator.get_list_finding(filtered_results)
|
|
166
|
-
|
|
167
|
-
return finding_list, path_file
|
|
168
|
-
return [], None
|
|
169
|
-
|
|
170
|
-
def get_iac_context_from_results(self, path_file_results):
|
|
171
|
-
#TODO: Implement this method
|
|
172
|
-
pass
|
|
222
|
+
return None
|
|
@@ -18,15 +18,61 @@ logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
|
|
|
18
18
|
|
|
19
19
|
class KubescapeTool(ToolGateway):
|
|
20
20
|
|
|
21
|
-
def
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
def run_tool(self, config_tool, folders_to_scan, platform_to_scan, **kwargs):
|
|
22
|
+
|
|
23
|
+
if folders_to_scan and "k8s" in platform_to_scan:
|
|
24
|
+
|
|
25
|
+
kubescape_version = config_tool["KUBESCAPE"]["VERSION"]
|
|
26
|
+
os_platform = platform.system()
|
|
27
|
+
base_url = f"https://github.com/kubescape/kubescape/releases/download/v{kubescape_version}/"
|
|
28
|
+
command_prefix = self._select_operative_system(os_platform, base_url)
|
|
29
|
+
self._execute_kubescape(folders_to_scan, command_prefix)
|
|
30
|
+
|
|
31
|
+
json_name = "results_kubescape.json"
|
|
32
|
+
data = self._load_json(json_name)
|
|
33
|
+
|
|
34
|
+
if not data:
|
|
35
|
+
return [], None
|
|
36
|
+
else:
|
|
37
|
+
kubescape_deserealizator = KubescapeDeserealizator()
|
|
38
|
+
result_extracted_data = (
|
|
39
|
+
kubescape_deserealizator.extract_failed_controls(data)
|
|
40
|
+
)
|
|
41
|
+
finding_list = kubescape_deserealizator.get_list_finding(
|
|
42
|
+
result_extracted_data
|
|
43
|
+
)
|
|
44
|
+
path_results = os.path.abspath(json_name)
|
|
45
|
+
return finding_list, path_results
|
|
46
|
+
else:
|
|
47
|
+
return [], None
|
|
48
|
+
|
|
49
|
+
def get_iac_context_from_results(self, path_file_results):
|
|
50
|
+
# TODO: Implement this method
|
|
51
|
+
pass
|
|
28
52
|
|
|
29
|
-
def
|
|
53
|
+
def _select_operative_system(self, os_platform, base_url):
|
|
54
|
+
if os_platform == "Linux":
|
|
55
|
+
distro_name = distro.name()
|
|
56
|
+
if distro_name == "Ubuntu":
|
|
57
|
+
file = "kubescape-ubuntu-latest"
|
|
58
|
+
self._install_tool(file, base_url + file)
|
|
59
|
+
return f"./{file}"
|
|
60
|
+
else:
|
|
61
|
+
logger.warning(f"{distro_name} is not supported.")
|
|
62
|
+
return None
|
|
63
|
+
elif os_platform == "Windows":
|
|
64
|
+
file = "kubescape-windows-latest.exe"
|
|
65
|
+
self._install_tool_windows(file, base_url + file)
|
|
66
|
+
return f"./{file}"
|
|
67
|
+
elif os_platform == "Darwin":
|
|
68
|
+
file = "kubescape-macos-latest"
|
|
69
|
+
self._install_tool(file, base_url + file)
|
|
70
|
+
return f"./{file}"
|
|
71
|
+
else:
|
|
72
|
+
logger.warning(f"{os_platform} is not supported.")
|
|
73
|
+
return [], None
|
|
74
|
+
|
|
75
|
+
def _install_tool(self, file, url):
|
|
30
76
|
installed = subprocess.run(
|
|
31
77
|
["which", f"./{file}"],
|
|
32
78
|
stdout=subprocess.PIPE,
|
|
@@ -34,13 +80,13 @@ class KubescapeTool(ToolGateway):
|
|
|
34
80
|
)
|
|
35
81
|
if installed.returncode == 1:
|
|
36
82
|
try:
|
|
37
|
-
self.
|
|
83
|
+
self._download_tool(file, url)
|
|
38
84
|
subprocess.run(["chmod", "+x", f"./{file}"])
|
|
39
85
|
|
|
40
86
|
except Exception as e:
|
|
41
87
|
logger.error(f"Error installing Kubescape: {e}")
|
|
42
88
|
|
|
43
|
-
def
|
|
89
|
+
def _install_tool_windows(self, file, url):
|
|
44
90
|
try:
|
|
45
91
|
subprocess.run(
|
|
46
92
|
[f"./{file}", "version"],
|
|
@@ -49,20 +95,39 @@ class KubescapeTool(ToolGateway):
|
|
|
49
95
|
)
|
|
50
96
|
except:
|
|
51
97
|
try:
|
|
52
|
-
self.
|
|
98
|
+
self._download_tool(file, url)
|
|
53
99
|
|
|
54
100
|
except Exception as e:
|
|
55
101
|
logger.error(f"Error installing Kubescape: {e}")
|
|
56
102
|
|
|
57
|
-
def
|
|
58
|
-
|
|
59
|
-
|
|
103
|
+
def _download_tool(self, file, url):
|
|
104
|
+
try:
|
|
105
|
+
response = requests.get(url, allow_redirects=True)
|
|
106
|
+
with open(file, "wb") as binary_file:
|
|
107
|
+
binary_file.write(response.content)
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.error(f"Error downloading Kubescape: {e}")
|
|
110
|
+
|
|
111
|
+
def _execute_kubescape(self, folders_to_scan, prefix):
|
|
112
|
+
command = (
|
|
113
|
+
[prefix, "scan"]
|
|
114
|
+
+ folders_to_scan
|
|
115
|
+
+ [
|
|
116
|
+
"--format",
|
|
117
|
+
"json",
|
|
118
|
+
"--format-version",
|
|
119
|
+
"v2",
|
|
120
|
+
"--output",
|
|
121
|
+
"results_kubescape.json",
|
|
122
|
+
"-v",
|
|
123
|
+
]
|
|
124
|
+
)
|
|
60
125
|
try:
|
|
61
126
|
subprocess.run(command, capture_output=True)
|
|
62
127
|
except subprocess.CalledProcessError as e:
|
|
63
128
|
logger.error(f"Error during Kubescape execution: {e}")
|
|
64
129
|
|
|
65
|
-
def
|
|
130
|
+
def _load_json(self, json_name):
|
|
66
131
|
try:
|
|
67
132
|
with open(json_name) as file:
|
|
68
133
|
return json.load(file)
|
|
@@ -71,53 +136,3 @@ class KubescapeTool(ToolGateway):
|
|
|
71
136
|
except json.JSONDecodeError:
|
|
72
137
|
logger.error("The JSON result is empty.")
|
|
73
138
|
return None
|
|
74
|
-
|
|
75
|
-
def select_operative_system(self, os_platform, base_url):
|
|
76
|
-
if os_platform == "Linux":
|
|
77
|
-
distro_name = distro.name()
|
|
78
|
-
if distro_name == "Ubuntu":
|
|
79
|
-
file = "kubescape-ubuntu-latest"
|
|
80
|
-
self.install_tool(file, base_url + file)
|
|
81
|
-
return f"./{file}"
|
|
82
|
-
else:
|
|
83
|
-
logger.warning(f"{distro_name} is not supported.")
|
|
84
|
-
return None
|
|
85
|
-
elif os_platform == "Windows":
|
|
86
|
-
file = "kubescape-windows-latest.exe"
|
|
87
|
-
self.install_tool_windows(file, base_url + file)
|
|
88
|
-
return f"./{file}"
|
|
89
|
-
elif os_platform == "Darwin":
|
|
90
|
-
file = "kubescape-macos-latest"
|
|
91
|
-
self.install_tool(file, base_url + file)
|
|
92
|
-
return f"./{file}"
|
|
93
|
-
else:
|
|
94
|
-
logger.warning(f"{os_platform} is not supported.")
|
|
95
|
-
return [], None
|
|
96
|
-
|
|
97
|
-
def run_tool(self, config_tool, folders_to_scan, platform_to_scan, **kwargs):
|
|
98
|
-
|
|
99
|
-
if folders_to_scan and "k8s" in platform_to_scan:
|
|
100
|
-
|
|
101
|
-
kubescape_version = config_tool["KUBESCAPE"]["VERSION"]
|
|
102
|
-
os_platform = platform.system()
|
|
103
|
-
base_url = f"https://github.com/kubescape/kubescape/releases/download/v{kubescape_version}/"
|
|
104
|
-
command_prefix = self.select_operative_system(os_platform, base_url)
|
|
105
|
-
self.execute_kubescape(folders_to_scan, command_prefix)
|
|
106
|
-
|
|
107
|
-
json_name = "results_kubescape.json"
|
|
108
|
-
data = self.load_json(json_name)
|
|
109
|
-
|
|
110
|
-
if not data:
|
|
111
|
-
return [], None
|
|
112
|
-
else:
|
|
113
|
-
kubescape_deserealizator = KubescapeDeserealizator()
|
|
114
|
-
result_extracted_data = kubescape_deserealizator.extract_failed_controls(data)
|
|
115
|
-
finding_list = kubescape_deserealizator.get_list_finding(result_extracted_data)
|
|
116
|
-
path_results = os.path.abspath(json_name)
|
|
117
|
-
return finding_list, path_results
|
|
118
|
-
else:
|
|
119
|
-
return [], None
|
|
120
|
-
|
|
121
|
-
def get_iac_context_from_results(self, path_file_results):
|
|
122
|
-
#TODO: Implement this method
|
|
123
|
-
pass
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import List, Optional
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
@dataclass
|
|
5
6
|
class ContextContainer:
|
|
6
7
|
cve_id: str
|
|
@@ -20,4 +21,4 @@ class ContextContainer:
|
|
|
20
21
|
published_date: Optional[str]
|
|
21
22
|
last_modified_date: Optional[str]
|
|
22
23
|
references: Optional[List[str]]
|
|
23
|
-
source_tool: str
|
|
24
|
+
source_tool: str
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from abc import ABCMeta, abstractmethod
|
|
2
2
|
from devsecops_engine_tools.engine_core.src.domain.model.finding import Finding
|
|
3
|
-
from devsecops_engine_tools.engine_sca.engine_container.src.domain.model.context_container import
|
|
3
|
+
from devsecops_engine_tools.engine_sca.engine_container.src.domain.model.context_container import (
|
|
4
|
+
ContextContainer,
|
|
5
|
+
)
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class DeseralizatorGateway(metaclass=ABCMeta):
|
|
@@ -8,5 +10,8 @@ class DeseralizatorGateway(metaclass=ABCMeta):
|
|
|
8
10
|
def get_list_findings(self, results_scan_list: list) -> "list[Finding]":
|
|
9
11
|
"Deseralizator"
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def get_container_context_from_results(
|
|
15
|
+
self, results_scan_list: list
|
|
16
|
+
) -> "list[ContextContainer]":
|
|
12
17
|
"Deseralizator"
|
devsecops_engine_tools/engine_sca/engine_container/src/domain/usecases/container_sca_scan.py
CHANGED
|
@@ -38,52 +38,6 @@ class ContainerScaScan:
|
|
|
38
38
|
self.pipeline_name = pipeline_name
|
|
39
39
|
self.context = context
|
|
40
40
|
|
|
41
|
-
def get_image(self, image_to_scan):
|
|
42
|
-
"""
|
|
43
|
-
Process the list of images.
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
list: List of processed images.
|
|
47
|
-
"""
|
|
48
|
-
return self.tool_images.list_images(image_to_scan)
|
|
49
|
-
|
|
50
|
-
def get_base_image(self, matching_image):
|
|
51
|
-
"""
|
|
52
|
-
Process the base image.
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
String: base image.
|
|
56
|
-
"""
|
|
57
|
-
return self.tool_images.get_base_image(matching_image)
|
|
58
|
-
|
|
59
|
-
def get_images_already_scanned(self):
|
|
60
|
-
"""
|
|
61
|
-
Create images scanned file if it does not exist and get the images that have already been scanned.
|
|
62
|
-
"""
|
|
63
|
-
scanned_images_file = os.path.join(os.getcwd(), "scanned_images.txt")
|
|
64
|
-
if not os.path.exists(scanned_images_file):
|
|
65
|
-
open(scanned_images_file, "w").close()
|
|
66
|
-
with open(scanned_images_file, "r") as file:
|
|
67
|
-
images_scanned = file.read().splitlines()
|
|
68
|
-
return images_scanned
|
|
69
|
-
|
|
70
|
-
def set_image_scanned(self, result_file):
|
|
71
|
-
"""
|
|
72
|
-
Write in scanned_images.txt the result file
|
|
73
|
-
"""
|
|
74
|
-
with open("scanned_images.txt", "a") as file:
|
|
75
|
-
file.write(result_file + "\n")
|
|
76
|
-
|
|
77
|
-
def validate_base_image_date(self, matching_image, referenced_date):
|
|
78
|
-
"""
|
|
79
|
-
Process the base image date validation.
|
|
80
|
-
|
|
81
|
-
Returns:
|
|
82
|
-
string: base image date.
|
|
83
|
-
"""
|
|
84
|
-
return self.tool_images.validate_base_image_date(
|
|
85
|
-
matching_image, referenced_date
|
|
86
|
-
)
|
|
87
41
|
|
|
88
42
|
def process(self):
|
|
89
43
|
"""
|
|
@@ -94,15 +48,15 @@ class ContainerScaScan:
|
|
|
94
48
|
"""
|
|
95
49
|
base_image = None
|
|
96
50
|
image_scanned = None
|
|
97
|
-
matching_image = self.
|
|
51
|
+
matching_image = self._get_image(self.image_to_scan)
|
|
98
52
|
if self.remote_config["GET_IMAGE_BASE"]:
|
|
99
|
-
base_image = self.
|
|
53
|
+
base_image = self._get_base_image(matching_image)
|
|
100
54
|
if self.remote_config["VALIDATE_BASE_IMAGE_DATE"][
|
|
101
55
|
"ENABLED"
|
|
102
56
|
] and not self.exclusions.get(self.pipeline_name, {}).get(
|
|
103
57
|
"VALIDATE_BASE_IMAGE_DATE"
|
|
104
58
|
):
|
|
105
|
-
self.
|
|
59
|
+
self._validate_base_image_date(
|
|
106
60
|
matching_image,
|
|
107
61
|
self.remote_config["VALIDATE_BASE_IMAGE_DATE"]["REFERENCE_IMAGE_DATE"],
|
|
108
62
|
)
|
|
@@ -114,7 +68,7 @@ class ContainerScaScan:
|
|
|
114
68
|
if matching_image:
|
|
115
69
|
image_name = matching_image.tags[0]
|
|
116
70
|
result_file = image_name.replace("/", "_") + "_scan_result.json"
|
|
117
|
-
if image_name in self.
|
|
71
|
+
if image_name in self._get_images_already_scanned():
|
|
118
72
|
print(f"The image {image_name} has already been scanned previously.")
|
|
119
73
|
return image_scanned, base_image, sbom_components
|
|
120
74
|
image_scanned, sbom_components = self.tool_run.run_tool_container_sca(
|
|
@@ -127,7 +81,7 @@ class ContainerScaScan:
|
|
|
127
81
|
self.exclusions,
|
|
128
82
|
generate_sbom,
|
|
129
83
|
)
|
|
130
|
-
self.
|
|
84
|
+
self._set_image_scanned(image_name)
|
|
131
85
|
else:
|
|
132
86
|
print(f"'Not image found for {self.image_to_scan}'. Tool skipped.")
|
|
133
87
|
return image_scanned, base_image, sbom_components
|
|
@@ -142,5 +96,52 @@ class ContainerScaScan:
|
|
|
142
96
|
context_flag = self.context
|
|
143
97
|
if context_flag == "true":
|
|
144
98
|
self.tool_deseralizator.get_container_context_from_results(image_scanned)
|
|
145
|
-
|
|
99
|
+
|
|
146
100
|
return self.tool_deseralizator.get_list_findings(image_scanned)
|
|
101
|
+
|
|
102
|
+
def _get_image(self, image_to_scan):
|
|
103
|
+
"""
|
|
104
|
+
Process the list of images.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
list: List of processed images.
|
|
108
|
+
"""
|
|
109
|
+
return self.tool_images.list_images(image_to_scan)
|
|
110
|
+
|
|
111
|
+
def _get_base_image(self, matching_image):
|
|
112
|
+
"""
|
|
113
|
+
Process the base image.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
String: base image.
|
|
117
|
+
"""
|
|
118
|
+
return self.tool_images.get_base_image(matching_image)
|
|
119
|
+
|
|
120
|
+
def _validate_base_image_date(self, matching_image, referenced_date):
|
|
121
|
+
"""
|
|
122
|
+
Process the base image date validation.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
string: base image date.
|
|
126
|
+
"""
|
|
127
|
+
return self.tool_images.validate_base_image_date(
|
|
128
|
+
matching_image, referenced_date
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def _get_images_already_scanned(self):
|
|
132
|
+
"""
|
|
133
|
+
Create images scanned file if it does not exist and get the images that have already been scanned.
|
|
134
|
+
"""
|
|
135
|
+
scanned_images_file = os.path.join(os.getcwd(), "scanned_images.txt")
|
|
136
|
+
if not os.path.exists(scanned_images_file):
|
|
137
|
+
open(scanned_images_file, "w").close()
|
|
138
|
+
with open(scanned_images_file, "r") as file:
|
|
139
|
+
images_scanned = file.read().splitlines()
|
|
140
|
+
return images_scanned
|
|
141
|
+
|
|
142
|
+
def _set_image_scanned(self, result_file):
|
|
143
|
+
"""
|
|
144
|
+
Write in scanned_images.txt the result file
|
|
145
|
+
"""
|
|
146
|
+
with open("scanned_images.txt", "a") as file:
|
|
147
|
+
file.write(result_file + "\n")
|
|
@@ -30,7 +30,7 @@ class PrismaDeserealizator(DeseralizatorGateway):
|
|
|
30
30
|
image_object = file.read()
|
|
31
31
|
|
|
32
32
|
json_data = json.loads(image_object)
|
|
33
|
-
console_url = json_data.get("consoleURL",False)
|
|
33
|
+
console_url = json_data.get("consoleURL", False)
|
|
34
34
|
if console_url:
|
|
35
35
|
print(f"Console URL: {console_url}")
|
|
36
36
|
vulnerabilities_data = (
|
|
@@ -67,7 +67,7 @@ class PrismaDeserealizator(DeseralizatorGateway):
|
|
|
67
67
|
list_open_vulnerabilities.extend(vulnerabilities)
|
|
68
68
|
|
|
69
69
|
return list_open_vulnerabilities
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
def get_container_context_from_results(self, image_scanned):
|
|
72
|
-
#TODO: Implement this method
|
|
72
|
+
# TODO: Implement this method
|
|
73
73
|
pass
|