capycli 2.6.0__tar.gz → 2.6.0.dev1__tar.gz

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.
Files changed (77) hide show
  1. {capycli-2.6.0 → capycli-2.6.0.dev1}/PKG-INFO +2 -9
  2. {capycli-2.6.0 → capycli-2.6.0.dev1}/Readme.md +1 -7
  3. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/bom_convert.py +11 -23
  4. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/create_components.py +6 -4
  5. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/filter_bom.py +1 -2
  6. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/findsources.py +1 -1
  7. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/handle_bom.py +4 -12
  8. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/merge_bom.py +0 -26
  9. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/capycli_bom_support.py +13 -126
  10. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/dependencies/python.py +4 -5
  11. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/main/options.py +4 -7
  12. {capycli-2.6.0 → capycli-2.6.0.dev1}/pyproject.toml +1 -2
  13. capycli-2.6.0/capycli/bom/bom_validate.py +0 -68
  14. {capycli-2.6.0 → capycli-2.6.0.dev1}/LICENSES/CC0-1.0.txt +0 -0
  15. {capycli-2.6.0 → capycli-2.6.0.dev1}/LICENSES/MIT.txt +0 -0
  16. {capycli-2.6.0 → capycli-2.6.0.dev1}/License.md +0 -0
  17. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/__init__.py +0 -0
  18. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/__main__.py +0 -0
  19. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/__init__.py +0 -0
  20. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/check_bom.py +0 -0
  21. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/check_bom_item_status.py +0 -0
  22. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/check_granularity.py +0 -0
  23. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/csv.py +0 -0
  24. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/diff_bom.py +0 -0
  25. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/download_sources.py +0 -0
  26. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/html.py +0 -0
  27. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/legacy.py +0 -0
  28. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/legacy_cx.py +0 -0
  29. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/map_bom.py +0 -0
  30. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/plaintext.py +0 -0
  31. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/bom/show_bom.py +0 -0
  32. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/__init__.py +0 -0
  33. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/comparable_version.py +0 -0
  34. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/component_cache.py +0 -0
  35. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/dependencies_base.py +0 -0
  36. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/file_support.py +0 -0
  37. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/html_support.py +0 -0
  38. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/json_support.py +0 -0
  39. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/map_result.py +0 -0
  40. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/print.py +0 -0
  41. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/purl_service.py +0 -0
  42. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/purl_store.py +0 -0
  43. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/purl_utils.py +0 -0
  44. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/script_base.py +0 -0
  45. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/common/script_support.py +0 -0
  46. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/data/__init__.py +0 -0
  47. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/data/granularity_list.csv +0 -0
  48. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/dependencies/__init__.py +0 -0
  49. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/dependencies/handle_dependencies.py +0 -0
  50. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/dependencies/javascript.py +0 -0
  51. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/dependencies/maven_list.py +0 -0
  52. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/dependencies/maven_pom.py +0 -0
  53. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/dependencies/nuget.py +0 -0
  54. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/main/__init__.py +0 -0
  55. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/main/application.py +0 -0
  56. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/main/argument_parser.py +0 -0
  57. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/main/cli.py +0 -0
  58. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/main/exceptions.py +0 -0
  59. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/main/result_codes.py +0 -0
  60. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/mapping/handle_mapping.py +0 -0
  61. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/mapping/mapping_to_html.py +0 -0
  62. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/mapping/mapping_to_xlsx.py +0 -0
  63. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/moverview/handle_moverview.py +0 -0
  64. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/moverview/moverview_to_html.py +0 -0
  65. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/moverview/moverview_to_xlsx.py +0 -0
  66. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/__init__.py +0 -0
  67. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/check_prerequisites.py +0 -0
  68. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/create_bom.py +0 -0
  69. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/create_project.py +0 -0
  70. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/create_readme.py +0 -0
  71. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/find_project.py +0 -0
  72. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/get_license_info.py +0 -0
  73. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/handle_project.py +0 -0
  74. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/show_ecc.py +0 -0
  75. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/show_licenses.py +0 -0
  76. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/show_project.py +0 -0
  77. {capycli-2.6.0 → capycli-2.6.0.dev1}/capycli/project/show_vulnerabilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: capycli
3
- Version: 2.6.0
3
+ Version: 2.6.0.dev1
4
4
  Summary: CaPyCli - Clearing Automation Python Command Line Interface for SW360
5
5
  Home-page: https://github.com/sw360/capycli
6
6
  License: MIT
