Skip to content

Commit 5168581

Browse files
authored
Merge pull request #180 from sw360/178-have-correct-cyclonedx-external-reference-file-urls
Have correct `file:///` uri for files in SBOM external references
2 parents 8b75ff5 + 89effc5 commit 5168581

File tree

8 files changed

+61
-24
lines changed

8 files changed

+61
-24
lines changed

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
components have been scanned and how many warnings and errors there are.
2323
* Adapt `getdependencies python` to the Poetry 2.x pyproject.toml format.
2424
* `getdependencies python` now also supports uv and its `uv.lock` file.
25+
* Have correct `file:///` uri for files in SBOM external references.
2526

2627
## 2.9.1
2728

capycli/bom/download_sources.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,17 @@ def download_sources(self, sbom: Bom, source_folder: str) -> None:
110110
new = False
111111
ext_ref = CycloneDxSupport.get_ext_ref(
112112
component, ExternalReferenceType.DISTRIBUTION, CaPyCliBom.SOURCE_FILE_COMMENT)
113+
file_uri = path
114+
if not file_uri.startswith("file://"):
115+
file_uri = "file:///" + file_uri
113116
if not ext_ref:
114117
ext_ref = ExternalReference(
115118
type=ExternalReferenceType.DISTRIBUTION,
116119
comment=CaPyCliBom.SOURCE_FILE_COMMENT,
117-
url=XsUri(path))
120+
url=XsUri(file_uri))
118121
new = True
119122
else:
120-
ext_ref.url = XsUri(path)
123+
ext_ref.url = XsUri(file_uri)
121124
ext_ref.hashes.add(HashType(
122125
alg=HashAlgorithm.SHA_1,
123126
content=sha1))
@@ -188,7 +191,7 @@ def run(self, args: Any) -> None:
188191
sys.exit(ResultCode.RESULT_ERROR_READING_BOM)
189192

190193
if args.verbose:
191-
print_text(" " + str(len(bom.components)) + "components written to SBOM file")
194+
print_text(" " + str(len(bom.components)) + "components read from SBOM file")
192195

193196
source_folder = "./"
194197
if args.source:

capycli/bom/legacy.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ def legacy_component_to_cdx(item: Dict[str, Any]) -> Component:
205205

