devsecops-engine-tools 1.98.1__py3-none-any.whl → 1.99.1__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_dast/src/infrastructure/driven_adapters/nuclei/nuclei_config.py +4 -1
- devsecops_engine_tools/engine_integrations/src/applications/runner_engine_integrations.py +62 -1
- devsecops_engine_tools/engine_integrations/src/domain/usecases/handle_integrations.py +12 -0
- devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/trivy_tool/trivy_manager_scan.py +18 -6
- devsecops_engine_tools/engine_utilities/copacetic/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/applications/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/applications/runner_copacetic.py +40 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/domain/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/domain/usecases/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/domain/usecases/copacetic.py +190 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/driven_adapters/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/driven_adapters/copacetic/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/driven_adapters/copacetic/copacetic_adapter.py +309 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/entry_points/__init__.py +0 -0
- devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/entry_points/entry_point_copacetic.py +78 -0
- devsecops_engine_tools/engine_utilities/trivy_utils/infrastructure/driven_adapters/trivy_deserialize_output.py +1 -1
- devsecops_engine_tools/engine_utilities/utils/logger_info.py +1 -1
- devsecops_engine_tools/version.py +1 -1
- {devsecops_engine_tools-1.98.1.dist-info → devsecops_engine_tools-1.99.1.dist-info}/METADATA +1 -1
- {devsecops_engine_tools-1.98.1.dist-info → devsecops_engine_tools-1.99.1.dist-info}/RECORD +25 -12
- {devsecops_engine_tools-1.98.1.dist-info → devsecops_engine_tools-1.99.1.dist-info}/WHEEL +0 -0
- {devsecops_engine_tools-1.98.1.dist-info → devsecops_engine_tools-1.99.1.dist-info}/entry_points.txt +0 -0
- {devsecops_engine_tools-1.98.1.dist-info → devsecops_engine_tools-1.99.1.dist-info}/top_level.txt +0 -0
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/nuclei/nuclei_config.py
CHANGED
|
@@ -33,9 +33,12 @@ class NucleiConfig:
|
|
|
33
33
|
with open(template_name, "r") as template_file: # abrir archivo
|
|
34
34
|
template_data = self.yaml.load(template_file)
|
|
35
35
|
if "http" in template_data:
|
|
36
|
+
parm_path = ""
|
|
37
|
+
if "parm" in new_template_data["operation"]:
|
|
38
|
+
parm_path = f"?{'&'.join([str(key) + '=' + str(value) for key, value in new_template_data['operation']['parm'].items()])}"
|
|
36
39
|
template_data["http"][0]["method"] = new_template_data["operation"]["method"]
|
|
37
40
|
template_data["http"][0]["path"] = [
|
|
38
|
-
"{{BaseURL}}" + new_template_data["operation"]["path"]
|
|
41
|
+
"{{BaseURL}}" + new_template_data["operation"]["path"] + parm_path
|
|
39
42
|
]
|
|
40
43
|
if "headers" in new_template_data["operation"]:
|
|
41
44
|
if "headers" not in template_data["http"][0]:
|
|
@@ -29,11 +29,33 @@ from devsecops_engine_tools.engine_utilities import settings
|
|
|
29
29
|
|
|
30
30
|
logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
|
|
31
31
|
|
|
32
|
+
def validate_integration_requirements(args):
|
|
33
|
+
integration = args.get("integration")
|
|
34
|
+
missing_args = []
|
|
35
|
+
|
|
36
|
+
if integration == "report_sonar":
|
|
37
|
+
if not args.get("sonar_url"):
|
|
38
|
+
missing_args.append("--sonar_url")
|
|
39
|
+
|
|
40
|
+
elif integration == "copacetic":
|
|
41
|
+
if not args.get("image"):
|
|
42
|
+
missing_args.append("--image")
|
|
43
|
+
|
|
44
|
+
if not args.get("vulnerability_report") and not args.get("platform"):
|
|
45
|
+
missing_args.append("--vulnerability_report or --platform")
|
|
46
|
+
|
|
47
|
+
if missing_args:
|
|
48
|
+
error_msg = f"Missing required arguments for {integration} integration: {', '.join(missing_args)}"
|
|
49
|
+
return False, error_msg
|
|
50
|
+
|
|
51
|
+
return True, None
|
|
52
|
+
|
|
32
53
|
def get_inputs_from_cli(args):
|
|
33
54
|
parser = argparse.ArgumentParser()
|
|
55
|
+
# General flags
|
|
34
56
|
parser.add_argument(
|
|
35
57
|
"--integration",
|
|
36
|
-
choices=["report_sonar"],
|
|
58
|
+
choices=["report_sonar", "copacetic"],
|
|
37
59
|
type=str,
|
|
38
60
|
required=True,
|
|
39
61
|
help="Name of the integration to run",
|
|
@@ -109,6 +131,35 @@ def get_inputs_from_cli(args):
|
|
|
109
131
|
required=False,
|
|
110
132
|
help="Token to access sonar server",
|
|
111
133
|
)
|
|
134
|
+
# copacetic flags
|
|
135
|
+
parser.add_argument(
|
|
136
|
+
"--image",
|
|
137
|
+
required=False,
|
|
138
|
+
help="Container image to patch with Copacetic",
|
|
139
|
+
)
|
|
140
|
+
parser.add_argument(
|
|
141
|
+
"--vulnerability_report",
|
|
142
|
+
required=False,
|
|
143
|
+
help="Path to vulnerability report file for Copacetic",
|
|
144
|
+
)
|
|
145
|
+
parser.add_argument(
|
|
146
|
+
"--patch_format",
|
|
147
|
+
choices=["trivy", "grype"],
|
|
148
|
+
type=str,
|
|
149
|
+
required=False,
|
|
150
|
+
default="trivy",
|
|
151
|
+
help="Vulnerability report format for Copacetic (default: trivy)",
|
|
152
|
+
)
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
"--output_image",
|
|
155
|
+
required=False,
|
|
156
|
+
help="Output tag name for patched image",
|
|
157
|
+
)
|
|
158
|
+
parser.add_argument(
|
|
159
|
+
"--platform",
|
|
160
|
+
required=False,
|
|
161
|
+
help="Target(s) platform(s) for multi-arch images when no report directory is provided",
|
|
162
|
+
)
|
|
112
163
|
|
|
113
164
|
args = parser.parse_args()
|
|
114
165
|
return {
|
|
@@ -124,6 +175,11 @@ def get_inputs_from_cli(args):
|
|
|
124
175
|
"token_cmdb": args.token_cmdb,
|
|
125
176
|
"token_vulnerability_management": args.token_vulnerability_management,
|
|
126
177
|
"token_sonar": args.token_sonar,
|
|
178
|
+
"image": args.image,
|
|
179
|
+
"vulnerability_report": args.vulnerability_report,
|
|
180
|
+
"patch_format": args.patch_format,
|
|
181
|
+
"output_image": args.output_image,
|
|
182
|
+
"platform": args.platform
|
|
127
183
|
}
|
|
128
184
|
|
|
129
185
|
def runner_engine_integrations():
|
|
@@ -132,6 +188,11 @@ def runner_engine_integrations():
|
|
|
132
188
|
if not args["remote_config_source"]:
|
|
133
189
|
args["remote_config_source"] = args["platform_devops"]
|
|
134
190
|
|
|
191
|
+
is_valid, error_message = validate_integration_requirements(args)
|
|
192
|
+
if not is_valid:
|
|
193
|
+
logger.error(f"Error: {error_message}")
|
|
194
|
+
sys.exit(1)
|
|
195
|
+
|
|
135
196
|
vulnerability_management_gateway = DefectDojoPlatform()
|
|
136
197
|
secrets_manager_gateway = SecretsManager()
|
|
137
198
|
devops_platform_gateway = {
|
|
@@ -13,6 +13,9 @@ from devsecops_engine_tools.engine_core.src.domain.model.gateway.metrics_manager
|
|
|
13
13
|
from devsecops_engine_tools.engine_utilities.sonarqube.src.applications.runner_report_sonar import (
|
|
14
14
|
runner_report_sonar
|
|
15
15
|
)
|
|
16
|
+
from devsecops_engine_tools.engine_utilities.copacetic.src.applications.runner_copacetic import (
|
|
17
|
+
runner_copacetic
|
|
18
|
+
)
|
|
16
19
|
|
|
17
20
|
class Integrations():
|
|
18
21
|
def __init__(
|
|
@@ -40,3 +43,12 @@ class Integrations():
|
|
|
40
43
|
metrics_manager_gateway=self.metrics_manager_gateway,
|
|
41
44
|
args=args,
|
|
42
45
|
)
|
|
46
|
+
elif integration == "copacetic":
|
|
47
|
+
return runner_copacetic(
|
|
48
|
+
vulnerability_management_gateway=self.vulnerability_management_gateway,
|
|
49
|
+
secrets_manager_gateway=self.secrets_manager_gateway,
|
|
50
|
+
devops_platform_gateway=self.devops_platform_gateway,
|
|
51
|
+
remote_config_source_gateway=self.remote_config_source_gateway,
|
|
52
|
+
metrics_manager_gateway=self.metrics_manager_gateway,
|
|
53
|
+
args=args,
|
|
54
|
+
)
|
|
@@ -16,7 +16,7 @@ logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
|
|
|
16
16
|
|
|
17
17
|
class TrivyScan(ToolGateway):
|
|
18
18
|
|
|
19
|
-
def scan_image(self, prefix, image_name, result_file, base_image, is_compressed_file=False):
|
|
19
|
+
def scan_image(self, prefix, image_name, result_file, base_image, vuln_type, ignore_unfixed=False, is_compressed_file=False):
|
|
20
20
|
command = [
|
|
21
21
|
prefix,
|
|
22
22
|
"--scanners",
|
|
@@ -25,7 +25,12 @@ class TrivyScan(ToolGateway):
|
|
|
25
25
|
"json",
|
|
26
26
|
"-o",
|
|
27
27
|
result_file,
|
|
28
|
+
"--pkg-types",
|
|
29
|
+
vuln_type
|
|
28
30
|
]
|
|
31
|
+
|
|
32
|
+
if ignore_unfixed:
|
|
33
|
+
command.append("--ignore-unfixed")
|
|
29
34
|
|
|
30
35
|
if is_compressed_file:
|
|
31
36
|
command.extend(["--quiet", "image", "--input", image_name])
|
|
@@ -47,7 +52,7 @@ class TrivyScan(ToolGateway):
|
|
|
47
52
|
except Exception as e:
|
|
48
53
|
logger.error(f"Error during image scan of {image_name}: {e}")
|
|
49
54
|
|
|
50
|
-
def _generate_sbom(self, prefix, image_name, remoteconfig, is_compressed_file=False):
|
|
55
|
+
def _generate_sbom(self, prefix, image_name, remoteconfig, vuln_type, ignore_unfixed=False, is_compressed_file=False):
|
|
51
56
|
result_sbom = f"{image_name.replace('/', '_')}_SBOM.json"
|
|
52
57
|
command = [
|
|
53
58
|
prefix,
|
|
@@ -55,8 +60,12 @@ class TrivyScan(ToolGateway):
|
|
|
55
60
|
"--format",
|
|
56
61
|
remoteconfig["TRIVY"]["SBOM_FORMAT"],
|
|
57
62
|
"--output",
|
|
58
|
-
result_sbom
|
|
63
|
+
result_sbom,
|
|
64
|
+
"--pkg-types",
|
|
65
|
+
vuln_type
|
|
59
66
|
]
|
|
67
|
+
if ignore_unfixed:
|
|
68
|
+
command.append("--ignore-unfixed")
|
|
60
69
|
if is_compressed_file:
|
|
61
70
|
command.extend(["--quiet", "--input", image_name])
|
|
62
71
|
else:
|
|
@@ -77,7 +86,10 @@ class TrivyScan(ToolGateway):
|
|
|
77
86
|
logger.error(f"Error generating SBOM: {e}")
|
|
78
87
|
|
|
79
88
|
def run_tool_container_sca(self, remoteconfig, secret_tool, token_engine_container, image_name, result_file, base_image, exclusions, generate_sbom, is_compressed_file=False):
|
|
80
|
-
trivy_version = remoteconfig["TRIVY"]["TRIVY_VERSION"]
|
|
89
|
+
trivy_version = remoteconfig["TRIVY"]["TRIVY_VERSION"]
|
|
90
|
+
vuln_type = remoteconfig["TRIVY"].get("VULN_TYPE", "all").lower()
|
|
91
|
+
vuln_type = vuln_type if vuln_type in ["os", "library"] else "os,library"
|
|
92
|
+
ignore_unfixed = remoteconfig["TRIVY"].get("IGNORE_UNFIXED", False)
|
|
81
93
|
command_prefix = TrivyManagerScanUtils().identify_os_and_install(trivy_version)
|
|
82
94
|
sbom_components = None
|
|
83
95
|
|
|
@@ -85,9 +97,9 @@ class TrivyScan(ToolGateway):
|
|
|
85
97
|
return None
|
|
86
98
|
|
|
87
99
|
image_scanned = (
|
|
88
|
-
self.scan_image(command_prefix, image_name, result_file, base_image, is_compressed_file)
|
|
100
|
+
self.scan_image(command_prefix, image_name, result_file, base_image, vuln_type, ignore_unfixed, is_compressed_file)
|
|
89
101
|
)
|
|
90
102
|
if generate_sbom:
|
|
91
|
-
sbom_components = self._generate_sbom(command_prefix, image_name, remoteconfig, is_compressed_file)
|
|
103
|
+
sbom_components = self._generate_sbom(command_prefix, image_name, remoteconfig, vuln_type, ignore_unfixed, is_compressed_file)
|
|
92
104
|
|
|
93
105
|
return image_scanned, sbom_components
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from devsecops_engine_tools.engine_utilities.copacetic.src.infrastructure.driven_adapters.copacetic.copacetic_adapter import(
|
|
2
|
+
CopaceticAdapter
|
|
3
|
+
)
|
|
4
|
+
from devsecops_engine_tools.engine_utilities.copacetic.src.infrastructure.entry_points.entry_point_copacetic import (
|
|
5
|
+
init_copacetic
|
|
6
|
+
)
|
|
7
|
+
from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
|
|
8
|
+
from devsecops_engine_tools.engine_utilities import settings
|
|
9
|
+
|
|
10
|
+
logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
|
|
11
|
+
|
|
12
|
+
def runner_copacetic(
|
|
13
|
+
vulnerability_management_gateway,
|
|
14
|
+
secrets_manager_gateway,
|
|
15
|
+
devops_platform_gateway,
|
|
16
|
+
remote_config_source_gateway,
|
|
17
|
+
metrics_manager_gateway,
|
|
18
|
+
args
|
|
19
|
+
):
|
|
20
|
+
try:
|
|
21
|
+
copacetic_gateway = CopaceticAdapter()
|
|
22
|
+
|
|
23
|
+
init_copacetic(
|
|
24
|
+
vulnerability_management_gateway=vulnerability_management_gateway,
|
|
25
|
+
secrets_manager_gateway=secrets_manager_gateway,
|
|
26
|
+
devops_platform_gateway=devops_platform_gateway,
|
|
27
|
+
remote_config_source_gateway=remote_config_source_gateway,
|
|
28
|
+
copacetic_gateway=copacetic_gateway,
|
|
29
|
+
metrics_manager_gateway=metrics_manager_gateway,
|
|
30
|
+
args=args,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
except Exception as e:
|
|
34
|
+
logger.error("Error copacetic: {0} ".format(str(e)))
|
|
35
|
+
print(
|
|
36
|
+
devops_platform_gateway.message(
|
|
37
|
+
"error", "Error copacetic: {0} ".format(str(e))
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
print(devops_platform_gateway.result_pipeline("failed"))
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from devsecops_engine_tools.engine_core.src.domain.model.gateway.vulnerability_management_gateway import (
|
|
2
|
+
VulnerabilityManagementGateway
|
|
3
|
+
)
|
|
4
|
+
from devsecops_engine_tools.engine_core.src.domain.model.gateway.secrets_manager_gateway import (
|
|
5
|
+
SecretsManagerGateway
|
|
6
|
+
)
|
|
7
|
+
from devsecops_engine_tools.engine_core.src.domain.model.gateway.devops_platform_gateway import (
|
|
8
|
+
DevopsPlatformGateway
|
|
9
|
+
)
|
|
10
|
+
from devsecops_engine_tools.engine_core.src.domain.model.input_core import InputCore
|
|
11
|
+
from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
|
|
12
|
+
from devsecops_engine_tools.engine_utilities import settings
|
|
13
|
+
import shutil
|
|
14
|
+
|
|
15
|
+
logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Copacetic:
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
vulnerability_management_gateway: VulnerabilityManagementGateway,
|
|
22
|
+
secrets_manager_gateway: SecretsManagerGateway,
|
|
23
|
+
devops_platform_gateway: DevopsPlatformGateway,
|
|
24
|
+
remote_config_source_gateway: DevopsPlatformGateway,
|
|
25
|
+
copacetic_gateway,
|
|
26
|
+
):
|
|
27
|
+
self.vulnerability_management_gateway = vulnerability_management_gateway
|
|
28
|
+
self.secrets_manager_gateway = secrets_manager_gateway
|
|
29
|
+
self.devops_platform_gateway = devops_platform_gateway
|
|
30
|
+
self.remote_config_source_gateway = remote_config_source_gateway
|
|
31
|
+
self.copacetic_gateway = copacetic_gateway
|
|
32
|
+
|
|
33
|
+
def process(self, args):
|
|
34
|
+
try:
|
|
35
|
+
copacetic_config = self.remote_config_source_gateway.get_remote_config(
|
|
36
|
+
args["remote_config_repo"], "/engine_integrations/copacetic/ConfigTool.json", args["remote_config_branch"]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
image = args.get("image")
|
|
40
|
+
vulnerability_report = args.get("vulnerability_report")
|
|
41
|
+
patch_format = args.get("patch_format", "trivy")
|
|
42
|
+
|
|
43
|
+
if not image:
|
|
44
|
+
raise ValueError("Image is required for Copacetic patching")
|
|
45
|
+
|
|
46
|
+
logger.info(f"Starting Copacetic patching for image: {image}")
|
|
47
|
+
|
|
48
|
+
image_info = {}
|
|
49
|
+
if hasattr(self.copacetic_gateway, 'get_image_info'):
|
|
50
|
+
image_info = self.copacetic_gateway.get_image_info(image)
|
|
51
|
+
if not image_info.get("exists", False):
|
|
52
|
+
logger.info(f"Image {image} may not exist locally. Copacetic will attempt to pull it.")
|
|
53
|
+
|
|
54
|
+
patch_result = self.copacetic_gateway.patch_image(
|
|
55
|
+
image=image,
|
|
56
|
+
vulnerability_report=vulnerability_report,
|
|
57
|
+
output_image=args.get("output_image"),
|
|
58
|
+
patch_format=patch_format,
|
|
59
|
+
config=copacetic_config,
|
|
60
|
+
work_folder=self.devops_platform_gateway.get_variable("path_directory"),
|
|
61
|
+
platform=args.get("platform")
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if patch_result["success"]:
|
|
65
|
+
logger.info("Copacetic patching completed successfully")
|
|
66
|
+
|
|
67
|
+
if image_info.get("exists", False):
|
|
68
|
+
patch_result["original_image_info"] = {
|
|
69
|
+
"architecture": image_info.get("architecture"),
|
|
70
|
+
"os": image_info.get("os"),
|
|
71
|
+
"size": image_info.get("size"),
|
|
72
|
+
"layers": image_info.get("layers")
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
self._print_results_table(patch_result)
|
|
76
|
+
else:
|
|
77
|
+
detailed_error = f"Copacetic patching failed for {image}: {patch_result.get('error', 'Unknown error')}"
|
|
78
|
+
if patch_result.get("copa_error"):
|
|
79
|
+
detailed_error += f"\nCopa stderr: {patch_result['copa_error']}"
|
|
80
|
+
|
|
81
|
+
print(
|
|
82
|
+
self.devops_platform_gateway.message("error", detailed_error)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return InputCore(
|
|
86
|
+
totalized_exclusions=[],
|
|
87
|
+
threshold_defined=None,
|
|
88
|
+
path_file_results=patch_result.get("output_file", ""),
|
|
89
|
+
custom_message_break_build=f"Copacetic patching completed for {image}",
|
|
90
|
+
scope_pipeline="",
|
|
91
|
+
scope_service="",
|
|
92
|
+
stage_pipeline=self.devops_platform_gateway.get_variable("stage")
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error(f"Error in Copacetic process: {str(e)}")
|
|
97
|
+
print(
|
|
98
|
+
self.devops_platform_gateway.message(
|
|
99
|
+
"error",
|
|
100
|
+
f"Error in Copacetic process: {str(e)}"
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def _print_results_table(self, summary):
|
|
105
|
+
try:
|
|
106
|
+
terminal_width = min(shutil.get_terminal_size().columns, 100)
|
|
107
|
+
except:
|
|
108
|
+
terminal_width = 100
|
|
109
|
+
|
|
110
|
+
logger.info(f"\n{'='*terminal_width}")
|
|
111
|
+
title = " COPACETIC PATCHING RESULTS "
|
|
112
|
+
padding = (terminal_width - len(title)) // 2
|
|
113
|
+
logger.info(f"{' '*padding}{title}{' '*padding}")
|
|
114
|
+
logger.info(f"{'='*terminal_width}")
|
|
115
|
+
|
|
116
|
+
logger.info("\n IMAGE INFORMATION:")
|
|
117
|
+
logger.info(f" Original Image: {summary['original_image']}")
|
|
118
|
+
logger.info(f" Patched Image: {summary['patched_image']}")
|
|
119
|
+
|
|
120
|
+
logger.info("\n PATCHING STATISTICS:")
|
|
121
|
+
vuln_count = summary.get('vulnerabilities_patched', 0)
|
|
122
|
+
pkg_count = summary.get('packages_updated', 0)
|
|
123
|
+
vex_generated = summary.get('vex_file_generated', False)
|
|
124
|
+
|
|
125
|
+
logger.info(f" Vulnerabilities Patched: {vuln_count}")
|
|
126
|
+
logger.info(f" Packages Updated: {pkg_count}")
|
|
127
|
+
logger.info(f" VEX Report Generated: {'Yes' if vex_generated else 'No'}")
|
|
128
|
+
|
|
129
|
+
if not vex_generated:
|
|
130
|
+
logger.info(" Note: VEX report not generated (no vulnerability report provided)")
|
|
131
|
+
|
|
132
|
+
platforms = summary.get('platforms_processed', [])
|
|
133
|
+
if platforms:
|
|
134
|
+
logger.info(f" Platforms Processed: {', '.join(platforms)}")
|
|
135
|
+
|
|
136
|
+
if summary.get('original_image_info'):
|
|
137
|
+
info = summary['original_image_info']
|
|
138
|
+
logger.info("\n IMAGE DETAILS:")
|
|
139
|
+
logger.info(f" Architecture: {info.get('architecture', 'N/A')}")
|
|
140
|
+
logger.info(f" OS: {info.get('os', 'N/A')}")
|
|
141
|
+
logger.info(f" Size: {info.get('size', 'N/A')}")
|
|
142
|
+
logger.info(f" Layers: {info.get('layers', 'N/A')}")
|
|
143
|
+
|
|
144
|
+
patch_details = summary.get('patch_details', [])
|
|
145
|
+
if patch_details:
|
|
146
|
+
logger.info("\n PATCH DETAILS:")
|
|
147
|
+
|
|
148
|
+
for i, detail in enumerate(patch_details, 1):
|
|
149
|
+
if isinstance(detail, dict):
|
|
150
|
+
cve = detail.get('vulnerability', detail.get('id', f'PATCH-{i}'))
|
|
151
|
+
logger.info(f" {cve}")
|
|
152
|
+
|
|
153
|
+
packages = detail.get('packages', [])
|
|
154
|
+
if not packages:
|
|
155
|
+
single_package = detail.get('package', detail.get('component'))
|
|
156
|
+
if single_package:
|
|
157
|
+
packages = [single_package]
|
|
158
|
+
|
|
159
|
+
for pkg in packages:
|
|
160
|
+
if isinstance(pkg, dict):
|
|
161
|
+
pkg_name = pkg.get('name', pkg.get('package', 'Unknown'))
|
|
162
|
+
version = pkg.get('version', pkg.get('fixed_version', ''))
|
|
163
|
+
if version:
|
|
164
|
+
logger.info(f" {pkg_name} (v{version})")
|
|
165
|
+
else:
|
|
166
|
+
logger.info(f" {pkg_name}")
|
|
167
|
+
else:
|
|
168
|
+
logger.info(f" {pkg}")
|
|
169
|
+
|
|
170
|
+
if not packages:
|
|
171
|
+
logger.info(" No package information available")
|
|
172
|
+
|
|
173
|
+
else:
|
|
174
|
+
logger.info(f" {detail}")
|
|
175
|
+
|
|
176
|
+
if i < len(patch_details):
|
|
177
|
+
logger.info("")
|
|
178
|
+
|
|
179
|
+
logger.info("\n SUMMARY:")
|
|
180
|
+
vex_generated = summary.get('vex_file_generated', False)
|
|
181
|
+
|
|
182
|
+
if vuln_count > 0:
|
|
183
|
+
logger.info(f" Status: SUCCESS - {vuln_count} vulnerabilities were patched")
|
|
184
|
+
elif vex_generated:
|
|
185
|
+
logger.info(" Status: COMPLETED - No vulnerabilities found to patch")
|
|
186
|
+
else:
|
|
187
|
+
logger.info(" Status: COMPLETED - Image patched successfully without vulnerability report")
|
|
188
|
+
logger.info(" Use --vulnerability_report to generate detailed VEX output")
|
|
189
|
+
|
|
190
|
+
logger.info(f"{'='*terminal_width}\n")
|
|
File without changes
|
devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/driven_adapters/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
import platform
|
|
5
|
+
import requests
|
|
6
|
+
import tarfile
|
|
7
|
+
import tempfile
|
|
8
|
+
import docker
|
|
9
|
+
from typing import Dict, Optional
|
|
10
|
+
from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
|
|
11
|
+
from devsecops_engine_tools.engine_utilities import settings
|
|
12
|
+
|
|
13
|
+
logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CopaceticAdapter:
|
|
17
|
+
def __init__(self, config: Optional[Dict] = None):
|
|
18
|
+
self.config = config or {}
|
|
19
|
+
|
|
20
|
+
def install_tool(self, version, path="."):
|
|
21
|
+
try:
|
|
22
|
+
installed = subprocess.run(
|
|
23
|
+
["which", "copa"],
|
|
24
|
+
stdout=subprocess.PIPE,
|
|
25
|
+
stderr=subprocess.PIPE,
|
|
26
|
+
)
|
|
27
|
+
if installed.returncode == 0:
|
|
28
|
+
return
|
|
29
|
+
except FileNotFoundError:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
os_platform = platform.system()
|
|
34
|
+
architecture = platform.machine()
|
|
35
|
+
|
|
36
|
+
if os_platform == "Linux":
|
|
37
|
+
if architecture == "aarch64" or architecture == "arm64":
|
|
38
|
+
arch = "arm64"
|
|
39
|
+
else:
|
|
40
|
+
arch = "amd64"
|
|
41
|
+
curr_os = "linux"
|
|
42
|
+
elif os_platform == "Darwin":
|
|
43
|
+
if architecture == "aarch64" or architecture == "arm64":
|
|
44
|
+
arch = "arm64"
|
|
45
|
+
else:
|
|
46
|
+
arch = "amd64"
|
|
47
|
+
curr_os = "darwin"
|
|
48
|
+
else:
|
|
49
|
+
raise OSError(f"Copa installation is not supported on {os_platform}")
|
|
50
|
+
|
|
51
|
+
url = f"https://github.com/project-copacetic/copacetic/releases/download/v{version}/copa_{version}_{curr_os}_{arch}.tar.gz"
|
|
52
|
+
|
|
53
|
+
response = requests.get(url, allow_redirects=True)
|
|
54
|
+
response.raise_for_status()
|
|
55
|
+
|
|
56
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix='.tar.gz') as temp_file:
|
|
57
|
+
temp_file.write(response.content)
|
|
58
|
+
temp_path = temp_file.name
|
|
59
|
+
|
|
60
|
+
with tarfile.open(temp_path, 'r:gz') as tar:
|
|
61
|
+
copa_member = None
|
|
62
|
+
for member in tar.getmembers():
|
|
63
|
+
if member.name.endswith('copa') and member.isfile():
|
|
64
|
+
copa_member = member
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
if copa_member:
|
|
68
|
+
tar.extract(copa_member, path=path)
|
|
69
|
+
extracted_path = os.path.join(path, copa_member.name)
|
|
70
|
+
if os.path.exists(extracted_path):
|
|
71
|
+
os.chmod(extracted_path, 0o755)
|
|
72
|
+
|
|
73
|
+
os.unlink(temp_path)
|
|
74
|
+
return extracted_path
|
|
75
|
+
|
|
76
|
+
except requests.RequestException as error:
|
|
77
|
+
logger.error(f"Error downloading Copa for {os_platform}: {error}")
|
|
78
|
+
except tarfile.TarError as error:
|
|
79
|
+
logger.error(f"Error extracting Copa archive: {error}")
|
|
80
|
+
except Exception as error:
|
|
81
|
+
logger.error(f"Error during Copa installation on {os_platform}: {error}")
|
|
82
|
+
|
|
83
|
+
def patch_image(
|
|
84
|
+
self,
|
|
85
|
+
image: str,
|
|
86
|
+
vulnerability_report: str,
|
|
87
|
+
output_image: Optional[str] = None,
|
|
88
|
+
patch_format: str = "trivy",
|
|
89
|
+
config: Optional[Dict] = None,
|
|
90
|
+
work_folder: str = ".",
|
|
91
|
+
platform: str = ""
|
|
92
|
+
):
|
|
93
|
+
try:
|
|
94
|
+
config = config or {}
|
|
95
|
+
buildkit_config = config.get("BUILDKIT_CONFIG", {})
|
|
96
|
+
prefix = self.install_tool(config.get("VERSION"), path=work_folder)
|
|
97
|
+
output_file = f"{image.replace('/', '_')}-patch-vex.json"
|
|
98
|
+
|
|
99
|
+
copa_cmd = [
|
|
100
|
+
prefix,
|
|
101
|
+
"patch",
|
|
102
|
+
"--image",
|
|
103
|
+
image,
|
|
104
|
+
"--scanner",
|
|
105
|
+
patch_format,
|
|
106
|
+
"--format",
|
|
107
|
+
"openvex",
|
|
108
|
+
"--output",
|
|
109
|
+
output_file
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
if vulnerability_report:
|
|
113
|
+
copa_cmd.extend(["--report", vulnerability_report])
|
|
114
|
+
|
|
115
|
+
buildkit_addr = buildkit_config.get("DEFAULT_ADDR")
|
|
116
|
+
if buildkit_addr:
|
|
117
|
+
copa_cmd.extend(["--addr", buildkit_addr])
|
|
118
|
+
|
|
119
|
+
progress_mode = buildkit_config.get("PROGRESS", "auto")
|
|
120
|
+
if progress_mode not in ["auto", "plain", "tty", "quiet", "rawjson"]:
|
|
121
|
+
raise ValueError(f"Invalid progress mode: {progress_mode}")
|
|
122
|
+
else:
|
|
123
|
+
copa_cmd.extend(["--progress", progress_mode])
|
|
124
|
+
|
|
125
|
+
if output_image:
|
|
126
|
+
copa_cmd.extend(["--tag", output_image])
|
|
127
|
+
else:
|
|
128
|
+
tag_suffix = config.get("DEFAULT_OUTPUT_SUFFIX", "-patched")
|
|
129
|
+
if tag_suffix.startswith("-"):
|
|
130
|
+
tag_suffix = tag_suffix[1:]
|
|
131
|
+
copa_cmd.extend(["--tag-suffix", tag_suffix])
|
|
132
|
+
|
|
133
|
+
if platform:
|
|
134
|
+
copa_cmd.extend(["--platform", platform])
|
|
135
|
+
elif not vulnerability_report:
|
|
136
|
+
raise ValueError(
|
|
137
|
+
"If a vulnerability report is not provided, the platforms to be patched must be provided."
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
timeout_duration = config.get("TIMEOUT", 5)
|
|
141
|
+
copa_cmd.extend(["--timeout", f"{timeout_duration}m"])
|
|
142
|
+
|
|
143
|
+
if buildkit_config.get("IGNORE_ERRORS", False):
|
|
144
|
+
copa_cmd.append("--ignore-errors")
|
|
145
|
+
|
|
146
|
+
result = subprocess.run(
|
|
147
|
+
copa_cmd,
|
|
148
|
+
capture_output=True,
|
|
149
|
+
text=True
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if result.returncode == 0:
|
|
153
|
+
if os.path.exists(output_file):
|
|
154
|
+
subprocess.run(["chmod", "644", f"./{output_file}"])
|
|
155
|
+
patch_details = self._parse_copa_output(output_file)
|
|
156
|
+
else:
|
|
157
|
+
if not vulnerability_report:
|
|
158
|
+
logger.info("Note: VEX report file not generated (no vulnerability report provided)")
|
|
159
|
+
logger.info("Image patching completed successfully without VEX output")
|
|
160
|
+
patch_details = {
|
|
161
|
+
"vulnerabilities_patched": 0,
|
|
162
|
+
"details": [],
|
|
163
|
+
"packages_updated": [],
|
|
164
|
+
"platforms_processed": []
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if output_image:
|
|
168
|
+
patched_image = output_image
|
|
169
|
+
else:
|
|
170
|
+
tag_suffix = config.get("DEFAULT_OUTPUT_SUFFIX", "-patched")
|
|
171
|
+
if ":" in image:
|
|
172
|
+
base_image, tag = image.rsplit(":", 1)
|
|
173
|
+
patched_image = f"{base_image}:{tag}{tag_suffix}"
|
|
174
|
+
else:
|
|
175
|
+
patched_image = f"{image}{tag_suffix}"
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
"success": True,
|
|
179
|
+
"original_image": image,
|
|
180
|
+
"patched_image": patched_image,
|
|
181
|
+
"platforms_processed": patch_details.get("platforms_processed", []),
|
|
182
|
+
"vulnerabilities_patched": patch_details.get("vulnerabilities_patched", 0),
|
|
183
|
+
"packages_updated": len(patch_details.get("packages_updated", [])),
|
|
184
|
+
"patch_details": patch_details.get("details", []),
|
|
185
|
+
"copa_output": result.stdout,
|
|
186
|
+
"vex_file_generated": os.path.exists(output_file)
|
|
187
|
+
}
|
|
188
|
+
else:
|
|
189
|
+
error_msg = f"Copa command failed with return code {result.returncode}. Error: {result.stderr}"
|
|
190
|
+
logger.error(error_msg)
|
|
191
|
+
return {
|
|
192
|
+
"success": False,
|
|
193
|
+
"error": error_msg,
|
|
194
|
+
"copa_output": result.stdout,
|
|
195
|
+
"copa_error": result.stderr
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
except subprocess.TimeoutExpired:
|
|
199
|
+
error_msg = f"Copa command timed out after {timeout_duration} minutes"
|
|
200
|
+
logger.error(error_msg)
|
|
201
|
+
return {
|
|
202
|
+
"success": False,
|
|
203
|
+
"error": error_msg
|
|
204
|
+
}
|
|
205
|
+
except Exception as e:
|
|
206
|
+
error_msg = f"Unexpected error during Copa execution: {str(e)}"
|
|
207
|
+
logger.error(error_msg)
|
|
208
|
+
return {
|
|
209
|
+
"success": False,
|
|
210
|
+
"error": error_msg
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
def get_image_info(self, image: str) -> Dict:
|
|
214
|
+
try:
|
|
215
|
+
client = docker.from_env()
|
|
216
|
+
image_data = client.api.inspect_image(image)
|
|
217
|
+
rootfs = image_data.get("RootFS", {})
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
"exists": True,
|
|
221
|
+
"architecture": image_data.get("Architecture"),
|
|
222
|
+
"os": image_data.get("Os"),
|
|
223
|
+
"size": image_data.get("Size"),
|
|
224
|
+
"layers": len(rootfs.get("Layers", []))
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.error(f"Unexpected error while getting image info for '{image}': {str(e)}")
|
|
229
|
+
return {"exists": False, "error": str(e)}
|
|
230
|
+
|
|
231
|
+
def _parse_copa_output(self, output_path: str) -> Dict:
|
|
232
|
+
try:
|
|
233
|
+
if not os.path.exists(output_path):
|
|
234
|
+
logger.info(f"VEX output file not found at {output_path}")
|
|
235
|
+
return {
|
|
236
|
+
"vulnerabilities_patched": 0,
|
|
237
|
+
"details": [],
|
|
238
|
+
"packages_updated": [],
|
|
239
|
+
"platforms_processed": []
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
details = {
|
|
243
|
+
"vulnerabilities_patched": 0,
|
|
244
|
+
"details": [],
|
|
245
|
+
"packages_updated": set(),
|
|
246
|
+
"platforms_processed": set()
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
with open(output_path, "r", encoding="utf-8") as f:
|
|
250
|
+
data = json.load(f)
|
|
251
|
+
|
|
252
|
+
arch_str = "?arch="
|
|
253
|
+
for stmt in data.get("statements", []):
|
|
254
|
+
vuln_id = stmt.get("vulnerability", {}).get("@id")
|
|
255
|
+
status = stmt.get("status")
|
|
256
|
+
products = stmt.get("products", [])
|
|
257
|
+
packages = []
|
|
258
|
+
|
|
259
|
+
if status == "fixed":
|
|
260
|
+
details["vulnerabilities_patched"] += 1
|
|
261
|
+
|
|
262
|
+
for product in products:
|
|
263
|
+
for sub in product.get("subcomponents", []):
|
|
264
|
+
pkg_id = sub.get("@id", "")
|
|
265
|
+
packages.append(pkg_id.split(arch_str)[0])
|
|
266
|
+
details["packages_updated"].add(pkg_id)
|
|
267
|
+
|
|
268
|
+
if arch_str in pkg_id:
|
|
269
|
+
arch = pkg_id.split(arch_str)[-1]
|
|
270
|
+
details["platforms_processed"].add(arch)
|
|
271
|
+
|
|
272
|
+
details["details"].append({
|
|
273
|
+
"vulnerability": vuln_id,
|
|
274
|
+
"status": status,
|
|
275
|
+
"products": [p.get("@id") for p in products],
|
|
276
|
+
"packages": packages
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
details["packages_updated"] = list(details["packages_updated"])
|
|
280
|
+
details["platforms_processed"] = list(details["platforms_processed"])
|
|
281
|
+
|
|
282
|
+
return details
|
|
283
|
+
|
|
284
|
+
except FileNotFoundError:
|
|
285
|
+
logger.error(f"VEX output file not found: {output_path}")
|
|
286
|
+
return {
|
|
287
|
+
"vulnerabilities_patched": 0,
|
|
288
|
+
"details": [],
|
|
289
|
+
"packages_updated": [],
|
|
290
|
+
"platforms_processed": []
|
|
291
|
+
}
|
|
292
|
+
except json.JSONDecodeError as e:
|
|
293
|
+
logger.error(f"Error parsing VEX JSON file: {e}")
|
|
294
|
+
return {
|
|
295
|
+
"vulnerabilities_patched": 0,
|
|
296
|
+
"details": [],
|
|
297
|
+
"packages_updated": [],
|
|
298
|
+
"platforms_processed": []
|
|
299
|
+
}
|
|
300
|
+
except Exception as e:
|
|
301
|
+
logger.error(f"Error processing VEX output file {output_path}: {e}")
|
|
302
|
+
return {
|
|
303
|
+
"vulnerabilities_patched": 0,
|
|
304
|
+
"details": [],
|
|
305
|
+
"packages_updated": [],
|
|
306
|
+
"platforms_processed": []
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from devsecops_engine_tools.engine_utilities.copacetic.src.domain.usecases.copacetic import (
|
|
2
|
+
Copacetic,
|
|
3
|
+
)
|
|
4
|
+
from devsecops_engine_tools.engine_core.src.domain.usecases.metrics_manager import (
|
|
5
|
+
MetricsManager,
|
|
6
|
+
)
|
|
7
|
+
import re
|
|
8
|
+
from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
|
|
9
|
+
from devsecops_engine_tools.engine_utilities import settings
|
|
10
|
+
|
|
11
|
+
logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def init_copacetic(
|
|
15
|
+
vulnerability_management_gateway,
|
|
16
|
+
secrets_manager_gateway,
|
|
17
|
+
devops_platform_gateway,
|
|
18
|
+
remote_config_source_gateway,
|
|
19
|
+
copacetic_gateway,
|
|
20
|
+
metrics_manager_gateway,
|
|
21
|
+
args,
|
|
22
|
+
):
|
|
23
|
+
config_tool = remote_config_source_gateway.get_remote_config(
|
|
24
|
+
args["remote_config_repo"], "/engine_core/ConfigTool.json", args["remote_config_branch"]
|
|
25
|
+
)
|
|
26
|
+
copacetic_config_tool = remote_config_source_gateway.get_remote_config(
|
|
27
|
+
args["remote_config_repo"], "/engine_integrations/copacetic/ConfigTool.json", args["remote_config_branch"]
|
|
28
|
+
)
|
|
29
|
+
excluded_pipelines = remote_config_source_gateway.get_remote_config(
|
|
30
|
+
args["remote_config_repo"], "/engine_integrations/copacetic/Exclusions.json", args["remote_config_branch"]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
pipeline_name = devops_platform_gateway.get_variable("pipeline_name")
|
|
34
|
+
branch = devops_platform_gateway.get_variable("branch_tag")
|
|
35
|
+
|
|
36
|
+
is_valid_pipeline = pipeline_name not in excluded_pipelines and not any(
|
|
37
|
+
[
|
|
38
|
+
re.match(
|
|
39
|
+
pattern,
|
|
40
|
+
pipeline_name,
|
|
41
|
+
re.IGNORECASE
|
|
42
|
+
) for pattern in
|
|
43
|
+
[copacetic_config_tool["IGNORE_SEARCH_PATTERN"]] +
|
|
44
|
+
list(excluded_pipelines.get("BY_PATTERN_SEARCH", {}).keys())
|
|
45
|
+
]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
is_valid_branch = any(
|
|
49
|
+
target_branch in str(branch).split("/")
|
|
50
|
+
for target_branch in copacetic_config_tool["TARGET_BRANCHES"]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
is_enabled = config_tool["COPACETIC"]["ENABLED"]
|
|
54
|
+
|
|
55
|
+
if is_enabled and is_valid_pipeline and is_valid_branch:
|
|
56
|
+
input_core = Copacetic(
|
|
57
|
+
vulnerability_management_gateway,
|
|
58
|
+
secrets_manager_gateway,
|
|
59
|
+
devops_platform_gateway,
|
|
60
|
+
remote_config_source_gateway,
|
|
61
|
+
copacetic_gateway,
|
|
62
|
+
).process(args)
|
|
63
|
+
|
|
64
|
+
if args["send_metrics"] == "true":
|
|
65
|
+
MetricsManager(devops_platform_gateway, metrics_manager_gateway).process(
|
|
66
|
+
config_tool, input_core, {"module": "copacetic"}, ""
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
if not is_enabled:
|
|
70
|
+
message = "DevSecOps Engine Tool - {0} in maintenance...".format(
|
|
71
|
+
"copacetic"
|
|
72
|
+
)
|
|
73
|
+
else:
|
|
74
|
+
message = "Tool skipped by DevSecOps policy"
|
|
75
|
+
|
|
76
|
+
print(
|
|
77
|
+
devops_platform_gateway.message("warning", message),
|
|
78
|
+
)
|
|
@@ -21,7 +21,7 @@ class TrivyDeserializator(DeseralizatorGateway):
|
|
|
21
21
|
with open(image_scanned, "rb") as file:
|
|
22
22
|
image_object = file.read()
|
|
23
23
|
json_data = json.loads(image_object)
|
|
24
|
-
vulnerabilities_data = json_data
|
|
24
|
+
vulnerabilities_data = json_data.get("Results", [{}])[0].get("Vulnerabilities", [])
|
|
25
25
|
vulnerabilities = [
|
|
26
26
|
Finding(
|
|
27
27
|
id=vul.get("VulnerabilityID", ""),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
version = '1.
|
|
1
|
+
version = '1.99.1'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
devsecops_engine_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
devsecops_engine_tools/version.py,sha256=
|
|
2
|
+
devsecops_engine_tools/version.py,sha256=kpCtR0hJPuiwybJjqXJ2YMV_S_at5T19xLodf9WZse4,19
|
|
3
3
|
devsecops_engine_tools/engine_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
devsecops_engine_tools/engine_core/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
devsecops_engine_tools/engine_core/src/applications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -81,7 +81,7 @@ devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/jwt/__init
|
|
|
81
81
|
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/jwt/jwt_object.py,sha256=YSPPVt7NTq5LasLPJZolc-Y44remOEroiqL79nqRXC8,2219
|
|
82
82
|
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/jwt/jwt_tool.py,sha256=9Yh7lOd6lsHcvl8exgWW7N8qTP55w-Znl0kid7IlKrM,5431
|
|
83
83
|
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/nuclei/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
84
|
-
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/nuclei/nuclei_config.py,sha256=
|
|
84
|
+
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/nuclei/nuclei_config.py,sha256=lcpGAETmZ1yHkQWK8KKCKAdrCifvDLZFTxXeG4R-Vf0,3985
|
|
85
85
|
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/nuclei/nuclei_deserealizer.py,sha256=qqoBMXr350ItzabSU6a_fD2-9kB6pAmtWioFP5AvCIE,1346
|
|
86
86
|
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/nuclei/nuclei_tool.py,sha256=KQKEq06izKut2VMhD9nfc-CFPdvT8wOcar3COB7x6ZA,6843
|
|
87
87
|
devsecops_engine_tools/engine_dast/src/infrastructure/driven_adapters/oauth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -94,10 +94,10 @@ devsecops_engine_tools/engine_dast/src/infrastructure/helpers/json_handler.py,sh
|
|
|
94
94
|
devsecops_engine_tools/engine_integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
95
95
|
devsecops_engine_tools/engine_integrations/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
96
96
|
devsecops_engine_tools/engine_integrations/src/applications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
97
|
-
devsecops_engine_tools/engine_integrations/src/applications/runner_engine_integrations.py,sha256=
|
|
97
|
+
devsecops_engine_tools/engine_integrations/src/applications/runner_engine_integrations.py,sha256=EhOmi4L6X6oNlQtko860MG7eEweWyoV2aOLVMgbJGvY,7588
|
|
98
98
|
devsecops_engine_tools/engine_integrations/src/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
99
99
|
devsecops_engine_tools/engine_integrations/src/domain/usecases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
100
|
-
devsecops_engine_tools/engine_integrations/src/domain/usecases/handle_integrations.py,sha256=
|
|
100
|
+
devsecops_engine_tools/engine_integrations/src/domain/usecases/handle_integrations.py,sha256=SW-PF6u5r4Ffd5tjBt3qT9u5-lc-JfMpnyqyZGcWu30,2553
|
|
101
101
|
devsecops_engine_tools/engine_integrations/src/infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
102
102
|
devsecops_engine_tools/engine_integrations/src/infrastructure/entry_points/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
103
103
|
devsecops_engine_tools/engine_integrations/src/infrastructure/entry_points/entry_point_integrations.py,sha256=RV2oynZVww4q9KeH8ISCpVTT7ZwbNy6F99MwIGk_B5A,1194
|
|
@@ -226,7 +226,7 @@ devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_ada
|
|
|
226
226
|
devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/prisma_cloud/prisma_cloud_manager_scan.py,sha256=Un0YmZeGh3LpOHiq6872lphD15cf02R9hwBUiHVuhCM,7848
|
|
227
227
|
devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/prisma_cloud/prisma_deserialize_output.py,sha256=FXb0jUReJVUdZq_H_Zz-gCueMmWf0AwMiwJB-Ceqv2A,2695
|
|
228
228
|
devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/trivy_tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
229
|
-
devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/trivy_tool/trivy_manager_scan.py,sha256=
|
|
229
|
+
devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/trivy_tool/trivy_manager_scan.py,sha256=Pg0Q0CKEQ-mE8u47H7ts1q3l0JOmTSeU9SBqr9_5U-Y,3839
|
|
230
230
|
devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/entry_points/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
231
231
|
devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/entry_points/entry_point_tool.py,sha256=MCBVnUxfjnax2stjn9ByM0Hy9LQ9vAMK9GZkOk3ex9M,3077
|
|
232
232
|
devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -268,6 +268,19 @@ devsecops_engine_tools/engine_utilities/azuredevops/infrastructure/azure_devops_
|
|
|
268
268
|
devsecops_engine_tools/engine_utilities/azuredevops/models/AzureMessageLoggingPipeline.py,sha256=pCwlPDDl-hgvZ9gvceuC8GsKbsMhRm3ykhFFVByVqcI,664
|
|
269
269
|
devsecops_engine_tools/engine_utilities/azuredevops/models/AzurePredefinedVariables.py,sha256=z9AGtc64o-BNTngiowRJFBlXmo_JmWqenL8YxdLs0aE,2561
|
|
270
270
|
devsecops_engine_tools/engine_utilities/azuredevops/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
271
|
+
devsecops_engine_tools/engine_utilities/copacetic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
272
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
273
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/applications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
274
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/applications/runner_copacetic.py,sha256=T4S3LMrVqA4RhpNaFezyILMLFkfNqFVm2Lz9h2o7IvQ,1488
|
|
275
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
276
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/domain/usecases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
277
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/domain/usecases/copacetic.py,sha256=IH9wRBLhmtjM5Cf_arRB_a3woTClDSNatI7FAsZOG6k,8381
|
|
278
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
279
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/driven_adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
280
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/driven_adapters/copacetic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
281
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/driven_adapters/copacetic/copacetic_adapter.py,sha256=Uc4zKMyMVnEGsQD5rMvPY1DzAtdmHNx_6b-4E7QDgWM,11957
|
|
282
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/entry_points/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
283
|
+
devsecops_engine_tools/engine_utilities/copacetic/src/infrastructure/entry_points/entry_point_copacetic.py,sha256=3FU0rsOaGJq_T8Vlo-hZNk1P37xeoG4T3OFeIgiLKKs,2782
|
|
271
284
|
devsecops_engine_tools/engine_utilities/defect_dojo/__init__.py,sha256=P-HiaN1sgDUekalZPCzSTF-zuqyXpNG2uVEsMDaC0ro,462
|
|
272
285
|
devsecops_engine_tools/engine_utilities/defect_dojo/applications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
273
286
|
devsecops_engine_tools/engine_utilities/defect_dojo/applications/component.py,sha256=Y6vA1nRfMCoqJEceRBDQ_QLpIKASqB-t8V1yqao-eUQ,1175
|
|
@@ -356,19 +369,19 @@ devsecops_engine_tools/engine_utilities/ssh/managment_private_key.py,sha256=Vvrr
|
|
|
356
369
|
devsecops_engine_tools/engine_utilities/trivy_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
357
370
|
devsecops_engine_tools/engine_utilities/trivy_utils/infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
358
371
|
devsecops_engine_tools/engine_utilities/trivy_utils/infrastructure/driven_adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
359
|
-
devsecops_engine_tools/engine_utilities/trivy_utils/infrastructure/driven_adapters/trivy_deserialize_output.py,sha256=
|
|
372
|
+
devsecops_engine_tools/engine_utilities/trivy_utils/infrastructure/driven_adapters/trivy_deserialize_output.py,sha256=7jiZ3FRKEnWw542ei6g4ZnGkpX8RInWND_dGwJVgbrs,5321
|
|
360
373
|
devsecops_engine_tools/engine_utilities/trivy_utils/infrastructure/driven_adapters/trivy_manager_scan_utils.py,sha256=9bUT0V-EFhdik8aNuGTI2i4OnT1YvFT7s7xu5M5sejM,2888
|
|
361
374
|
devsecops_engine_tools/engine_utilities/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
362
375
|
devsecops_engine_tools/engine_utilities/utils/api_error.py,sha256=yRbad5gNUHh5nALBKkRDi-d98JPmqAhw-QJEGW4psrw,528
|
|
363
376
|
devsecops_engine_tools/engine_utilities/utils/dataclass_classmethod.py,sha256=S-w6pybVKlyVBhV3HE3IGDvO4ByXxiVePP1JaMnISgM,4302
|
|
364
377
|
devsecops_engine_tools/engine_utilities/utils/datetime_parsing.py,sha256=2891pkh01dfW8E5CW2eTpsUF1t6k0rgbQf8BgkdrSEk,224
|
|
365
|
-
devsecops_engine_tools/engine_utilities/utils/logger_info.py,sha256=
|
|
378
|
+
devsecops_engine_tools/engine_utilities/utils/logger_info.py,sha256=rxNjzT0o64qfnmI5XyFXZRTz9C021o2QZim8nDQ1aKw,3572
|
|
366
379
|
devsecops_engine_tools/engine_utilities/utils/name_conversion.py,sha256=ADJrRGaxYSDe0ZRh6VHRf53H4sXPcb-vNP_i81PUn3I,307
|
|
367
380
|
devsecops_engine_tools/engine_utilities/utils/printers.py,sha256=amYAr9YQfYgR6jK9a2l26z3oovFPQ3FAKmhq6BKhEBA,623
|
|
368
381
|
devsecops_engine_tools/engine_utilities/utils/session_manager.py,sha256=Z0fdhB3r-dxU0nGSD9zW_B4r2Qol1rUnUCkhFR0U-HQ,487
|
|
369
382
|
devsecops_engine_tools/engine_utilities/utils/utils.py,sha256=HCjS900TBoNcHrC4LaiP-Kf9frVdtagF130qOUgnO2M,6757
|
|
370
|
-
devsecops_engine_tools-1.
|
|
371
|
-
devsecops_engine_tools-1.
|
|
372
|
-
devsecops_engine_tools-1.
|
|
373
|
-
devsecops_engine_tools-1.
|
|
374
|
-
devsecops_engine_tools-1.
|
|
383
|
+
devsecops_engine_tools-1.99.1.dist-info/METADATA,sha256=_za0wvZSQ4PtBLwRQ-VqLZR3MsjAnbrAsnDh8IudvMU,3200
|
|
384
|
+
devsecops_engine_tools-1.99.1.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
385
|
+
devsecops_engine_tools-1.99.1.dist-info/entry_points.txt,sha256=OWAww5aBsGeMv0kWhSgVNB0ySKKpYuJd4dly0ikFPkc,283
|
|
386
|
+
devsecops_engine_tools-1.99.1.dist-info/top_level.txt,sha256=ge6y0X_xBAU1aG3EMWFtl9djbVyg5BxuSp2r2Lg6EQU,23
|
|
387
|
+
devsecops_engine_tools-1.99.1.dist-info/RECORD,,
|
|
File without changes
|
{devsecops_engine_tools-1.98.1.dist-info → devsecops_engine_tools-1.99.1.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{devsecops_engine_tools-1.98.1.dist-info → devsecops_engine_tools-1.99.1.dist-info}/top_level.txt
RENAMED
|
File without changes
|