@@ -27,7 +27,6 @@ Requires-Dist: colorama (>=0.4.3,<0.5.0)
27
27
  Requires-Dist: cyclonedx-python-lib (>=8.0.0,<9.0.0)
28
28
  Requires-Dist: dateparser (>=1.1.8,<2.0.0)
29
29
  Requires-Dist: importlib-resources (>=5.12.0,<6.0.0)
30
- Requires-Dist: jsonschema (>=4.23.0,<5.0.0)
31
30
  Requires-Dist: openpyxl (>=3.0.3,<4.0.0)
32
31
  Requires-Dist: packageurl-python (>=0.15.6,<0.16.0)
33
32
  Requires-Dist: pyjwt (>=1.7.1,<2.0.0)
@@ -134,7 +133,7 @@ Commands and Sub-Commands
134
133
  Prerequisites checks whether all prerequisites for a successful
135
134
  software clearing are fulfilled
136
135
  Show show project details
137
- Licenses show licenses of all cleared components
136
+ Licenses show licenses of all cleared compponents
138
137
  Create create or update a project on SW360
139
138
  Update update an exiting project, preserving linked releases
140
139
  GetLicenseInfo get license info of all project components
@@ -185,12 +184,6 @@ Options:
185
184
  --forceerror FORCE_ERROR force an error exit code in case of visual errors
186
185
  ```
187
186
 
188
- **Note about `--forceexit` and `--forceerror`**:
189
- These options are not available for all commands. At the moment
190
-
191
- * `--forceexit` applies only to the `project vulnerabilities` command.
192
- * `--forceerror` applies only to the `project prerequisites` and `project getlicenseinfo` commands.
193
-
194
187
  ## Use Cases
195
188
 
196
189
  Over the time we implemented more and more commands with more and more parameters.
@@ -90,7 +90,7 @@ Commands and Sub-Commands
90
90
  Prerequisites checks whether all prerequisites for a successful
91
91
  software clearing are fulfilled
92
92
  Show show project details
93
- Licenses show licenses of all cleared components
93
+ Licenses show licenses of all cleared compponents
94
94
  Create create or update a project on SW360
95
95
  Update update an exiting project, preserving linked releases
96
96
  GetLicenseInfo get license info of all project components
@@ -141,12 +141,6 @@ Options:
141
141
  --forceerror FORCE_ERROR force an error exit code in case of visual errors
142
142
  ```
143
143
 
144
- **Note about `--forceexit` and `--forceerror`**:
145
- These options are not available for all commands. At the moment
146
-
147
- * `--forceexit` applies only to the `project vulnerabilities` command.
148
- * `--forceerror` applies only to the `project prerequisites` and `project getlicenseinfo` commands.
149
-
150
144
  ## Use Cases
151
145
 
152
146
  Over the time we implemented more and more commands with more and more parameters.
@@ -18,16 +18,17 @@ from sortedcontainers import SortedSet
18
18
  import capycli.common.json_support
19
19
  import capycli.common.script_base
20
20
  from capycli import get_logger
21
- from capycli.bom.csv import CsvSupport
22
- from capycli.bom.html import HtmlConversionSupport
23
- from capycli.bom.legacy import LegacySupport
24
- from capycli.bom.legacy_cx import LegacyCx
25
- from capycli.bom.plaintext import PlainTextSupport
26
21
  from capycli.common.capycli_bom_support import CaPyCliBom
27
- from capycli.common.print import print_red, print_text, print_yellow
22
+ from capycli.common.print import print_red, print_text
28
23
  from capycli.main.exceptions import CaPyCliException
29
24
  from capycli.main.result_codes import ResultCode
30
25
 
26
+ from .csv import CsvSupport
27
+ from .html import HtmlConversionSupport
28
+ from .legacy import LegacySupport
29
+ from .legacy_cx import LegacyCx
30
+ from .plaintext import PlainTextSupport
31
+
31
32
  LOG = get_logger(__name__)
32
33
 
33
34
 
@@ -46,8 +47,6 @@ class BomFormat(str, Enum):
46
47
  LEGACY_CX = "legacy-cx"
47
48
  # HTML
48
49
  HTML = "html"
49
- # CycloneDX XML
50
- XML = "xml"
51
50
 
52
51
 
53
52
  class BomConvert(capycli.common.script_base.ScriptBase):
@@ -76,11 +75,6 @@ class BomConvert(capycli.common.script_base.ScriptBase):
76
75
  cdx_components = sbom.components
77
76
  project = sbom.metadata.component
