scanoss 1.31.2__py3-none-any.whl → 1.31.3__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.
scanoss/__init__.py CHANGED
@@ -22,4 +22,4 @@ SPDX-License-Identifier: MIT
22
22
  THE SOFTWARE.
23
23
  """
24
24
 
25
- __version__ = '1.31.2'
25
+ __version__ = '1.31.3'
scanoss/csvoutput.py CHANGED
@@ -21,11 +21,10 @@ SPDX-License-Identifier: MIT
21
21
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
22
  THE SOFTWARE.
23
23
  """
24
-
24
+ import csv
25
25
  import json
26
26
  import os.path
27
27
  import sys
28
- import csv
29
28
 
30
29
  from .scanossbase import ScanossBase
31
30
 
@@ -44,16 +43,20 @@ class CsvOutput(ScanossBase):
44
43
  self.output_file = output_file
45
44
  self.debug = debug
46
45
 
47
- def parse(self, data: json):
46
+ # TODO Refactor (fails linter)
47
+ def parse(self, data: json): #noqa PLR0912, PLR0915
48
48
  """
49
49
  Parse the given input (raw/plain) JSON string and return CSV summary
50
50
  :param data: json - JSON object
51
51
  :return: CSV dictionary
52
52
  """
53
- if not data:
53
+ if data is None:
54
54
  self.print_stderr('ERROR: No JSON data provided to parse.')
55
55
  return None
56
- self.print_debug(f'Processing raw results into CSV format...')
56
+ if len(data) == 0:
57
+ self.print_msg('Warning: Empty scan results provided. Returning empty CSV list.')
58
+ return []
59
+ self.print_debug('Processing raw results into CSV format...')
57
60
  csv_dict = []
58
61
  row_id = 1
59
62
  for f in data:
@@ -92,7 +95,8 @@ class CsvOutput(ScanossBase):
92
95
  detected['licenses'] = ''
93
96
  else:
94
97
  detected['licenses'] = ';'.join(dc)