206206
binaryFile = item.get("BinaryFile", "")
207207
if binaryFile:
208+
if not binaryFile.startswith("file://"):
209+
binaryFile = "file:///" + binaryFile
208210
ext_ref = ExternalReference(
209211
type=ExternalReferenceType.DISTRIBUTION,
210212
comment=CaPyCliBom.BINARY_FILE_COMMENT,

capycli/bom/legacy_cx.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -------------------------------------------------------------------------------
2-
# Copyright (c) 2023 Siemens
2+
# Copyright (c) 2023-2025 Siemens
33
# All Rights Reserved.
44
# Author: thomas.graf@siemens.com
55
#
@@ -66,10 +66,13 @@ def _convert_component(cls, component: Component) -> Component:
6666
# extra handling
6767
prop = CycloneDxSupport.get_property(component, "source-file")
6868
if prop:
69+
file_uri = prop.value
70+
if not file_uri.startswith("file://"):
71+
file_uri = "file:///" + file_uri
6972
ext_ref = ExternalReference(
7073
type=ExternalReferenceType.DISTRIBUTION,
7174
comment=CaPyCliBom.SOURCE_FILE_COMMENT,
72-
url=XsUri(prop.value))
75+
url=XsUri(file_uri))
7376
prop2 = CycloneDxSupport.get_property(component, "source-file-hash")
7477
if prop2:
7578
ext_ref.hashes.add(HashType(
@@ -80,10 +83,13 @@ def _convert_component(cls, component: Component) -> Component:
8083

8184
prop = CycloneDxSupport.get_property(component, "source-file-url")
8285
if prop:
86+
file_uri = prop.value
87+
if not file_uri.startswith("file://"):
88+
file_uri = "file:///" + file_uri
8389
ext_ref = ExternalReference(
8490
type=ExternalReferenceType.DISTRIBUTION,
8591
comment=CaPyCliBom.SOURCE_URL_COMMENT,
86-
url=XsUri(prop.value))
92+
url=XsUri(file_uri))
8793
prop2 = CycloneDxSupport.get_property(component, "source-file-hash")
8894
if prop2:
8995
ext_ref.hashes.add(HashType(
@@ -112,10 +118,13 @@ def _convert_component(cls, component: Component) -> Component:
112118

113119
prop = CycloneDxSupport.get_property(component, "binary-file")
114120
if prop:
121+
file_uri = prop.value
122+
if not file_uri.startswith("file://"):
123+
file_uri = "file:///" + file_uri
115124
ext_ref = ExternalReference(
116125
type=ExternalReferenceType.DISTRIBUTION,
117126
comment=CaPyCliBom.BINARY_FILE_COMMENT,
118-
url=XsUri(prop.value))
127+
url=XsUri(file_uri))
119128
prop2 = CycloneDxSupport.get_property(component, "binary-file-hash")
120129
if prop2:
121130
ext_ref.hashes.add(HashType(

capycli/common/capycli_bom_support.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,18 @@ def update_or_set_ext_ref(comp: Component, type: ExternalReferenceType, comment:
163163
@staticmethod
164164
def have_relative_ext_ref_path(ext_ref: ExternalReference, rel_to: str) -> str:
165165
if isinstance(ext_ref.url, str):
166-
bip = pathlib.PurePath(ext_ref.url)
166+
check_val = ext_ref.url._uri
167+
if check_val.startswith("file:///"):
168+
check_val = check_val[8:]
169+
bip = pathlib.PurePath(check_val)
167170
else:
168-
bip = pathlib.PurePath(ext_ref.url._uri)
171+
check_val = ext_ref.url._uri
172+
if check_val.startswith("file:///"):
173+
check_val = check_val[8:]
174+
bip = pathlib.PurePath(check_val)
169175
file = bip.as_posix()
170176
if os.path.isfile(file):
171-
ext_ref.url = XsUri("file://" + bip.relative_to(rel_to).as_posix())
177+
ext_ref.url = XsUri("file:///" + bip.relative_to(rel_to).as_posix())
172178
return bip.name
173179

174180
@staticmethod
@@ -223,7 +229,9 @@ def get_ext_ref_source_file(comp: Component) -> str:
223229
if (ext_ref.type == ExternalReferenceType.DISTRIBUTION) \
224230
and (ext_ref.comment == CaPyCliBom.SOURCE_FILE_COMMENT):
225231
url = str(ext_ref.url)
226-
if url.startswith("file://"):
232+
if url.startswith("file:///"):
233+
return url[8:]
234+
elif url.startswith("file://"):
227235
return url[7:]
228236
else:
229237
return url
@@ -245,7 +253,9 @@ def get_ext_ref_binary_file(comp: Component) -> str:
245253
if (ext_ref.type == ExternalReferenceType.DISTRIBUTION) \
246254
and (ext_ref.comment == CaPyCliBom.BINARY_FILE_COMMENT):
247255
url = str(ext_ref.url)
248-
if url.startswith("file://"):
256+
if url.startswith("file:///"):
257+
return url[8:]
258+
elif url.startswith("file://"):
249259
return url[7:]
250260
else:
251261
return url

capycli/dependencies/javascript.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def get_dependency(self, data: Dict[str, Any], sbom: Bom) -> Bom:
6262

6363
url = dep.get("resolved", "").split('/')[-1]
6464
if url:
65+
if not url.startswith("file://"):
66+
url = "file:///" + url
6567
ext_ref = ExternalReference(
6668
type=ExternalReferenceType.DISTRIBUTION,
6769
comment=CaPyCliBom.BINARY_FILE_COMMENT,
@@ -131,6 +133,8 @@ def get_dependency_lockversion3(self, data: Dict[str, Any], sbom: Bom) -> Bom:
131133

132134
url = dep.get("resolved", "").split('/')[-1]
133135
if url:
136+
if not url.startswith("file://"):
137+
url = "file:///" + url
134138
ext_ref = ExternalReference(
135139
type=ExternalReferenceType.DISTRIBUTION,
136140
comment=CaPyCliBom.BINARY_FILE_COMMENT,

capycli/dependencies/python.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,10 +314,13 @@ def add_meta_data_to_bomitem(self, cxcomp: Component, package_source: str = "")
314314
for item in meta["urls"]:
315315
if "packagetype" in item:
316316
if item["packagetype"] == "bdist_wheel":
317+
file_uri = item["filename"]
318+
if not file_uri.startswith("file://"):
319+
file_uri = "file:///" + file_uri
317320
ext_ref = ExternalReference(
318321
type=ExternalReferenceType.DISTRIBUTION,
319322
comment=CaPyCliBom.BINARY_FILE_COMMENT,
320-
url=XsUri(item["filename"]))
323+
url=XsUri(file_uri))
321324
cxcomp.external_references.add(ext_ref)
322325
LOG.debug(" got binary file")
323326

@@ -329,10 +332,13 @@ def add_meta_data_to_bomitem(self, cxcomp: Component, package_source: str = "")
329332
LOG.debug(" got binary file url")
330333

331334
if item["packagetype"] == "sdist":
335+
file_uri = item["filename"]
336+
if not file_uri.startswith("file://"):
337+
file_uri = "file:///" + file_uri
332338
ext_ref = ExternalReference(
333339
type=ExternalReferenceType.DISTRIBUTION,
334340
comment=CaPyCliBom.SOURCE_FILE_COMMENT,
335-
url=XsUri(item["filename"]))
341+
url=XsUri(file_uri))
336342
cxcomp.external_references.add(ext_ref)
337343
LOG.debug(" got source file")
338344

tests/test_bom_downloadsources.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -------------------------------------------------------------------------------
2-
# Copyright (c) 2023-2024 Siemens
2+
# Copyright (c) 2023-2025 Siemens
33
# All Rights Reserved.
44
# Author: thomas.graf@siemens.com
55
#
@@ -130,7 +130,7 @@ def test_simple_bom(self) -> None:
130130
try:
131131
out = self.capture_stdout(sut.run, args)
132132
out_bom = CaPyCliBom.read_sbom(args.outputfile)
133-
# capycli.common.json_support.write_json_to_file(out, "STDOUT.TXT")
133+
# json_support.write_json_to_file(out, "STDOUT.TXT")
134134
self.assertTrue("Loading SBOM file" in out)
135135
self.assertTrue("sbom_for_download.json" in out) # path may vary
136136
self.assertIn("SBOM file is not relative to", out)
@@ -144,11 +144,10 @@ def test_simple_bom(self) -> None:
144144
out_bom.components[0], ExternalReferenceType.DISTRIBUTION, CaPyCliBom.SOURCE_FILE_COMMENT)
145145
self.assertIsNotNone(ext_ref)
146146
if ext_ref: # only for mypy
147-
self.assertEqual(ext_ref.url._uri, resultfile)
148-
# if ext_ref.url is XsUri:
149-
# self.assertEqual(ext_ref.url._uri, resultfile)
150-
# else:
151-
# self.assertEqual(ext_ref.url, resultfile)
147+
check_val = ext_ref.url._uri
148+
if check_val.startswith("file:///"):
149+
check_val = check_val[8:]
150+
self.assertEqual(check_val, resultfile)
152151

153152
self.delete_file(args.outputfile)
154153
return
@@ -192,7 +191,7 @@ def test_simple_bom_relative_path(self) -> None:
192191
out_bom.components[0], ExternalReferenceType.DISTRIBUTION, CaPyCliBom.SOURCE_FILE_COMMENT)
193192
self.assertIsNotNone(ext_ref)
194193
if ext_ref: # only for mypy
195-
self.assertEqual(ext_ref.url._uri, "file://certifi-2022.12.7.tar.gz")
194+
self.assertEqual(ext_ref.url._uri, "file:///certifi-2022.12.7.tar.gz")
196195

197196
self.delete_file(args.outputfile)
198197
return
@@ -275,7 +274,10 @@ def test_simple_bom_no_url(self) -> None:
275274
bom.components[0], ExternalReferenceType.DISTRIBUTION, CaPyCliBom.SOURCE_FILE_COMMENT)
276275
self.assertIsNotNone(ext_ref)
277276
if ext_ref: # only for mypy
278-
self.assertEqual(str(ext_ref.url), resultfile)
277+
check_val = ext_ref.url._uri
278+
if check_val.startswith("file:///"):
279+
check_val = check_val[8:]
280+
self.assertEqual(check_val, resultfile)
279281

280282
self.assertEqual(len(bom.components[1].external_references), 0)
281283
return
@@ -288,4 +290,4 @@ def test_simple_bom_no_url(self) -> None:
288290

289291
if __name__ == "__main__":
290292
lib = TestBomDownloadsources()
291-
lib.test_simple_bom()
293+
lib.test_simple_bom_relative_path()

0 commit comments

Comments
 (0)