78
77
  print_text(f" {len(cdx_components)} components read from file {inputfile}")
79
- elif (inputformat == BomFormat.XML):
80
- sbom = CaPyCliBom.read_sbom_xml(inputfile)
81
- cdx_components = sbom.components
82
- project = sbom.metadata.component
83
- print_text(f" {len(cdx_components)} components read from file {inputfile}")
84
78
  elif inputformat == BomFormat.LEGACY:
85
79
  cdx_components = SortedSet(LegacySupport.legacy_to_cdx_components(inputfile))
86
80
  print_text(f" {len(cdx_components)} components read from file {inputfile}")
@@ -112,12 +106,6 @@ class BomConvert(capycli.common.script_base.ScriptBase):
112
106
  else:
113
107
  CaPyCliBom.write_simple_sbom(cdx_components, outputfile)
114
108
  print_text(f" {len(cdx_components)} components written to file {outputfile}")
115
- elif outputformat == BomFormat.XML:
116
- if sbom:
117
- CaPyCliBom.write_sbom_xml(sbom, outputfile)
118
- print_text(f" {len(sbom.components)} components written to file {outputfile}")
119
- else:
120
- print_yellow(" This command only works for CycloneDX SBOM input files!")
121
109
  elif outputformat == BomFormat.LEGACY:
122
110
  LegacySupport.write_cdx_components_as_legacy(cdx_components, outputfile)
123
111
  print_text(f" {len(cdx_components)} components written to file {outputfile}")
@@ -151,15 +139,15 @@ class BomConvert(capycli.common.script_base.ScriptBase):
151
139
 
152
140
  def display_help(self) -> None:
153
141
  """Display (local) help."""
154
- print("usage: CaPyCli bom convert [-h] [-i INPUTFILE] [-if {capycli,sbom,text,csv,legacy,legacy-cx,xml}]")
155
- print(" [-o OUTPUTFILE] [-of {capycli,text,csv,legacy,legacy-cx,html,xml}]")
142
+ print("usage: CaPyCli bom convert [-h] [-i INPUTFILE] [-if {capycli,text,csv,legacy,legacy-cx}]")
143
+ print(" [-o OUTPUTFILE] [-of {capycli,text,csv,legacy,legacy-cx,html}]")
156
144
  print("")
157
145
  print("optional arguments:")
158
146
  print(" -h, --help Show this help message and exit")
159
147
  print(" -i INPUTFILE Input BOM filename (JSON)")
160
148
  print(" -o OUTPUTFILE Output BOM filename")
161
- print(" -if INPUTFORMAT Input file format: capycli|sbom|text|csv|legacy|legacy-cx|xml")
162
- print(" -of OUTPUTFORMAT Output file format: capycli|text|csv|legacy|html|xml")
149
+ print(" -if INPUTFORMAT Specify input file format: capycli|sbom|text|csv|legacy|legacy-cx")
150
+ print(" -of OUTPUTFORMAT Specify output file format: capycli|text|csv|legacy|html")
163
151
 
164
152
  def run(self, args: Any) -> None:
165
153
  """Main method()"""
@@ -299,6 +299,8 @@ class BomCreateComponents(capycli.common.script_base.ScriptBase):
299
299
  print_yellow(
300
300
  " WARNING: SW360 source URL", release_data["sourceCodeDownloadurl"],
301
301
  "differs from BOM URL", data["sourceCodeDownloadurl"])
302
+ if data["sourceCodeDownloadurl"].endswith(('zip', 'tgz', 'tar.gz', 'tar')):
303
+ update_data["sourceCodeDownloadurl"] = data["sourceCodeDownloadurl"]
302
304
 
303
305
  if "binaryDownloadurl" in data and data["binaryDownloadurl"]:
304
306
  if not release_data.get("binaryDownloadurl", ""):
@@ -366,6 +368,10 @@ class BomCreateComponents(capycli.common.script_base.ScriptBase):
366
368
  filename = str(CycloneDxSupport.get_ext_ref_binary_file(cx_comp))
367
369
  filehash = str(CycloneDxSupport.get_binary_file_hash(cx_comp))
368
370
 
371
+ if filename is not None and filename.endswith('.git'):
372
+ print_red(" WARNING: resetting filename to prevent uploading .git file")
373
+ filename = None
374
+
369
375
  # Note that we retrieve the SHA1 has from the CycloneDX data.
370
376
  # But there is no guarantee that this *IS* really a SHA1 hash!
371
377
 