95
- # inventory_id,path,usage,detected_component,detected_license,detected_version,detected_latest,purl
98
+ # inventory_id,path,usage,detected_component,detected_license,
99
+ # detected_version,detected_latest,purl
96
100
  csv_dict.append(
97
101
  {
98
102
  'inventory_id': row_id,
@@ -183,9 +187,11 @@ class CsvOutput(ScanossBase):
183
187
  :return: True if successful, False otherwise
184
188
  """
185
189
  csv_data = self.parse(data)
186
- if not csv_data:
190
+ if csv_data is None:
187
191
  self.print_stderr('ERROR: No CSV data returned for the JSON string provided.')
188
192
  return False
193
+ if len(csv_data) == 0:
194
+ self.print_msg('Warning: Empty scan results - generating CSV with headers only.')
189
195
  # Header row/column details
190
196
  fields = [
191
197
  'inventory_id',
scanoss/cyclonedx.py CHANGED
@@ -57,9 +57,12 @@ class CycloneDx(ScanossBase):
57
57
  :param data: dict - JSON object
58
58
  :return: CycloneDX dictionary, and vulnerability dictionary
59
59
  """
60
- if not data:
60
+ if data is None:
61
61
  self.print_stderr('ERROR: No JSON data provided to parse.')
62
62
  return None, None
63
+ if len(data) == 0:
64
+ self.print_msg('Warning: Empty scan results provided. Returning empty component dictionary.')
65
+ return {}, {}
63
66
  self.print_debug('Processing raw results into CycloneDX format...')
64
67
  cdx = {}
65
68
  vdx = {}
@@ -186,9 +189,11 @@ class CycloneDx(ScanossBase):
186
189
  json: The CycloneDX output
187
190
  """
188
191
  cdx, vdx = self.parse(data)
189
- if not cdx:
192
+ if cdx is None:
190
193
  self.print_stderr('ERROR: No CycloneDX data returned for the JSON string provided.')
191
- return False, None
194
+ return False, {}
195
+ if len(cdx) == 0:
196
+ self.print_msg('Warning: Empty scan results - generating minimal CycloneDX SBOM with no components.')
192
197
  self._spdx.load_license_data() # Load SPDX license name data for later reference
193
198
  #
194
199
  # Using CDX version 1.4: https://cyclonedx.org/docs/1.4/json/
@@ -1 +1 @@
1
- date: 20250812104538, utime: 1754995538
1
+ date: 20250819171726, utime: 1755623846
@@ -118,7 +118,12 @@ class DependencyTrackExporter(ScanossBase):
118
118
  Base64 encoded string
119
119
  """
120
120
  if not sbom_content:
121
- self.print_stderr('Warning: Empty SBOM content')
121
+ self.print_stderr('Warning: Empty SBOM content provided')
122
+ return ''
123
+ # Check if SBOM has no components (empty scan results)
124
+ components = sbom_content.get('components', [])
125
+ if len(components) == 0:
126
+ self.print_msg('Notice: SBOM contains no components (empty scan results)')
122
127
  json_str = json.dumps(sbom_content, separators=(',', ':'))
123
128
  encoded = base64.b64encode(json_str.encode('utf-8')).decode('utf-8')
124
129
  return encoded
@@ -230,16 +230,34 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
230
230
  if not dt_project:
231
231
  self.print_stderr('Warning: No project details supplied. Returning False.')
232
232
  return False
233
- last_import = dt_project.get('lastBomImport', 0)
234
- last_vulnerability_analysis = dt_project.get('lastVulnerabilityAnalysis', 0)
233
+
234
+ # Safely extract and normalise timestamp values to numeric types
235
+ def _safe_timestamp(field, value=None, default=0) -> float:
236
+ """Convert timestamp value to float, handling string/numeric types safely."""
237
+ if value is None:
238
+ return float(default)
239
+ try:
240
+ return float(value)
241
+ except (ValueError, TypeError):
242
+ self.print_stderr(f'Warning: Invalid timestamp for {field}, value: {value}, using default: {default}')
243
+ return float(default)
244
+
245
+ last_import = _safe_timestamp('lastBomImport', dt_project.get('lastBomImport'), 0)
246
+ last_vulnerability_analysis = _safe_timestamp('lastVulnerabilityAnalysis',
247
+ dt_project.get('lastVulnerabilityAnalysis'), 0
248
+ )
235
249
  metrics = dt_project.get('metrics', {})
236
- last_occurrence = metrics.get('lastOccurrence', 0) if isinstance(metrics, dict) else 0
250
+ last_occurrence = _safe_timestamp('lastOccurrence',
251
+ metrics.get('lastOccurrence', 0)
252
+ if isinstance(metrics, dict) else 0, 0
253
+ )
237
254
  if self.debug:
238
255
  self.print_msg(f'last_import: {last_import}')
239
256
  self.print_msg(f'last_vulnerability_analysis: {last_vulnerability_analysis}')
240
257
  self.print_msg(f'last_occurrence: {last_occurrence}')
241
258
  self.print_msg(f'last_vulnerability_analysis is updated: {last_vulnerability_analysis >= last_import}')
242
259
  self.print_msg(f'last_occurrence is updated: {last_occurrence >= last_import}')
260
+ # If all timestamps are zero, this indicates no processing has occurred
243
261
  if last_vulnerability_analysis == 0 or last_occurrence == 0 or last_import == 0:
244
262
  self.print_stderr(f'Warning: Some project data appears to be unset. Returning False: {dt_project}')
245
263
  return False
@@ -434,12 +452,16 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
434
452
  return PolicyStatus.ERROR.value
435
453
  # Get project violations from Dependency Track
436
454
  dt_project_violations = self.dep_track_service.get_project_violations(self.project_id)
455
+ # Handle case where service returns None (API error) vs empty list (no violations)
456
+ if dt_project_violations is None:
457
+ self.print_stderr('Error: Failed to retrieve project violations from Dependency Track')
458
+ return PolicyStatus.ERROR.value
437
459
  # Sort violations by priority and format output
438
460
  formatter = self._get_formatter()
439
461
  if formatter is None:
440
462
  self.print_stderr('Error: Invalid format specified.')
441
463
  return PolicyStatus.ERROR.value
442
- # Format and output data
464
+ # Format and output data - handle empty results gracefully
443
465
  data = formatter(self._sort_project_violations(dt_project_violations))
444
466
  self.print_to_file_or_stdout(data['details'], self.output)
445
467
  self.print_to_file_or_stderr(data['summary'], self.status)
@@ -97,6 +97,7 @@ class DependencyTrackService(ScanossBase):
97
97
  if not project_id:
98
98
  self.print_stderr('Error: Missing project id. Cannot search for project violations.')
99
99
  return None
100
+ # Return the result as-is - None indicates API failure, empty list means no violations
100
101
  return self.get_dep_track_data(f'{self.url}/api/v1/violation/project/{project_id}')
101
102
 
102
103
  def get_project_by_id(self, project_id:str):
scanoss/spdxlite.py CHANGED
@@ -71,9 +71,12 @@ class SpdxLite:
71
71
  :param data: json - JSON object
72
72
  :return: summary dictionary
73
73
  """
74
- if not data:
74
+ if data is None:
75
75
  self.print_stderr('ERROR: No JSON data provided to parse.')
76
76
  return None
77
+ if len(data) == 0:
78
+ self.print_debug('Warning: Empty scan results provided. Returning empty summary.')
79
+ return {}
77
80
 
78
81
  self.print_debug('Processing raw results into summary format...')
79
82
  return self._process_files(data)
@@ -277,9 +280,11 @@ class SpdxLite:
277
280
  :return: True if successful, False otherwise
278
281
  """
279
282
  raw_data = self.parse(data)
280
- if not raw_data:
283
+ if raw_data is None:
281
284
  self.print_stderr('ERROR: No SPDX data returned for the JSON string provided.')
282
285
  return False
286
+ if len(raw_data) == 0:
287
+ self.print_debug('Warning: Empty scan results - generating minimal SPDX Lite document with no packages.')
283
288
 
284
289
  self.load_license_data()
285
290
  spdx_document = self._create_base_document(raw_data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scanoss
3
- Version: 1.31.2
3
+ Version: 1.31.3
4
4
  Summary: Simple Python library to leverage the SCANOSS APIs
5
5
  Home-page: https://scanoss.com
6
6
  Author: SCANOSS
@@ -4,13 +4,13 @@ protoc_gen_swagger/options/annotations_pb2.py,sha256=b25EDD6gssUWnFby9gxgcpLIROT
4
4
  protoc_gen_swagger/options/annotations_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
5
5
  protoc_gen_swagger/options/openapiv2_pb2.py,sha256=vYElGp8E1vGHszvWqX97zNG9GFJ7u2QcdK9ouq0XdyI,14939
6
6
  protoc_gen_swagger/options/openapiv2_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
7
- scanoss/__init__.py,sha256=cFt9OUu-aKYB4_uvkEER0rRsCyfmvCCF5AM57UVN-5g,1146
7
+ scanoss/__init__.py,sha256=-rDryvJMuAssXozu_AP2VLfNxsIq3GScH_VO60elp-c,1146
8
8
  scanoss/cli.py,sha256=Hx8ay24LQJq7H1vIs5kKt8UX7OeFxGADY-3uPgNnzlw,91645
9
9
  scanoss/components.py,sha256=b0R9DdKuXqyQiw5nZZwjQ6NJXBr1U9gyx1RI2FP9ozA,14511
10
10
  scanoss/constants.py,sha256=On8mQ-8ardVMHSJ7WOJqeTvGXIOWPLCgUanjE7Wk-wE,351
11
11
  scanoss/cryptography.py,sha256=oj5HHgJk1e31dzQfB-5sIVmQVcUJMsP5DUPyP9QpPgQ,9806
12
- scanoss/csvoutput.py,sha256=qNKRwcChSkgIwLm00kZiVX6iHVQUF4Apl-sMbzJ5Taw,10192
13
- scanoss/cyclonedx.py,sha256=JeAeuj2KYaN72v_08j7b3ZyVOhjXhOHOW7DE3Byp0Wk,16986
12
+ scanoss/csvoutput.py,sha256=3wdXPeIqZG84bCtXFh8fMZO3XodekeSx6RZXoOhZMFc,10551
13
+ scanoss/cyclonedx.py,sha256=JHyFuGCqH3TVsJCOXihKgPplWSFo5xzqoV1q3SeElWE,17291
14
14
  scanoss/file_filters.py,sha256=VxfEBylliXReD07YczsHL0coiI3bdNPbfiLJt7GwPWs,20589
15
15
  scanoss/filecount.py,sha256=RZjKQ6M5P_RQg0_PMD2tsRe5Z8f98ke0sxYVjPDN8iQ,6538
16
16
  scanoss/results.py,sha256=47ZXXuU2sDjYa5vhtbWTmikit9jHhA0rsYKwkvZFI5w,9252
@@ -22,7 +22,7 @@ scanoss/scanossbase.py,sha256=Dkpwxa8NH8XN1iRl03NM_Mkvby0JQ4qfvCiiUrJ5ul0,3163
22
22
  scanoss/scanossgrpc.py,sha256=uwAp9CzA_t7oMXYo7o81j8kVgn8qSeTjA4b1Jj8hoL0,30011
23
23
  scanoss/scanpostprocessor.py,sha256=-JsThlxrU70r92GHykTMERnicdd-6jmwNsE4PH0MN2o,11063
24
24
  scanoss/scantype.py,sha256=gFmyVmKQpHWogN2iCmMj032e_sZo4T92xS3_EH5B3Tc,1310
25
- scanoss/spdxlite.py,sha256=MQqFgQhIO-yrbRwEAQS77HmRgP5GDxff-2JYLVoceA0,28946
25
+ scanoss/spdxlite.py,sha256=sSEugYbtzgKB_hdFLPG6Q4rJBl01fhEU1QU_nXR0qhA,29247
26
26
  scanoss/threadeddependencies.py,sha256=CAeZnoYd3d1ayoRvfm_aVjYbaTDLsk6DMWDxkoBPvq0,9866
27
27
  scanoss/threadedscanning.py,sha256=38ryN_kZGpzmrd_hkuiY9Sb3tOG248canGCDQDmGEwI,9317
28
28
  scanoss/winnowing.py,sha256=RsR9jRTR3TzS1pEeKQ2RuYlIG8Q7RnUQFfgPLog6B-A,21679
@@ -57,15 +57,15 @@ scanoss/api/vulnerabilities/__init__.py,sha256=IFrDk_DTJgKSZmmU-nuLXuq_s8sQZlrSC
57
57
  scanoss/api/vulnerabilities/v2/__init__.py,sha256=IFrDk_DTJgKSZmmU-nuLXuq_s8sQZlrSCHhIDMJT4r0,1122
58
58
  scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py,sha256=CFhF80av8tenGvn9AIsGEtRJPuV2dC_syA5JLZb2lDw,5464
59
59
  scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py,sha256=HlS4k4Zmx6RIAqaO9I96jD-eyF5yU6Xx04pVm7pdqOg,6864
60
- scanoss/data/build_date.txt,sha256=3pVDHRaR6Bp2Dl2p7In03mJ7Tn73XmOFlHhDDTOSVaU,40
60
+ scanoss/data/build_date.txt,sha256=2tCxoRbQknMo5kAyoXwYD0J0x61ovZ4bAjZlzX7WBc4,40
61
61
  scanoss/data/scanoss-settings-schema.json,sha256=ClkRYAkjAN0Sk704G8BE_Ok006oQ6YnIGmX84CF8h9w,8798
62
62
  scanoss/data/spdx-exceptions.json,sha256=s7UTYxC7jqQXr11YBlIWYCNwN6lRDFTR33Y8rpN_dA4,17953
63
63
  scanoss/data/spdx-licenses.json,sha256=A6Z0q82gaTLtnopBfzeIVZjJFxkdRW1g2TuumQc-lII,228794
64
64
  scanoss/export/__init__.py,sha256=D4C0lWLuNp8k_BjQZEc07WZcUgAvriVwQWOk063b0ZU,1122
65
- scanoss/export/dependency_track.py,sha256=-jQdwiBGoTYz8r7jljB33XK4tSQrd1jNuDH3V8QHaiQ,9001
65
+ scanoss/export/dependency_track.py,sha256=A_xQH6_r9xL_fth1Wr770GCTRFVyn7XcUPfVUsXp4-w,9271
66
66
  scanoss/inspection/__init__.py,sha256=D4C0lWLuNp8k_BjQZEc07WZcUgAvriVwQWOk063b0ZU,1122
67
67
  scanoss/inspection/policy_check.py,sha256=JOJko_QVB7_6I8VQiGFJmOmJheN5jlwtpGOS2kBMMCo,9756
68
- scanoss/inspection/dependency_track/project_violation.py,sha256=OzQSyN_diOCvFHFBk8AGZJBnxMcfediU5al_Wbh9l8E,18778
68
+ scanoss/inspection/dependency_track/project_violation.py,sha256=p_azHUesYuKqEMYjj7JV8lC2BSwS2tRp5hJVg6NrILQ,20094
69
69
  scanoss/inspection/raw/component_summary.py,sha256=J4DDGNg9WIxIaTeblk6u4tmtMf4veXDesuC4rmpHNkM,4090
70
70
  scanoss/inspection/raw/copyleft.py,sha256=xAKIYROUG-F9SbPs3iIDmTg8yqovh3NVZNni4-byd68,9324
71
71
  scanoss/inspection/raw/license_summary.py,sha256=m5JVcqnqViPLrwHI5-XCpIEQzUiKtnhFC3FWhfM0T00,5823
@@ -77,15 +77,15 @@ scanoss/scanners/container_scanner.py,sha256=fOrb64owrstX7LnTuxiIan059YgLeKXeBS6
77
77
  scanoss/scanners/folder_hasher.py,sha256=-qvTtMC0iPj7zS8nMSZZJyt9d62MeQIK0LcrNDkt7yc,12267
78
78
  scanoss/scanners/scanner_config.py,sha256=egG7cw3S2akU-D9M1aLE5jLrfz_c8e7_DIotMnnpM84,2601
79
79
  scanoss/scanners/scanner_hfh.py,sha256=OvayCIq_a5iJwv7H7OCdB9K0vI9oxAz9UvgGfg7xrLU,8392
80
- scanoss/services/dependency_track_service.py,sha256=K0dUXSiVf8NAUOfZcKORqdJJl7fW-08nbndSimlLq8Y,4967
80
+ scanoss/services/dependency_track_service.py,sha256=L89cwX1uweUfuFFg10kx2eVJbS9e5_OZefkw1h4SRN8,5062
81
81
  scanoss/utils/__init__.py,sha256=0hjb5ktavp7utJzFhGMPImPaZiHWgilM2HwvTp5lXJE,1122
82
82
  scanoss/utils/abstract_presenter.py,sha256=teiDTxBj5jBMCk2T8i4l1BJPf_u4zBLWrtCTFHSSECM,3148
83
83
  scanoss/utils/crc64.py,sha256=TMrwQimSdE6imhFOUL7oAG6Kxu-8qMpGWMuMg8QpSVs,3169
84
84
  scanoss/utils/file.py,sha256=62cA9a17TU9ZvfA3FY5HY4-QOajJeSrc8S6xLA_f-3M,2980
85
85
  scanoss/utils/simhash.py,sha256=6iu8DOcecPAY36SZjCOzrrLMT9oIE7-gI6QuYwUQ7B0,5793
86
- scanoss-1.31.2.dist-info/licenses/LICENSE,sha256=LLUaXoiyOroIbr5ubAyrxBOwSRLTm35ETO2FmLpy8QQ,1074
87
- scanoss-1.31.2.dist-info/METADATA,sha256=QXcsd5657eJirhyXK3vIFTIXjuaLVErjnx4ZuDgomv0,6108
88
- scanoss-1.31.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
89
- scanoss-1.31.2.dist-info/entry_points.txt,sha256=Uy28xnaDL5KQ7V77sZD5VLDXPNxYYzSr5tsqtiXVzAs,48
90
- scanoss-1.31.2.dist-info/top_level.txt,sha256=V11PrQ6Pnrc-nDF9xnisnJ8e6-i7HqSIKVNqduRWcL8,27
91
- scanoss-1.31.2.dist-info/RECORD,,
86
+ scanoss-1.31.3.dist-info/licenses/LICENSE,sha256=LLUaXoiyOroIbr5ubAyrxBOwSRLTm35ETO2FmLpy8QQ,1074
87
+ scanoss-1.31.3.dist-info/METADATA,sha256=bIPN0AvLiMRacepK5dGbiL-YLoHA5fQF1fmZLcueI0k,6108
88
+ scanoss-1.31.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
89
+ scanoss-1.31.3.dist-info/entry_points.txt,sha256=Uy28xnaDL5KQ7V77sZD5VLDXPNxYYzSr5tsqtiXVzAs,48
90
+ scanoss-1.31.3.dist-info/top_level.txt,sha256=V11PrQ6Pnrc-nDF9xnisnJ8e6-i7HqSIKVNqduRWcL8,27
91
+ scanoss-1.31.3.dist-info/RECORD,,