scanoss 1.30.0__py3-none-any.whl → 1.31.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.
@@ -1 +1 @@
1
- date: 20250722103216, utime: 1753180336
1
+ date: 20250808142900, utime: 1754663340
@@ -25,85 +25,65 @@ SPDX-License-Identifier: MIT
25
25
  import base64
26
26
  import json
27
27
  import traceback
28
- from dataclasses import dataclass
29
- from typing import Optional
30
28
 
31
29
  import requests
32
30
 
33
- from scanoss.cyclonedx import CycloneDx
34
-
31
+ from ..cyclonedx import CycloneDx
35
32
  from ..scanossbase import ScanossBase
33
+ from ..services.dependency_track_service import DependencyTrackService
36
34
  from ..utils.file import validate_json_file
37
35
 
38
36
 
39
- @dataclass
40
- class DependencyTrackExporterConfig:
41
- debug: bool = False
42
- trace: bool = False
43
- quiet: bool = False
44
- dt_url: str = None
45
- dt_apikey: str = None
46
- dt_projectid: Optional[str] = None
47
- dt_projectname: Optional[str] = None
48
- dt_projectversion: Optional[str] = None
37
+ def _build_payload(encoded_sbom: str, project_id, project_name, project_version) -> dict:
38
+ """
39
+ Build the API payload
49
40
 
41
+ Args:
42
+ encoded_sbom: Base64 encoded SBOM
50
43
 
51
- def create_dependency_track_exporter_config_from_args(args) -> DependencyTrackExporterConfig:
52
- return DependencyTrackExporterConfig(
53
- debug=getattr(args, 'debug', False),
54
- trace=getattr(args, 'trace', False),
55
- quiet=getattr(args, 'quiet', False),
56
- dt_url=getattr(args, 'dt_url', None),
57
- dt_apikey=getattr(args, 'dt_apikey', None),
58
- dt_projectid=getattr(args, 'dt_projectid', None),
59
- dt_projectname=getattr(args, 'dt_projectname', None),
60
- dt_projectversion=getattr(args, 'dt_projectversion', None),
61
- )
44
+ Returns:
45
+ API payload dictionary
46
+ """
47
+ if project_id:
48
+ return {'project': project_id, 'bom': encoded_sbom}
49
+ else:
50
+ return {
51
+ 'projectName': project_name,
52
+ 'projectVersion': project_version,
53
+ 'autoCreate': True,
54
+ 'bom': encoded_sbom,
55
+ }
62
56
 
63
57
 
64
58
  class DependencyTrackExporter(ScanossBase):
65
59
  """
66
60
  Class for exporting SBOM files to Dependency Track
67
61
  """