@@ -374,10 +380,6 @@ class BomCreateComponents(capycli.common.script_base.ScriptBase):
374
380
  if filename_parsed:
375
381
  filename = os.path.basename(filename_parsed.path)
376
382
 
377
- if filetype in ["SOURCE", "SOURCE_SELF"] and filename is not None and filename.endswith('.git'):
378
- print_red(" WARNING: resetting filename to prevent uploading .git file")
379
- filename = None
380
-
381
383
  if not filename:
382
384
  print_red(" Unable to identify filename from url!")
383
385
  return
@@ -20,7 +20,7 @@ import capycli.common.json_support
20
20
  import capycli.common.script_base
21
21
  from capycli import get_logger
22
22
  from capycli.bom.legacy import LegacySupport
23
- from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport, SbomCreator, SbomWriter
23
+ from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport, SbomWriter
24
24
  from capycli.common.print import print_red, print_text, print_yellow
25
25
  from capycli.main.result_codes import ResultCode
26
26
 
@@ -320,7 +320,6 @@ class FilterBom(capycli.common.script_base.ScriptBase):
320
320
 
321
321
  print_text("Writing new SBOM to " + args.outputfile)
322
322
  try:
323
- SbomCreator.add_standard_bom_standard(sbom)
324
323
  SbomWriter.write_to_json(sbom, args.outputfile, True)
325
324
  except Exception as ex:
326
325
  print_red("Error writing updated SBOM file: " + repr(ex))
@@ -524,7 +524,7 @@ class FindSources(capycli.common.script_base.ScriptBase):
524
524
  if self.verbose:
525
525
  print(" No Source code URL available - try to find with language:")
526
526
  source_url = self.find_source_url_by_language(component)
527
- if not source_url and (language.lower() == "golang" or language.lower() == "go"):
527
+ if not source_url and language.lower() == "golang":
528
528
  if self.verbose:
529
529
  print(" No Source code URL available - try to find on pkg.go.dev:")
530
530
  source_url = self.find_golang_url(component)
@@ -1,5 +1,5 @@
1
1
  # -------------------------------------------------------------------------------
2
- # Copyright (c) 2019-24 Siemens
2
+ # Copyright (c) 2019-23 Siemens
3
3
  # All Rights Reserved.
4
4
  # Author: thomas.graf@siemens.com
5
5
  #
@@ -10,7 +10,6 @@ import sys
10
10
  from typing import Any
11
11
 
12
12
  import capycli.bom.bom_convert
13
- import capycli.bom.bom_validate
14
13
  import capycli.bom.check_bom
15
14
  import capycli.bom.check_bom_item_status
16
15
  import capycli.bom.check_granularity
@@ -47,10 +46,9 @@ def run_bom_command(args: Any) -> None:
47
46
  print(" CreateComponents create new components and releases on SW360 (use with care!)")
48
47
  print(" DownloadSources download source files from the URL specified in the SBOM")
49
48
  print(" Granularity check a bill of material for potential component granularity issues")
50
- print(" Diff compare two bills of material")
51
- print(" Merge merge two bills of material")
52
- print(" Findsources determine the source code for SBOM items")
53
- print(" Validate validate an SBOM")
49
+ print(" Diff compare two bills of material.")
50
+ print(" Merge merge two bills of material.")
51
+ print(" Findsources determine the source code for SBOM items.")
54
52
  return
55
53
 
56
54
  subcommand = args.command[1].lower()
@@ -133,11 +131,5 @@ def run_bom_command(args: Any) -> None:
133
131
  app13.run(args)
134
132
  return
135
133
 
136
- if subcommand == "validate":
137
- """Validate an SBOM."""
138
- app14 = capycli.bom.bom_validate.BomValidate()
139
- app14.run(args)
140
- return
141
-
142
134
  print_red("Unknown sub-command: ")
143
135
  sys.exit(ResultCode.RESULT_COMMAND_ERROR)
@@ -11,9 +11,7 @@ import sys
11
11
  from typing import Any, Optional
12
12
 
13
13
  from cyclonedx.model.bom import Bom
14
- from cyclonedx.model.bom_ref import BomRef
15
14
  from cyclonedx.model.component import Component
16
- from cyclonedx.model.dependency import Dependency
17
15
 
18
16
  import capycli.common.script_base
19
17
  from capycli.common.capycli_bom_support import CaPyCliBom, SbomWriter
@@ -83,36 +81,12 @@ class MergeBom(capycli.common.script_base.ScriptBase):
83
81
 
