devsecops-engine-tools 1.25.0__py3-none-any.whl → 1.26.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.

Files changed (36) hide show
  1. devsecops_engine_tools/engine_core/src/applications/runner_engine_core.py +6 -0
  2. devsecops_engine_tools/engine_core/src/domain/model/component.py +7 -0
  3. devsecops_engine_tools/engine_core/src/domain/model/gateway/sbom_manager.py +11 -0
  4. devsecops_engine_tools/engine_core/src/domain/model/gateway/vulnerability_management_gateway.py +6 -0
  5. devsecops_engine_tools/engine_core/src/domain/usecases/handle_scan.py +125 -112
  6. devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/defect_dojo/defect_dojo.py +73 -14
  7. devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/syft/__init__.py +0 -0
  8. devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/syft/syft.py +122 -0
  9. devsecops_engine_tools/engine_core/src/infrastructure/entry_points/entry_point_core.py +3 -1
  10. devsecops_engine_tools/engine_sast/engine_iac/src/domain/usecases/iac_scan.py +1 -1
  11. devsecops_engine_tools/engine_sast/engine_iac/src/infrastructure/driven_adapters/checkov/checkov_tool.py +0 -3
  12. devsecops_engine_tools/engine_sca/engine_container/src/domain/model/gateways/images_gateway.py +4 -0
  13. devsecops_engine_tools/engine_sca/engine_container/src/domain/model/gateways/tool_gateway.py +1 -1
  14. devsecops_engine_tools/engine_sca/engine_container/src/domain/usecases/container_sca_scan.py +32 -8
  15. devsecops_engine_tools/engine_sca/engine_container/src/domain/usecases/set_input_core.py +30 -17
  16. devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/docker/docker_images.py +17 -0
  17. devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/prisma_cloud/prisma_cloud_manager_scan.py +80 -5
  18. devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/driven_adapters/trivy_tool/trivy_manager_scan.py +51 -14
  19. devsecops_engine_tools/engine_sca/engine_container/src/infrastructure/entry_points/entry_point_tool.py +8 -5
  20. devsecops_engine_tools/engine_sca/engine_dependencies/src/applications/runner_dependencies_scan.py +6 -3
  21. devsecops_engine_tools/engine_sca/engine_dependencies/src/infrastructure/entry_points/entry_point_tool.py +31 -4
  22. devsecops_engine_tools/engine_utilities/defect_dojo/__init__.py +2 -1
  23. devsecops_engine_tools/engine_utilities/defect_dojo/applications/component.py +29 -0
  24. devsecops_engine_tools/engine_utilities/defect_dojo/domain/models/component.py +20 -0
  25. devsecops_engine_tools/engine_utilities/defect_dojo/domain/user_case/component.py +11 -0
  26. devsecops_engine_tools/engine_utilities/defect_dojo/infraestructure/driver_adapters/component.py +52 -0
  27. devsecops_engine_tools/engine_utilities/sbom/__init__.py +0 -0
  28. devsecops_engine_tools/engine_utilities/sbom/deserealizator.py +24 -0
  29. devsecops_engine_tools/engine_utilities/sonarqube/src/infrastructure/entry_points/entry_point_report_sonar.py +32 -16
  30. devsecops_engine_tools/engine_utilities/utils/session_manager.py +4 -1
  31. devsecops_engine_tools/version.py +1 -1
  32. {devsecops_engine_tools-1.25.0.dist-info → devsecops_engine_tools-1.26.0.dist-info}/METADATA +1 -1
  33. {devsecops_engine_tools-1.25.0.dist-info → devsecops_engine_tools-1.26.0.dist-info}/RECORD +36 -26
  34. {devsecops_engine_tools-1.25.0.dist-info → devsecops_engine_tools-1.26.0.dist-info}/WHEEL +0 -0
  35. {devsecops_engine_tools-1.25.0.dist-info → devsecops_engine_tools-1.26.0.dist-info}/entry_points.txt +0 -0
  36. {devsecops_engine_tools-1.25.0.dist-info → devsecops_engine_tools-1.26.0.dist-info}/top_level.txt +0 -0