68
-
69
- def __init__(
62
+ def __init__( # noqa: PLR0913
70
63
  self,
71
- config: DependencyTrackExporterConfig,
64
+ url: str = None,
65
+ apikey: str = None,
66
+ output: str = None,
72
67
  debug: bool = False,
73
68
  trace: bool = False,
74
- quiet: bool = False,
69
+ quiet: bool = False
75
70
  ):
76
71
  """
77
72
  Initialize DependencyTrackExporter
78
73
 
79
74
  Args:
80
- config: Configuration parameters for the dependency track exporter
75
+ url: Dependency Track URL
76
+ apikey: Dependency Track API Key
77
+ output: File to store output response data (optional)
81
78
  debug: Enable debug output
82
79
  trace: Enable trace output
83
80
  quiet: Enable quiet mode
84
81
  """
85
82
  super().__init__(debug=debug, trace=trace, quiet=quiet)
86
-
87
- self.dt_url = config.dt_url.rstrip('/')
88
- self.dt_apikey = config.dt_apikey
89
- self.dt_projectid = config.dt_projectid
90
- self.dt_projectname = config.dt_projectname
91
- self.dt_projectversion = config.dt_projectversion
92
-
93
- self._validate_config()
94
-
95
- def _validate_config(self):
96
- """
97
- Validate that the configuration is valid.
98
- """
99
- has_id = bool(self.dt_projectid)
100
- has_name_version = bool(self.dt_projectname and self.dt_projectversion)
101
-
102
- if not (has_id or has_name_version):
103
- raise ValueError('Either --dt-projectid OR (--dt-projectname and --dt-projectversion) must be provided')
104
-
105
- if has_id and has_name_version:
106
- self.print_debug('Both DT project ID and name/version provided. Using project ID.')
83
+ self.url = url.rstrip('/')
84
+ self.apikey = apikey
85
+ self.output = output
86
+ self.dt_service = DependencyTrackService(self.apikey, self.url, debug=debug, trace=trace, quiet=quiet)
107
87
 
108
88
  def _read_and_validate_sbom(self, input_file: str) -> dict:
109
89
  """
@@ -116,7 +96,7 @@ class DependencyTrackExporter(ScanossBase):
116
96
  Parsed SBOM content as dictionary
117
97
 
118
98
  Raises:
119
- ValueError: If file doesn't exist or is invalid or not a valid CycloneDX SBOM
99
+ ValueError: If the file doesn't exist or is invalid or not a valid CycloneDX SBOM
120
100
  """
121
101
  result = validate_json_file(input_file)
122
102
  if not result.is_valid:
@@ -125,7 +105,6 @@ class DependencyTrackExporter(ScanossBase):
125
105
  cdx = CycloneDx(debug=self.debug)
126
106
  if not cdx.is_cyclonedx_json(json.dumps(result.data)):
127
107
  raise ValueError(f'Input file is not a valid CycloneDX SBOM: {input_file}')
128
-
129
108
  return result.data
130
109
 
131
110
  def _encode_sbom(self, sbom_content: dict) -> str:
@@ -138,84 +117,106 @@ class DependencyTrackExporter(ScanossBase):
138
117
  Returns:
139
118
  Base64 encoded string
140
119
  """
120
+ if not sbom_content:
121
+ self.print_stderr('Warning: Empty SBOM content')
141
122
  json_str = json.dumps(sbom_content, separators=(',', ':'))
142
123
  encoded = base64.b64encode(json_str.encode('utf-8')).decode('utf-8')
143
124
  return encoded
144
125
 
145
- def _build_payload(self, encoded_sbom: str) -> dict:
126
+ def upload_sbom_file(self, input_file, project_id, project_name, project_version, output_file):
146
127
  """
147
- Build the API payload
128
+ Uploads an SBOM file to the specified project with an
129
+ optional output file and processes the file content for validation.
148
130
 
149
131
  Args:
150
- encoded_sbom: Base64 encoded SBOM
132
+ input_file (str): The path to the SBOM file to be read and uploaded.
133
+ project_id (str): The unique identifier of the project to which the SBOM is being uploaded.
134
+ project_name (str): The name of the project to which the SBOM is being uploaded.
135
+ project_version (str): The version of the project to which the SBOM is being uploaded.
136
+ output_file (str): The path to save output related to the SBOM upload process.
151
137
 
152
138
  Returns:
153
- API payload dictionary
154
- """
155
- if self.dt_projectid:
156
- return {'project': self.dt_projectid, 'bom': encoded_sbom}
157
- else:
158
- return {
159
- 'projectName': self.dt_projectname,
160
- 'projectVersion': self.dt_projectversion,
161
- 'autoCreate': True,
162
- 'bom': encoded_sbom,
163
- }
164
-
165
- def upload_sbom(self, input_file: str) -> bool:
166
- """
167
- Upload SBOM file to Dependency Track
139
+ bool: Returns True if the SBOM file was uploaded successfully, False otherwise.
168
140
 
169
- Args:
170
- input_file: Path to the SBOM file
171
-
172
- Returns:
173
- True if successful, False otherwise
141
+ Raises:
142
+ ValueError: Raised if there are validation issues with the SBOM content.
174
143
  """
175
144
  try:
176
- self.print_stderr(f'Reading SBOM file: {input_file}')
145
+ if not self.quiet:
146
+ self.print_stderr(f'Reading SBOM file: {input_file}')
177
147
  sbom_content = self._read_and_validate_sbom(input_file)
148
+ return self.upload_sbom_contents(sbom_content, project_id, project_name, project_version, output_file)
149
+ except ValueError as e:
150
+ self.print_stderr(f'Validation error: {e}')
151
+ return False
178
152
 
179
- self.print_debug('Encoding SBOM to base64')
180
- encoded_sbom = self._encode_sbom(sbom_content)
181
-
182
- payload = self._build_payload(encoded_sbom)
153
+ def upload_sbom_contents(self, sbom_content: dict, project_id, project_name, project_version, output_file) -> bool:
154
+ """
155
+ Uploads an SBOM to a Dependency Track server.
183
156
 
184
- url = f'{self.dt_url}/api/v1/bom'
185
- headers = {'Content-Type': 'application/json', 'X-Api-Key': self.dt_apikey}
157
+ Parameters:
158
+ sbom_content (dict): The SBOM content in dictionary format to be uploaded.
159
+ project_id: The unique identifier for the project.
160
+ project_name: The name of the project in Dependency Track.
161
+ project_version: The version of the project in Dependency Track.
162
+ output_file: The path to the file where the token and UUID data
163
+ should be written. If not provided, the data will be written to
164
+ standard output.
186
165
 
187
- if self.trace:
188
- self.print_trace(f'URL: {url}')
189
- self.print_trace(f'Headers: {headers}')
190
- self.print_trace(f'Payload keys: {list(payload.keys())}')
166
+ Returns:
167
+ bool: True if the upload is successful; False otherwise.
191
168
 
169
+ Raises:
170
+ ValueError: If the SBOM encoding process fails.
171
+ requests.exceptions.RequestException: If an error occurs during the HTTP request.
172
+ Exception: For any other unexpected error.
173
+ """
174
+ if not project_id and not (project_name and project_version):
175
+ self.print_stderr('Error: Missing project id or name and version.')
176
+ return False
177
+ output = self.output
178
+ if output_file:
179
+ output = output_file
180
+ try:
181
+ self.print_debug('Encoding SBOM to base64')
182
+ payload = _build_payload(self._encode_sbom(sbom_content), project_id, project_name, project_version)
183
+ url = f'{self.url}/api/v1/bom'
184
+ headers = {'Content-Type': 'application/json', 'X-Api-Key': self.apikey}
185
+ self.print_trace(f'URL: {url}, Headers: {headers}, Payload keys: {list(payload.keys())}')
192
186
  self.print_msg('Uploading SBOM to Dependency Track...')
193
187
  response = requests.put(url, json=payload, headers=headers)
194
-
195
- if response.status_code in [200, 201]:
196
- self.print_stderr('SBOM uploaded successfully')
197
-
188
+ response.raise_for_status()
189
+ # Treat any 2xx status as success
190
+ if (requests.codes.ok <= response.status_code < requests.codes.multiple_choices and
191
+ response.status_code != requests.codes.no_content):
192
+ self.print_msg('SBOM uploaded successfully')
198
193
  try:
199
194
  response_data = response.json()
195
+ token = ''
196
+ project_uuid = project_id
200
197
  if 'token' in response_data:
201
- self.print_stderr(f'Upload token: {response_data["token"]}')
198
+ token = response_data['token']
199
+ if project_name and project_version:
200
+ project_data = self.dt_service.get_project_by_name_version(project_name, project_version)
201
+ if project_data:
202
+ project_uuid = project_data.get("uuid", project_id)
203
+ token_json = json.dumps(
204
+ {"token": token, "project_uuid": project_uuid},
205
+ indent=2
206
+ )
207
+ self.print_to_file_or_stdout(token_json, output)
202
208
  except json.JSONDecodeError:
203
209
  pass
204
-
205
210
  return True
206
211
  else:
207
212
  self.print_stderr(f'Upload failed with status code: {response.status_code}')
208
213
  self.print_stderr(f'Response: {response.text}')
209
- return False
210
-
211
214
  except ValueError as e:
212
- self.print_stderr(f'Validation error: {e}')
213
- return False
215
+ self.print_stderr(f'DT SBOM Upload Validation error: {e}')
214
216
  except requests.exceptions.RequestException as e:
215
- self.print_stderr(f'Request error: {e}')
216
- return False
217
+ self.print_stderr(f'DT API Request error: {e}')
217
218
  except Exception as e:
218
219
  self.print_stderr(f'Unexpected error: {e}')
219
220
  if self.debug:
220
221
  traceback.print_exc()
221
- return False
222
+ return False
scanoss/file_filters.py CHANGED
@@ -72,11 +72,7 @@ DEFAULT_SKIPPED_DIRS = {
72
72
  'htmlcov',
73
73
  '__pypackages__',
74
74
  'example',
75
- 'examples',
76
- 'docs',
77
- 'tests',
78
- 'doc',
79
- 'test',
75
+ 'examples'
80
76
  }
81
77
 
82
78
  DEFAULT_SKIPPED_DIRS_HFH = {