84
82
  return None
85
83
 
86
- def find_dependency(self, bom_ref: BomRef, bom: Bom) -> Optional[Component]:
87
- """Find a certain dependency (component) by bom_ref in the given bom."""
88
- component: Component
89
- for component in bom.components:
90
- if component.bom_ref == bom_ref:
91
- return component
92
-
93
- return None
94
-
95
84
  def merge_boms(self, bom_old: Bom, bom_new: Bom) -> Bom:
96
- """Merges two SBOMs."""
97
-
98
- # step 1: merge components
99
- component_new: Component
100
85
  for component_new in bom_new.components:
101
86
  found = self.find_in_bom(bom_old, component_new)
102
87
  if not found:
103
88
  bom_old.components.add(component_new)
104
89
 
105
- # step 2: reconstruct dependencies
106
- dep: Dependency
107
- for dep in bom_new.dependencies:
108
- cr = self.find_dependency(dep.ref, bom_new)
109
- if not cr:
110
- continue
111
- for d in dep.dependencies:
112
- cd = self.find_dependency(d.ref, bom_new)
113
- if cd:
114
- bom_old.register_dependency(cr, [cd])
115
-
116
90
  return bom_old
117
91
 
118
92
  def run(self, args: Any) -> None:
@@ -10,9 +10,8 @@ import json
10
10
  import os
11
11
  import pathlib
12
12
  from enum import Enum
13
- from typing import TYPE_CHECKING, Any, List, Optional, Union
13
+ from typing import Any, List, Optional, Union
14
14
 
15
- from cyclonedx.exception import MissingOptionalDependencyException
16
15
  from cyclonedx.factory.license import LicenseFactory
17
16
  from cyclonedx.model import ExternalReference, ExternalReferenceType, HashAlgorithm, HashType, Property, XsUri
18
17
  from cyclonedx.model.bom import Bom
@@ -20,22 +19,12 @@ from cyclonedx.model.component import Component, ComponentType
20
19
  from cyclonedx.model.contact import OrganizationalEntity
21
20
  from cyclonedx.model.definition import Definitions, Standard
22
21
  from cyclonedx.model.tool import ToolRepository
23
- from cyclonedx.output import make_outputter
24
22
  from cyclonedx.output.json import JsonV1Dot6
25
- from cyclonedx.schema import OutputFormat, SchemaVersion
26
- from cyclonedx.validation.json import JsonStrictValidator
27
-
28
- if TYPE_CHECKING:
29
- from cyclonedx.output.json import Json as JsonOutputter
30
- from cyclonedx.output.xml import Xml as XmlOutputter
31
-
32
- from defusedxml import ElementTree as SafeElementTree # type:ignore[import-untyped]
33
23
  from sortedcontainers import SortedSet
34
24
 
35
25
  import capycli.common.script_base
36
26
  from capycli import LOG
37
27
  from capycli.common import json_support
38
- from capycli.common.print import print_green, print_yellow
39
28
  from capycli.main.exceptions import CaPyCliException
40
29
 
41
30
  # -------------------------------------
@@ -200,10 +189,6 @@ class CycloneDxSupport():
200
189
  and (ext_ref.comment == CaPyCliBom.SOURCE_URL_COMMENT):
201
190
  return ext_ref.url
202
191
 
203
- # new for CyCloneDX 1.6
204
- if (ext_ref.type == ExternalReferenceType.SOURCE_DISTRIBUTION):
205
- return ext_ref.url
206
-
207
192
  return ""
208
193
 
209
194
  @staticmethod
@@ -260,33 +245,6 @@ class SbomCreator():
260
245
  def __init__(self) -> None:
261
246
  pass
262
247
 
263
- @staticmethod
264
- def remove_all_tools(sbom: Bom) -> None:
265
- """Remove all existing tool entries."""
266
- if (not sbom) or (not sbom.metadata) or (not sbom.metadata.tools):
267
- return
268
-
269
- if not sbom.metadata.tools.components:
270
- return
271
-
272
- sbom.metadata.tools.components.clear()
273
-
274
- @staticmethod
275
- def has_capycli_tool(sbom: Bom) -> bool:
276
- """Checks whether CaPyCLI is already set as tool."""
277
- if (not sbom) or (not sbom.metadata) or (not sbom.metadata.tools):
278
- return False
279
-
280
- if not sbom.metadata.tools.components:
281
- return False
282
-
283
- comp: Component
284
- for comp in sbom.metadata.tools.components:
285
- if comp.name == "CaPyCLI":
286
- return True
287
-
288
- return False
289
-
290
248
  @staticmethod