@@ -21,6 +21,7 @@ def init_engine_core(
21
21
  devops_platform_gateway: any,
22
22
  print_table_gateway: any,
23
23
  metrics_manager_gateway: any,
24
+ sbom_tool_gateway: any,
24
25
  args: any
25
26
  ):
26
27
  config_tool = devops_platform_gateway.get_remote_config(
@@ -28,7 +29,7 @@ def init_engine_core(
28
29
  )
29
30
  Printers.print_logo_tool(config_tool["BANNER"])
30
31
 
31
- if config_tool[args["tool"].upper()]["ENABLED"] == "true":
32
+ if config_tool[args["tool"].upper()]["ENABLED"]:
32
33
  if args["tool"] == "engine_risk":
33
34
  results, input_core = HandleRisk(
34
35
  vulnerability_management_gateway,
@@ -42,6 +43,7 @@ def init_engine_core(
42
43
  vulnerability_management_gateway,
43
44
  secrets_manager_gateway,
44
45
  devops_platform_gateway,
46
+ sbom_tool_gateway
45
47
  ).process(args, config_tool)
46
48
 
47
49
  results = BreakBuild(devops_platform_gateway, print_table_gateway).process(
@@ -115,7 +115,7 @@ class IacScan:
115
115
 
116
116
  if dict_args["folder_path"]:
117
117
  if (
118
- config_tool.update_service_file_name_cft == "True"
118
+ config_tool.update_service_file_name_cft
119
119
  and "cloudformation" in dict_args["platform"]
120
120
  ):
121
121
  files = os.listdir(os.path.join(os.getcwd(), dict_args["folder_path"]))
@@ -2,7 +2,6 @@ import yaml
2
2
  import subprocess
3
3
  import time
4
4
  import os
5
- import platform
6
5
  import queue
7
6
  import threading
8
7
  import json
@@ -171,7 +170,6 @@ class CheckovTool(ToolGateway):
171
170
  f"{config_tool[self.TOOL_CHECKOV]['EXTERNAL_CHECKS_GIT']}/{self.framework_mapping[rule]}"
172
171
  ]
173
172
  if config_tool[self.TOOL_CHECKOV]["USE_EXTERNAL_CHECKS_GIT"]
174
- == "True"
175
173
  and agent_env is not None
176
174
  and rule in self.framework_external_checks
177
175
  else []
@@ -180,7 +178,6 @@ class CheckovTool(ToolGateway):
180
178
  external_checks_dir=(
181
179
  f"/tmp/rules/{self.framework_mapping[rule]}"
182
180
  if config_tool[self.TOOL_CHECKOV]["USE_EXTERNAL_CHECKS_DIR"]
183
- == "True"
184
181
  and rule in self.framework_external_checks
185
182
  else []
186
183
  ),
@@ -5,3 +5,7 @@ class ImagesGateway(metaclass=ABCMeta):
5
5
  @abstractmethod
6
6
  def list_images(self, image_to_scan) -> str:
7
7
  "get image to scan"
8
+
9
+ @abstractmethod
10
+ def get_base_image(self, image_to_scan) -> str:
11
+ "get base image"
@@ -3,5 +3,5 @@ from abc import ABCMeta, abstractmethod
3
3
 
4
4
  class ToolGateway(metaclass=ABCMeta):
5
5
  @abstractmethod
6
- def run_tool_container_sca(self, dict_args, secret_tool, token_engine_container, scan_image, release) -> str:
6
+ def run_tool_container_sca(self, dict_args, secret_tool, token_engine_container, scan_image, release, base_image, exclusions, generate_sbom):
7
7
  "run tool container sca"
@@ -18,19 +18,21 @@ class ContainerScaScan:
18
18
  remote_config,
19
19
  tool_images: ImagesGateway,
20
20
  tool_deseralizator: DeseralizatorGateway,
21
- build_id,
21
+ branch,
22
22
  secret_tool,
23
23
  token_engine_container,
24
24
  image_to_scan,
25
+ exclusions
25
26
  ):
26
27
  self.tool_run = tool_run
27
28
  self.remote_config = remote_config
28
29
  self.tool_images = tool_images
29
30
  self.tool_deseralizator = tool_deseralizator
30
- self.build_id = build_id
31
+ self.branch = branch
31
32
  self.secret_tool = secret_tool
32
33
  self.token_engine_container = token_engine_container
33
34
  self.image_to_scan = image_to_scan
35
+ self.exclusions = exclusions
34
36
 
35
37
  def get_image(self, image_to_scan):
36
38
  """
@@ -41,6 +43,15 @@ class ContainerScaScan:
41
43
  """
42
44
  return self.tool_images.list_images(image_to_scan)
43
45
 
46
+ def get_base_image(self, matching_image):
47
+ """
48
+ Process the base image.
49
+
50
+ Returns:
51
+ String: base image.
52
+ """
53
+ return self.tool_images.get_base_image(matching_image)
54
+
44
55
  def get_images_already_scanned(self):
45
56
  """
46
57
  Create images scanned file if it does not exist and get the images that have already been scanned.
@@ -66,21 +77,34 @@ class ContainerScaScan:
66
77
  Returns:
67
78
  string: file scanning results name.
68
79
  """
69
- matching_image = self.get_image(self.image_to_scan)
80
+ base_image = None
70
81
  image_scanned = None
82
+ matching_image = self.get_image(self.image_to_scan)
83
+ if self.remote_config['GET_IMAGE_BASE']:
84
+ base_image = self.get_base_image(matching_image)
85
+ sbom_components = None
86
+ generate_sbom = self.remote_config["SBOM"]["ENABLED"] and any(
87
+ branch in str(self.branch)
88
+ for branch in self.remote_config["SBOM"]["BRANCH_FILTER"]
89
+ )
71
90
  if matching_image:
72
91
  image_name = matching_image.tags[0]
73
- result_file = image_name.replace("/","_") + "_scan_result.json"
92
+ result_file = image_name.replace("/", "_") + "_scan_result.json"
74
93
  if image_name in self.get_images_already_scanned():
75
94
  print(f"The image {image_name} has already been scanned previously.")
76
- return image_scanned
77
- image_scanned = self.tool_run.run_tool_container_sca(
78
- self.remote_config, self.secret_tool, self.token_engine_container, image_name, result_file
95
+ return image_scanned, base_image, sbom_components
96
+ image_scanned, sbom_components = self.tool_run.run_tool_container_sca(
97
+ self.remote_config,
98
+ self.secret_tool,
99
+ self.token_engine_container,
100
+ image_name,
101
+ result_file, base_image, self.exclusions,
102
+ generate_sbom,
79
103
  )
80
104
  self.set_image_scanned(image_name)
81
105
  else:
82
106
  print(f"'Not image found for {self.image_to_scan}'. Tool skipped.")
83
- return image_scanned
107
+ return image_scanned, base_image, sbom_components
84
108
 
85
109
  def deseralizator(self, image_scanned):
86
110
  """
@@ -12,25 +12,37 @@ class SetInputCore:
12
12
  self.tool = tool
13
13
  self.stage = stage
14
14
 
15
- def get_exclusions(self, exclusions_data, pipeline_name, tool):
16
- list_exclusions = [
17
- Exclusions(
18
- id=item.get("id", ""),
19
- where=item.get("where", ""),
20
- cve_id=item.get("cve_id", ""),
21
- create_date=item.get("create_date", ""),
22
- expired_date=item.get("expired_date", ""),
23
- severity=item.get("severity", ""),
24
- hu=item.get("hu", ""),
25
- reason=item.get("reason", "Risk acceptance"),
26
- )
27
- for key, value in exclusions_data.items()
28
- if key in {"All", pipeline_name} and value.get(tool)
29
- for item in value[tool]
30
- ]
15
+ def get_exclusions(self, exclusions_data, pipeline_name, tool, base_image):
16
+ list_exclusions = []
17
+ print("The base image used is:", base_image)
18
+ for key, value in exclusions_data.items():
19
+ if key not in {"All", pipeline_name} or not value.get(tool):
20
+ continue
21
+
22
+ for item in value[tool]:
23
+ if key == "All":
24
+ source_images = item.get("source_images", [])
25
+ if source_images and base_image is None:
26
+ continue
27
+ if source_images and not any(base_image in source for source in source_images):
28
+ continue
29
+
30
+ list_exclusions.append(
31
+ Exclusions(
32
+ id=item.get("id", ""),
33
+ where=item.get("where", ""),
34
+ cve_id=item.get("cve_id", ""),
35
+ create_date=item.get("create_date", ""),
36
+ expired_date=item.get("expired_date", ""),
37
+ severity=item.get("severity", ""),
38
+ hu=item.get("hu", ""),
39
+ reason=item.get("reason", "Risk acceptance"),
40
+ )
41
+ )
42
+
31
43
  return list_exclusions
32
44
 
33
- def set_input_core(self, image_scanned):
45
+ def set_input_core(self, image_scanned,base_image):
34
46
  """
35
47
  Set the input core.
36
48
 
@@ -42,6 +54,7 @@ class SetInputCore:
42
54
  self.exclusions,
43
55
  self.pipeline_name,
44
56
  self.tool,
57
+ base_image
45
58
  ),
46
59
  Utils.update_threshold(
47
60
  self,
@@ -31,3 +31,20 @@ class DockerImages(ImagesGateway):
31
31
  logger.error(
32
32
  f"Error listing images, docker must be running and added to PATH: {e}"
33
33
  )
34
+
35
+ def get_base_image(self, matching_image):
36
+ try:
37
+ client = docker.from_env()
38
+ image_details = client.api.inspect_image(matching_image.id)
39
+ labels = image_details.get("Config", {}).get("Labels", {})
40
+ source_image = labels.get("source-image")
41
+ if source_image:
42
+ logger.info(f"Base image for '{matching_image}' from source-image label: {source_image}")
43
+ return source_image
44
+
45
+ logger.warning(f"Base image not found for '{matching_image}'.")
46
+ return None
47
+
48
+ except Exception as e:
49
+ logger.error(f"Error getting base image: {e}")
50
+ return None
@@ -4,9 +4,13 @@ import os
4
4
  import subprocess
5
5
  import logging
6
6
  import base64
7
+ import json
7
8
  from devsecops_engine_tools.engine_sca.engine_container.src.domain.model.gateways.tool_gateway import (
8
9
  ToolGateway,
9
10
  )
11
+ from devsecops_engine_tools.engine_utilities.sbom.deserealizator import (
12
+ get_list_component,
13
+ )
10
14
  from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
11
15
  from devsecops_engine_tools.engine_utilities import settings
12
16
 
@@ -68,19 +72,81 @@ class PrismaCloudManagerScan(ToolGateway):
68
72
  text=True,
69
73
  )
70
74
  print(f"The image {image_name} was scanned")
71
-
72
75
  return result_file
73
76
 
74
77
  except subprocess.CalledProcessError as e:
75
78
  logger.error(f"Error during image scan of {image_name}: {e.stderr}")
76
79
 
80
+ def _write_image_base(self, result_file, base_image, exclusions_data):
81
+ try:
82
+ with open(result_file, "r") as file:
83
+ data = json.load(file)
84
+
85
+ prisma_exclusions = exclusions_data.get("All", {}).get("PRISMA", [])
86
+ modified = False
87
+ for result in data.get("results", []):
88
+ for vulnerability in result.get("vulnerabilities", []):
89
+ for exclusion in prisma_exclusions:
90
+ if (
91
+ vulnerability.get("id") == exclusion.get("id") and
92
+ any(image.startswith(base_image) for image in exclusion.get("source_images", []))
93
+ ):
94
+ vulnerability["baseImage"] = base_image
95
+ modified = True
96
+
97
+ if modified:
98
+ with open(result_file, "w") as file:
99
+ json.dump(data, file, indent=4)
100
+ except subprocess.CalledProcessError as e:
101
+ logger.error(f"Error during write image base of {base_image}: {e.stderr}")
102
+
103
+ def _generate_sbom(self, image_scanned, remoteconfig, prisma_secret_key, image_name):
104
+
105
+ url = f"{remoteconfig['PRISMA_CLOUD']['PRISMA_CONSOLE_URL']}/api/{remoteconfig['PRISMA_CLOUD']['PRISMA_API_VERSION']}/sbom/download/cli-images"
106
+ credentials = base64.b64encode(
107
+ f"{remoteconfig['PRISMA_CLOUD']['PRISMA_ACCESS_KEY']}:{prisma_secret_key}".encode()
108
+ ).decode()
109
+ headers = {"Authorization": f"Basic {credentials}"}
110
+ try:
111
+
112
+ with open(image_scanned, "rb") as file:
113
+ image_object = file.read()
114
+ json_data = json.loads(image_object)
115
+
116
+ if not json_data["results"]:
117
+ print("No results found in the scan, SBOM not generated")
118
+ return None
119
+
120
+ response = requests.get(
121
+ url,
122
+ headers=headers,
123
+ params={
124
+ "id": json_data["results"][0]["scanID"],
125
+ "sbomFormat": remoteconfig["PRISMA_CLOUD"]["SBOM_FORMAT"],
126
+ },
127
+ )
128
+ response.raise_for_status()
129
+
130
+ result_sbom = f"{image_name.replace('/', '_')}_SBOM.json"
131
+ with open(result_sbom, "wb") as file:
132
+ file.write(response.content)
133
+
134
+ print(f"SBOM generated and saved to: {result_sbom}")
135
+
136
+ return get_list_component(result_sbom, remoteconfig["PRISMA_CLOUD"]["SBOM_FORMAT"])
137
+ except Exception as e:
138
+ logger.error(f"Error generating SBOM: {e}")
139
+
77
140
  def run_tool_container_sca(
78
- self, remoteconfig, secret_tool, token_engine_container, image_name, result_file
141
+ self, remoteconfig, secret_tool, token_engine_container, image_name, result_file, base_image, exclusions, generate_sbom
79
142
  ):
80
- prisma_secret_key = secret_tool["token_prisma_cloud"] if secret_tool else token_engine_container
143
+ prisma_secret_key = (
144
+ secret_tool["token_prisma_cloud"] if secret_tool else token_engine_container
145
+ )
81
146
  file_path = os.path.join(
82
147
  os.getcwd(), remoteconfig["PRISMA_CLOUD"]["TWISTCLI_PATH"]
83
148
  )
149
+ sbom_components = None
84
150
 
85
151
  if not os.path.exists(file_path):
86
152
  self.download_twistcli(
@@ -95,7 +161,16 @@ class PrismaCloudManagerScan(ToolGateway):
95
161
  image_name,
96
162
  result_file,
97
163
  remoteconfig,
98
- prisma_secret_key,
164
+ prisma_secret_key
99
165
  )
166
+ if base_image:
167
+ self._write_image_base(result_file, base_image, exclusions)
168
+ if generate_sbom:
169
+ sbom_components = self._generate_sbom(
170
+ image_scanned,
171
+ remoteconfig,
172
+ prisma_secret_key,
173
+ image_name
174
+ )
100
175
 
101
- return image_scanned
176
+ return image_scanned, sbom_components
@@ -7,10 +7,15 @@ import platform
7
7
  import requests
8
8
  import tarfile
9
9
  import zipfile
10
+ import json
10
11
 
11
12
  from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
12
13
  from devsecops_engine_tools.engine_utilities import settings
13
14
 
15
+ from devsecops_engine_tools.engine_utilities.sbom.deserealizator import (
16
+ get_list_component,
17
+ )
18
+
14
19
  logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
15
20
 
16
21
 
@@ -23,9 +28,9 @@ class TrivyScan(ToolGateway):
23
28
  except Exception as e:
24
29
  logger.error(f"Error downloading trivy: {e}")
25
30
 
26
- def install_tool(self, file, url):
31
+ def install_tool(self, file, url, command_prefix):
27
32
  installed = subprocess.run(
28
- ["which", "./trivy"],
33
+ ["which", command_prefix],
29
34
  stdout=subprocess.PIPE,
30
35
  stderr=subprocess.PIPE,
31
36
  )
@@ -34,25 +39,30 @@ class TrivyScan(ToolGateway):
34
39
  self.download_tool(file, url)
35
40
  with tarfile.open(file, 'r:gz') as tar_file:
36
41
  tar_file.extract(member=tar_file.getmember("trivy"))
42
+ return "./trivy"
37
43
  except Exception as e:
38
44
  logger.error(f"Error installing trivy: {e}")
45
+ else:
46
+ return installed.stdout.decode().strip()
39
47
 
40
- def install_tool_windows(self, file, url):
48
+ def install_tool_windows(self, file, url, command_prefix):
41
49
  try:
42
50
  subprocess.run(
43
- ["./trivy.exe", "--version"],
51
+ [command_prefix, "--version"],
44
52
  stdout=subprocess.PIPE,
45
53
  stderr=subprocess.PIPE,
46
54
  )
55
+ return command_prefix
47
56
  except:
48
57
  try:
49
58
  self.download_tool(file, url)
50
59
  with zipfile.ZipFile(file, 'r') as zip_file:
51
60
  zip_file.extract(member="trivy.exe")
61
+ return "./trivy.exe"
52
62
  except Exception as e:
53
63
  logger.error(f"Error installing trivy: {e}")
54
64
 
55
- def scan_image(self, prefix, image_name, result_file):
65
+ def scan_image(self, prefix, image_name, result_file, base_image):
56
66
  command = [
57
67
  prefix,
58
68
  "--scanners",
@@ -78,30 +88,57 @@ class TrivyScan(ToolGateway):
78
88
  except Exception as e:
79
89
  logger.error(f"Error during image scan of {image_name}: {e}")
80
90
 
81
- def run_tool_container_sca(self, remoteconfig, secret_tool, token_engine_container, image_name, result_file):
91
+ def _generate_sbom(self, prefix, image_name, remoteconfig):
92
+ result_sbom = f"{image_name.replace('/', '_')}_SBOM.json"
93
+ command = [
94
+ prefix,
95
+ "image",
96
+ "--format",
97
+ remoteconfig["TRIVY"]["SBOM_FORMAT"],
98
+ "--output",
99
+ result_sbom
100
+ ]
101
+ command.extend(["--quiet", image_name])
102
+ try:
103
+ subprocess.run(
104
+ command,
105
+ check=True,
106
+ stdout=subprocess.PIPE,
107
+ stderr=subprocess.PIPE,
108
+ text=True,
109
+ )
110
+ print(f"SBOM generated and saved to: {result_sbom}")
111
+
112
+ return get_list_component(result_sbom, remoteconfig["TRIVY"]["SBOM_FORMAT"])
113
+
114
+ except Exception as e:
115
+ logger.error(f"Error generating SBOM: {e}")
116
+
117
+ def run_tool_container_sca(self, remoteconfig, secret_tool, token_engine_container, image_name, result_file, base_image, exclusions, generate_sbom):
82
118
  trivy_version = remoteconfig["TRIVY"]["TRIVY_VERSION"]
83
119
  os_platform = platform.system()
84
120
  arch_platform = platform.architecture()[0]
85
121
  base_url = f"https://github.com/aquasecurity/trivy/releases/download/v{trivy_version}/"
122
+ sbom_components = None
86
123
 
124
+ command_prefix = "trivy"
87
125
  if os_platform == "Linux":
88
126
  file=f"trivy_{trivy_version}_Linux-{arch_platform}.tar.gz"
89
- self.install_tool(file, base_url+file)
90
- command_prefix = "./trivy"
127
+ command_prefix = self.install_tool(file, base_url+file, "trivy")
91
128
  elif os_platform == "Darwin":
92
129
  file=f"trivy_{trivy_version}_macOS-{arch_platform}.tar.gz"
93
- self.install_tool(file, base_url+file)
94
- command_prefix = "./trivy"
130
+ command_prefix = self.install_tool(file, base_url+file, "trivy")
95
131
  elif os_platform == "Windows":
96
132
  file=f"trivy_{trivy_version}_windows-{arch_platform}.zip"
97
- self.install_tool_windows(file, base_url+file)
98
- command_prefix = "./trivy.exe"
133
+ command_prefix = self.install_tool_windows(file, base_url+file, "trivy.exe")
99
134
  else:
100
135
  logger.warning(f"{os_platform} is not supported.")
101
136
  return None
102
137
 
103
138
  image_scanned = (
104
- self.scan_image(command_prefix, image_name, result_file)
139
+ self.scan_image(command_prefix, image_name, result_file, base_image)
105
140
  )
141
+ if generate_sbom:
142
+ sbom_components = self._generate_sbom(command_prefix, image_name, remoteconfig)
106
143
 
107
- return image_scanned
144
+ return image_scanned, sbom_components
@@ -34,10 +34,12 @@ def init_engine_sca_rm(
34
34
  )
35
35
  skip_flag = handle_remote_config_patterns.skip_from_exclusion()
36
36
  scan_flag = handle_remote_config_patterns.ignore_analysis_pattern()
37
- build_id = tool_remote.get_variable("build_id")
37
+ branch = tool_remote.get_variable("branch_tag")
38
38
  stage = tool_remote.get_variable("stage")
39
39
  image_to_scan = dict_args["image_to_scan"]
40
40
  image_scanned = None
41
+ base_image = None
42
+ sbom_components = None
41
43
  deseralized = []
42
44
  input_core = SetInputCore(remote_config, exclusions, pipeline_name, tool, stage)
43
45
  if scan_flag and not (skip_flag):
@@ -46,17 +48,18 @@ def init_engine_sca_rm(
46
48
  remote_config,
47
49
  tool_images,
48
50
  tool_deseralizator,
49
- build_id,
51
+ branch,
50
52
  secret_tool,
51
53
  dict_args["token_engine_container"],
52
54
  image_to_scan,
55
+ exclusions
53
56
  )
54
- image_scanned = container_sca_scan.process()
57
+ image_scanned, base_image, sbom_components = container_sca_scan.process()
55
58
  if image_scanned:
56
59
  deseralized = container_sca_scan.deseralizator(image_scanned)
57
60
  else:
58
61
  print("Tool skipped by DevSecOps policy")
59
62
  dict_args["send_metrics"] = "false"
60
- core_input = input_core.set_input_core(image_scanned)
63
+ core_input = input_core.set_input_core(image_scanned,base_image)
61
64
 
62
- return deseralized, core_input
65
+ return deseralized, core_input, sbom_components
@@ -16,20 +16,22 @@ from devsecops_engine_tools.engine_sca.engine_dependencies.src.infrastructure.en
16
16
 
17
17
 
18
18
  def runner_engine_dependencies(
19
- dict_args, config_tool, secret_tool, devops_platform_gateway
19
+ dict_args, config_tool, secret_tool, devops_platform_gateway, sbom_tool_gateway
20
20
  ):
21
21
  try:
22
22
  tools_mapping = {
23
- "XRAY": {"tool_run": XrayScan, "tool_deserializator": XrayDeserializator},
23
+ "XRAY": {"tool_run": XrayScan, "tool_deserializator": XrayDeserializator, "tool_sbom": sbom_tool_gateway},
24
24
  "DEPENDENCY_CHECK": {
25
25
  "tool_run": DependencyCheckTool,
26
26
  "tool_deserializator": DependencyCheckDeserialize,
27
+ "tool_sbom": sbom_tool_gateway
27
28
  },
28
29
  }
29
30
 
30
31
  selected_tool = config_tool["ENGINE_DEPENDENCIES"]["TOOL"]
31
32
  tool_run = tools_mapping[selected_tool]["tool_run"]()
32
33
  tool_deserializator = tools_mapping[selected_tool]["tool_deserializator"]()
34
+ tool_sbom = tools_mapping[selected_tool]["tool_sbom"]
33
35
 
34
36
  return init_engine_dependencies(
35
37
  tool_run,
@@ -37,7 +39,8 @@ def runner_engine_dependencies(
37
39
  tool_deserializator,
38
40
  dict_args,
39
41
  secret_tool,
40
- config_tool["ENGINE_DEPENDENCIES"]["TOOL"],
42
+ config_tool,
43
+ tool_sbom
41
44
  )
42
45
 
43
46
  except Exception as e:
@@ -7,9 +7,14 @@ from devsecops_engine_tools.engine_sca.engine_dependencies.src.domain.usecases.s
7
7
  from devsecops_engine_tools.engine_sca.engine_dependencies.src.domain.usecases.handle_remote_config_patterns import (
8
8
  HandleRemoteConfigPatterns,
9
9
  )
10
+ from devsecops_engine_tools.engine_core.src.domain.model.gateway.devops_platform_gateway import (
11
+ DevopsPlatformGateway,
12
+ )
13
+ from devsecops_engine_tools.engine_core.src.domain.model.gateway.sbom_manager import (
14
+ SbomManagerGateway,
15
+ )
10
16
 
11
17
  import os
12
- import sys
13
18
 
14
19
  from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
15
20
  from devsecops_engine_tools.engine_utilities import settings
@@ -18,7 +23,13 @@ logger = MyLogger.__call__(**settings.SETTING_LOGGER).get_logger()
18
23
 
19
24
 
20
25
  def init_engine_dependencies(
21
- tool_run, tool_remote, tool_deserializator, dict_args, secret_tool, tool
26
+ tool_run,
27
+ tool_remote: DevopsPlatformGateway,
28
+ tool_deserializator,
29
+ dict_args,
30
+ secret_tool,
31
+ config_tool,
32
+ tool_sbom: SbomManagerGateway,
22
33
  ):
23
34
  remote_config = tool_remote.get_remote_config(
24
35
  dict_args["remote_config_repo"],
@@ -40,7 +51,14 @@ def init_engine_dependencies(
40
51
 
41
52
  dependencies_scanned = None
42
53
  deserialized = []
43
- input_core = SetInputCore(remote_config, exclusions, pipeline_name, tool)
54
+ sbom_components = None
55
+ config_sbom = config_tool["SBOM_MANAGER"]
56
+ input_core = SetInputCore(
57
+ remote_config,
58
+ exclusions,
59
+ pipeline_name,
60
+ config_tool["ENGINE_DEPENDENCIES"]["TOOL"],
61
+ )
44
62
 
45
63
  if scan_flag and not (skip_flag):
46
64
  to_scan = dict_args["folder_path"] if dict_args["folder_path"] else os.getcwd()
@@ -55,6 +73,15 @@ def init_engine_dependencies(
55
73
  to_scan,
56
74
  secret_tool,
57
75
  )
76
+ if config_sbom["ENABLED"] and any(
77
+ branch in str(tool_remote.get_variable("branch_tag"))
78
+ for branch in config_sbom["BRANCH_FILTER"]
79
+ ):
80
+ sbom_components = tool_sbom.get_components(
81
+ to_scan,
82
+ config_sbom,
83
+ pipeline_name
84
+ )
58
85
  dependencies_scanned = dependencies_sca_scan.process()
59
86
  deserialized = (
60
87
  dependencies_sca_scan.deserializator(dependencies_scanned)
@@ -69,4 +96,4 @@ def init_engine_dependencies(
69
96
 
70
97
  core_input = input_core.set_input_core(dependencies_scanned)
71
98
 
72
- return deserialized, core_input
99
+ return deserialized, core_input, sbom_components
@@ -4,4 +4,5 @@ from .applications.defect_dojo import DefectDojo
4
4
  from .applications.finding import Finding
5
5
  from .applications.connect import Connect
6
6
  from .applications.engagement import Engagement
7
- from .applications.product import Product
7
+ from .applications.product import Product
8
+ from .applications.component import Component
@@ -0,0 +1,29 @@
1
+ from devsecops_engine_tools.engine_utilities.utils.api_error import ApiError
2
+ from devsecops_engine_tools.engine_utilities.utils.logger_info import MyLogger
3
+ from devsecops_engine_tools.engine_utilities.defect_dojo.infraestructure.driver_adapters.component import ComponentRestConsumer
4
+ from devsecops_engine_tools.engine_utilities.defect_dojo.domain.user_case.component import ComponentUserCase
5
+ from devsecops_engine_tools.engine_utilities.settings import SETTING_LOGGER
6
+
7
+ logger = MyLogger.__call__(**SETTING_LOGGER).get_logger()
8
+
9
+ class Component:
10
+
11
+ @staticmethod
12
+ def get_component(session, request: dict):
13
+ try:
14
+ rest_component = ComponentRestConsumer(session=session)
15
+ uc = ComponentUserCase(rest_component)
16
+ return uc.get(request)
17
+ except ApiError as e:
18
+ logger.error(f"Error during get component: {e}")
19
+ raise e
20
+
21
+ @staticmethod
22
+ def create_component(session, request):
23
+ try:
24
+ rest_component = ComponentRestConsumer(session=session)
25
+ uc = ComponentUserCase(rest_component)
26
+ return uc.post(request)
27
+ except ApiError as e:
28
+ logger.error(f"Error during create component: {e}")
29
+ raise e