291
249
  def get_capycli_tool(version: str = "") -> Component:
292
250
  """Get CaPyCLI as tool."""
@@ -428,12 +386,11 @@ class SbomWriter():
428
386
 
429
387
  @classmethod
430
388
  def write_to_json(cls, sbom: Bom, outputfile: str, pretty_print: bool = False) -> None:
431
- """Write CaPyCLI/CycloneDX JSON."""
432
- SbomCreator.remove_all_tools(sbom)
433
- if not SbomCreator.has_capycli_tool(sbom):
389
+ SbomWriter._remove_tool_python_lib(sbom)
390
+ if len(sbom.metadata.tools) == 0:
434
391
  sbom.metadata.tools.components.add(SbomCreator.get_capycli_tool())
435
392
 
436
- writer: 'JsonOutputter' = JsonV1Dot6(sbom)
393
+ writer = JsonV1Dot6(sbom)
437
394
  cls.remove_empty_properties_in_sbom(sbom)
438
395
 
439
396
  if pretty_print:
@@ -442,20 +399,6 @@ class SbomWriter():
442
399
  else:
443
400
  writer.output_to_file(filename=outputfile, allow_overwrite=True)
444
401
 
445
- @classmethod
446
- def write_to_xml(cls, sbom: Bom, outputfile: str, pretty_print: bool = False) -> None:
447
- """Write CaPyCLI/CycloneDX XML."""
448
- SbomCreator.remove_all_tools(sbom)
449
- if not SbomCreator.has_capycli_tool(sbom):
450
- sbom.metadata.tools.components.add(SbomCreator.get_capycli_tool())
451
- cls.remove_empty_properties_in_sbom(sbom)
452
-
453
- writer: 'XmlOutputter' = make_outputter(sbom, OutputFormat.XML, SchemaVersion.V1_6)
454
- if pretty_print:
455
- writer.output_to_file(outputfile, indent=2)
456
- else:
457
- writer.output_to_file(outputfile)
458
-
459
402
 
460
403
  class CaPyCliBom():
461
404
  """
@@ -477,23 +420,19 @@ class CaPyCliBom():
477
420
  except Exception as exp:
478
421
  raise CaPyCliException("Error reading raw JSON file: " + str(exp))
479
422
 
423
+ # my_json_validator = JsonStrictValidator(SchemaVersion.V1_6)
424
+ # try:
425
+ # validation_errors = my_json_validator.validate_str(json_string)
426
+ # if validation_errors:
427
+ # raise CaPyCliException("JSON validation error: " + repr(validation_errors))
428
+ #
429
+ # print_green("JSON file successfully validated")
430
+ # except MissingOptionalDependencyException as error:
431
+ # print_yellow('JSON-validation was skipped due to', error)
480
432
  bom = Bom.from_json( # type: ignore[attr-defined]
481
433
  json_data)
482
434
  return bom
483
435
 
484
- @classmethod
485
- def read_sbom_xml(cls, inputfile: str) -> Bom:
486
- LOG.debug(f"Reading from file {inputfile}")
487
- with open(inputfile) as fin:
488
- try:
489
- xml_data = fin.read()
490
- except Exception as exp:
491
- raise CaPyCliException("Error reading raw XML file: " + str(exp))
492
-
493
- bom = Bom.from_xml( # type: ignore[attr-defined]
494
- SafeElementTree.fromstring(xml_data))
495
- return bom
496
-
497
436
  @classmethod
498
437
  def write_sbom(cls, sbom: Bom, outputfile: str) -> None:
499
438
  LOG.debug(f"Writing to file {outputfile}")
@@ -505,22 +444,6 @@ class CaPyCliBom():
505
444
  raise CaPyCliException("Error writing CaPyCLI file: " + str(exp))
506
445
  LOG.debug("done")
507
446
 
508
- @classmethod
509
- def write_sbom_xml(cls, sbom: Bom, outputfile: str) -> None:
510
- LOG.debug(f"Writing to file {outputfile}")
511
- try:
512
- # always add/update profile
513
- SbomCreator.add_profile(sbom, "clearing")
514
-
515
- # ensure that file does exist
516
- if os.path.isfile(outputfile):
517
- os.remove(outputfile)
518
-
519
- SbomWriter.write_to_xml(sbom, outputfile, pretty_print=True)
520
- except Exception as exp:
521
- raise CaPyCliException("Error writing CaPyCLI file: " + str(exp))
522
- LOG.debug("done")
523
-
524
447
  @classmethod
525
448
  def write_simple_sbom(cls, bom: SortedSet, outputfile: str) -> None:
526
449
  LOG.debug(f"Writing to file {outputfile}")
@@ -531,39 +454,3 @@ class CaPyCliBom():
531
454
  except Exception as exp:
532
455
  raise CaPyCliException("Error writing CaPyCLI file: " + str(exp))
533
456
  LOG.debug("done")
534
-
535
- @classmethod
536
- def _string_to_schema_version(cls, spec_version: str) -> SchemaVersion:
537
- """Convert the given string to a CycloneDX spec version."""
538
- if spec_version == "1.6":
539
- return SchemaVersion.V1_6
540
- if spec_version == "1.5":
541
- return SchemaVersion.V1_5
542
- if spec_version == "1.4":
543
- return SchemaVersion.V1_4
544
-
545
- print_yellow("Unknown CycloneDX spec version, defaulting to 1.6")
546
- return SchemaVersion.V1_6
547
-
548
- @classmethod
549
- def validate_sbom(cls, inputfile: str, spec_version: str) -> bool:
550
- """Validate the given SBOM file against the given CycloneDX spec. version."""
551
- LOG.debug(f"Validating SBOM from file {inputfile}")
552
- with open(inputfile) as fin:
553
- try:
554
- json_string = fin.read()
555
- except Exception as exp:
556
- raise CaPyCliException("Error reading raw JSON file: " + str(exp))
557
-
558
- my_json_validator = JsonStrictValidator(cls._string_to_schema_version(spec_version))
559
- try:
560
- validation_errors = my_json_validator.validate_str(json_string)
561
- if validation_errors:
562
- raise CaPyCliException("JSON validation error: " + repr(validation_errors))
563
-
564
- print_green("JSON file successfully validated.")
565
- return True
566
- except MissingOptionalDependencyException as error:
567
- print_yellow('JSON-validation was skipped due to', error)
568
-
569
- return False
@@ -310,7 +310,7 @@ class GetPythonDependencies(capycli.common.script_base.ScriptBase):
310
310
 
311
311
  print()
312
312
 
313
- def determine_file_type(self, full_filename: str) -> InputFileType:
313
+ def determine_file_type(self, filename: str) -> InputFileType:
314
314
  """
315
315
  Try to guess the input file type from the filename.
316
316
 
@@ -320,14 +320,13 @@ class GetPythonDependencies(capycli.common.script_base.ScriptBase):
320
320
  Returns:
321
321
  An InputFileType value.
322
322
  """
323
- filename = os.path.basename(full_filename).lower()
323
+ filename = os.path.basename(filename).lower()
324
324
  if filename == "requirements.txt":
325
325
  LOG.debug("Guessing requirements file")
326
326
  return InputFileType.REQUIREMENTS
327
327
 
328
- if (filename == "poetry.lock") or (filename == "poetry2.lock"):
329
- # "poetry2.lock is only for unit test
330
- data = self.read_poetry_lock_file(full_filename)
328
+ if filename == "poetry.lock":
329
+ data = self.read_poetry_lock_file(filename)
331
330
  if data:
332
331
  LOG.debug("Guessing poetry.lock file")
333
332
  return InputFileType.POETRY_LOCK
@@ -46,10 +46,9 @@ class CommandlineSupport():
46
46
  CreateComponents create new components and releases on SW360 (use with care!)
47
47
  DownloadSources download source files from the URL specified in the SBOM
48
48
  Granularity check a bill of material for potential component granularity issues
49
- Diff compare two bills of material
50
- Merge merge two bills of material
51
- Findsources determine the source code for SBOM items
52
- Validate validate an SBOM
49
+ Diff compare two bills of material.
50
+ Merge merge two bills of material.
51
+ Findsources determine the source code for SBOM items.
53
52
 
54
53
  mapping
55
54
  ToHtml create a HTML page showing the mapping result
@@ -64,7 +63,7 @@ class CommandlineSupport():
64
63
  Prerequisites checks whether all prerequisites for a successful
65
64
  software clearing are fulfilled
66
65
  Show show project details
67
- Licenses show licenses of all cleared components
66
+ Licenses show licenses of all cleared compponents
68
67
  Create create or update a project on SW360
69
68
  Update update an exiting project, preserving linked releases
70
69
  GetLicenseInfo get license info of all project components
@@ -107,7 +106,6 @@ class CommandlineSupport():
107
106
  input_formats.append(BomFormat.LEGACY_CX)
108
107
  input_formats.append(BomFormat.SBOM)
109
108
  input_formats.append(BomFormat.CAPYCLI)
110
- input_formats.append(BomFormat.XML)
111
109
 
112
110
  output_formats = []
113
111
  output_formats.append(BomFormat.CAPYCLI)
@@ -116,7 +114,6 @@ class CommandlineSupport():
116
114
  output_formats.append(BomFormat.CSV)
117
115
  output_formats.append(BomFormat.LEGACY)
118
116
  output_formats.append(BomFormat.HTML)
119
- output_formats.append(BomFormat.XML)
120
117
 
121
118
  map_modes = []
122
119
  map_modes.append(MapMode.ALL)
@@ -3,7 +3,7 @@
3
3
 
4
4
  [tool.poetry]
5
5
  name = "capycli"
6
- version = "2.6.0"
6
+ version = "2.6.0.dev1"
7
7
  description = "CaPyCli - Clearing Automation Python Command Line Interface for SW360"
8
8
  authors = ["Thomas Graf <thomas.graf@siemens.com>"]
9
9
  license = "MIT"
@@ -52,7 +52,6 @@ dateparser = "^1.1.8"
52
52
  urllib3 = "*"
53
53
  importlib-resources = "^5.12.0"
54
54
  beautifulsoup4 = "^4.11.1"
55
- jsonschema = "^4.23.0"
56
55
 
57
56
  [tool.poetry.group.dev.dependencies]
58
57
  flake8 = ">=3.7.8"
@@ -1,68 +0,0 @@
1
- # -------------------------------------------------------------------------------
2
- # Copyright (c) 2024 Siemens
3
- # All Rights Reserved.
4
- # Author: thomas.graf@siemens.com
5
- #
6
- # SPDX-License-Identifier: MIT
7
- # -------------------------------------------------------------------------------
8
-
9
- import os
10
- import sys
11
- from typing import Any
12
-
13
- import capycli.common.json_support
14
- import capycli.common.script_base
15
- from capycli import get_logger
16
- from capycli.common.capycli_bom_support import CaPyCliBom
17
- from capycli.common.print import print_text
18
- from capycli.main.exceptions import CaPyCliException
19
- from capycli.main.result_codes import ResultCode
20
-
21
- LOG = get_logger(__name__)
22
-
23
-
24
- class BomValidate(capycli.common.script_base.ScriptBase):
25
- def validate(self, inputfile: str, spec_version: str) -> None:
26
- """Main validation method."""
27
- try:
28
- if not spec_version:
29
- print_text("No CycloneDX spec version specified, defaulting to 1.6")
30
- spec_version = "1.6"
31
- CaPyCliBom.validate_sbom(inputfile, spec_version)
32
- except CaPyCliException as error:
33
- LOG.error(f"Error processing input file: {str(error)}")
34
- sys.exit(ResultCode.RESULT_GENERAL_ERROR)
35
-
36
- def check_arguments(self, args: Any) -> None:
37
- """Check input arguments."""
38
- if not args.inputfile:
39
- LOG.error("No input file specified!")
40
- sys.exit(ResultCode.RESULT_COMMAND_ERROR)
41
-
42
- if not os.path.isfile(args.inputfile):
43
- LOG.error("Input file not found!")
44
- sys.exit(ResultCode.RESULT_FILE_NOT_FOUND)
45
-
46
- def display_help(self) -> None:
47
- """Display (local) help."""
48
- print("usage: CaPyCli bom validate [-h] -i INPUTFILE [-version SpecVersion]")
49
- print("")
50
- print("optional arguments:")
51
- print(" -h, --help Show this help message and exit")
52
- print(" -i INPUTFILE Input BOM filename (JSON)")
53
- print(" -version SpecVersion CycloneDX spec version to validate against: allowed are 1.4, 1.5, and 1.6")
54
-
55
- def run(self, args: Any) -> None:
56
- """Main method()"""
57
- print("\n" + capycli.APP_NAME + ", " + capycli.get_app_version() + " - Validate a CaPyCLI/CycloneDX SBOM\n")
58
-
59
- if args.help:
60
- self.display_help()
61
- return
62
-
63
- self.check_arguments(args)
64
- if args.debug:
65
- global LOG
66
- LOG = get_logger(__name__)
67
-
68
- self.validate(args.inputfile, args.version)
File without changes
File without changes
File without changes